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.
5 package org.chromium.chrome.browser;
7 import android.app.Activity;
8 import android.app.SearchManager;
9 import android.content.ContentProvider;
10 import android.content.ContentUris;
11 import android.content.ContentValues;
12 import android.content.Context;
13 import android.content.Intent;
14 import android.content.SharedPreferences;
15 import android.content.UriMatcher;
16 import android.database.Cursor;
17 import android.database.MatrixCursor;
18 import android.graphics.Bitmap;
19 import android.net.Uri;
20 import android.os.Binder;
21 import android.os.Build;
22 import android.os.Bundle;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.preference.PreferenceManager;
26 import android.provider.BaseColumns;
27 import android.provider.Browser;
28 import android.provider.Browser.BookmarkColumns;
29 import android.provider.Browser.SearchColumns;
30 import android.text.TextUtils;
31 import android.util.Log;
33 import com.google.common.annotations.VisibleForTesting;
35 import org.chromium.base.CalledByNative;
36 import org.chromium.base.CalledByNativeUnchecked;
37 import org.chromium.base.ThreadUtils;
38 import org.chromium.chrome.browser.database.SQLiteCursor;
39 import org.chromium.sync.notifier.SyncStatusHelper;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Vector;
46 import java.util.concurrent.atomic.AtomicBoolean;
49 * This class provides various information of Chrome, like bookmarks, most
50 * visited page etc. It is used to support android.provider.Browser.
53 public class ChromeBrowserProvider extends ContentProvider {
54 private static final String TAG = "ChromeBrowserProvider";
56 // The permission required for using the bookmark folders API. Android build system does
57 // not generate Manifest.java for java libraries, hence use the permission name string. When
58 // making changes to this permission, also update the permission in AndroidManifest.xml.
59 private static final String PERMISSION_READ_WRITE_BOOKMARKS = "READ_WRITE_BOOKMARK_FOLDERS";
61 // Defines the API methods that the Client can call by name.
62 static final String CLIENT_API_BOOKMARK_NODE_EXISTS = "BOOKMARK_NODE_EXISTS";
63 static final String CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE = "CREATE_BOOKMARKS_FOLDER_ONCE";
64 static final String CLIENT_API_GET_BOOKMARK_FOLDER_HIERARCHY = "GET_BOOKMARK_FOLDER_HIERARCHY";
65 static final String CLIENT_API_GET_BOOKMARK_NODE = "GET_BOOKMARK_NODE";
66 static final String CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER = "GET_DEFAULT_BOOKMARK_FOLDER";
67 static final String CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID =
68 "GET_MOBILE_BOOKMARKS_FOLDER_ID";
69 static final String CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH =
70 "IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH";
71 static final String CLIENT_API_DELETE_ALL_BOOKMARKS = "DELETE_ALL_BOOKMARKS";
72 static final String CLIENT_API_RESULT_KEY = "result";
75 // Defines Chrome's API authority, so it can be run and tested
77 private static final String API_AUTHORITY_SUFFIX = ".browser";
79 private static final String BROWSER_CONTRACT_API_AUTHORITY =
80 "com.google.android.apps.chrome.browser-contract";
82 // These values are taken from android.provider.BrowserContract.java since
83 // that class is hidden from the SDK.
84 private static final String BROWSER_CONTRACT_AUTHORITY = "com.android.browser";
85 private static final String BROWSER_CONTRACT_HISTORY_CONTENT_TYPE =
86 "vnd.android.cursor.dir/browser-history";
87 private static final String BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE =
88 "vnd.android.cursor.item/browser-history";
90 // This Authority is for internal interface. It's concatenated with
91 // Context.getPackageName() so that we can install different channels
92 // SxS and have different authorities.
93 private static final String AUTHORITY_SUFFIX = ".ChromeBrowserProvider";
94 private static final String BOOKMARKS_PATH = "bookmarks";
95 private static final String SEARCHES_PATH = "searches";
96 private static final String HISTORY_PATH = "history";
97 private static final String COMBINED_PATH = "combined";
98 private static final String BOOKMARK_FOLDER_PATH = "hierarchy";
100 public static final Uri BROWSER_CONTRACTS_BOOKMAKRS_API_URI = buildContentUri(
101 BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH);
103 public static final Uri BROWSER_CONTRACTS_SEARCHES_API_URI = buildContentUri(
104 BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH);
106 public static final Uri BROWSER_CONTRACTS_HISTORY_API_URI = buildContentUri(
107 BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH);
109 public static final Uri BROWSER_CONTRACTS_COMBINED_API_URI = buildContentUri(
110 BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH);
112 /** The parameter used to specify a bookmark parent ID in ContentValues. */
113 public static final String BOOKMARK_PARENT_ID_PARAM = "parentId";
115 /** The parameter used to specify whether this is a bookmark folder. */
116 public static final String BOOKMARK_IS_FOLDER_PARAM = "isFolder";
118 /** Invalid id value for the Android ContentProvider API calls. */
119 public static final long INVALID_CONTENT_PROVIDER_ID = 0;
121 // ID used to indicate an invalid id for bookmark nodes.
122 // Client API queries should use ChromeBrowserProviderClient.INVALID_BOOKMARK_ID.
123 static final long INVALID_BOOKMARK_ID = -1;
125 private static final String LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY = "last_bookmark_folder_id";
127 private static final int URI_MATCH_BOOKMARKS = 0;
128 private static final int URI_MATCH_BOOKMARKS_ID = 1;
129 private static final int URL_MATCH_API_BOOKMARK = 2;
130 private static final int URL_MATCH_API_BOOKMARK_ID = 3;
131 private static final int URL_MATCH_API_SEARCHES = 4;
132 private static final int URL_MATCH_API_SEARCHES_ID = 5;
133 private static final int URL_MATCH_API_HISTORY_CONTENT = 6;
134 private static final int URL_MATCH_API_HISTORY_CONTENT_ID = 7;
135 private static final int URL_MATCH_API_BOOKMARK_CONTENT = 8;
136 private static final int URL_MATCH_API_BOOKMARK_CONTENT_ID = 9;
137 private static final int URL_MATCH_BOOKMARK_SUGGESTIONS_ID = 10;
138 private static final int URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID = 11;
140 // TODO : Using Android.provider.Browser.HISTORY_PROJECTION once THUMBNAIL,
141 // TOUCH_ICON, and USER_ENTERED fields are supported.
142 private static final String[] BOOKMARK_DEFAULT_PROJECTION = new String[] {
143 BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS,
144 BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITLE,
145 BookmarkColumns.FAVICON, BookmarkColumns.CREATED};
147 private static final String[] SUGGEST_PROJECTION = new String[] {
149 BookmarkColumns.TITLE,
151 BookmarkColumns.DATE,
152 BookmarkColumns.BOOKMARK
155 private final Object mInitializeUriMatcherLock = new Object();
156 private final Object mLoadNativeLock = new Object();
157 private UriMatcher mUriMatcher;
158 private long mLastModifiedBookmarkFolderId = INVALID_BOOKMARK_ID;
159 private int mNativeChromeBrowserProvider;
160 private BookmarkNode mMobileBookmarksFolder;
163 * Records whether we've received a call to one of the public ContentProvider APIs.
165 protected boolean mContentProviderApiCalled;
167 private void ensureUriMatcherInitialized() {
168 synchronized (mInitializeUriMatcherLock) {
169 if (mUriMatcher != null) return;
171 mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
173 String authority = getContext().getPackageName() + AUTHORITY_SUFFIX;
174 mUriMatcher.addURI(authority, BOOKMARKS_PATH, URI_MATCH_BOOKMARKS);
175 mUriMatcher.addURI(authority, BOOKMARKS_PATH + "/#", URI_MATCH_BOOKMARKS_ID);
176 // The internal authority for public APIs
177 String apiAuthority = getContext().getPackageName() + API_AUTHORITY_SUFFIX;
178 mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH, URL_MATCH_API_BOOKMARK);
179 mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH + "/#", URL_MATCH_API_BOOKMARK_ID);
180 mUriMatcher.addURI(apiAuthority, SEARCHES_PATH, URL_MATCH_API_SEARCHES);
181 mUriMatcher.addURI(apiAuthority, SEARCHES_PATH + "/#", URL_MATCH_API_SEARCHES_ID);
182 mUriMatcher.addURI(apiAuthority, HISTORY_PATH, URL_MATCH_API_HISTORY_CONTENT);
183 mUriMatcher.addURI(apiAuthority, HISTORY_PATH + "/#", URL_MATCH_API_HISTORY_CONTENT_ID);
184 mUriMatcher.addURI(apiAuthority, COMBINED_PATH, URL_MATCH_API_BOOKMARK);
185 mUriMatcher.addURI(apiAuthority, COMBINED_PATH + "/#", URL_MATCH_API_BOOKMARK_ID);
186 // The internal authority for BrowserContracts
187 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH,
188 URL_MATCH_API_HISTORY_CONTENT);
189 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH + "/#",
190 URL_MATCH_API_HISTORY_CONTENT_ID);
191 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH,
192 URL_MATCH_API_BOOKMARK);
193 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH + "/#",
194 URL_MATCH_API_BOOKMARK_ID);
195 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH,
196 URL_MATCH_API_SEARCHES);
197 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH + "/#",
198 URL_MATCH_API_SEARCHES_ID);
199 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH,
200 URL_MATCH_API_BOOKMARK_CONTENT);
201 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH + "/#",
202 URL_MATCH_API_BOOKMARK_CONTENT_ID);
203 // Added the Android Framework URIs, so the provider can easily switched
204 // by adding 'browser' and 'com.android.browser' in manifest.
205 // The Android's BrowserContract
206 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH,
207 URL_MATCH_API_HISTORY_CONTENT);
208 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH + "/#",
209 URL_MATCH_API_HISTORY_CONTENT_ID);
210 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined", URL_MATCH_API_BOOKMARK);
211 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined/#", URL_MATCH_API_BOOKMARK_ID);
212 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH, URL_MATCH_API_SEARCHES);
213 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH + "/#",
214 URL_MATCH_API_SEARCHES_ID);
215 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH,
216 URL_MATCH_API_BOOKMARK_CONTENT);
217 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH + "/#",
218 URL_MATCH_API_BOOKMARK_CONTENT_ID);
219 // For supporting android.provider.browser.BookmarkColumns and
221 mUriMatcher.addURI("browser", BOOKMARKS_PATH, URL_MATCH_API_BOOKMARK);
222 mUriMatcher.addURI("browser", BOOKMARKS_PATH + "/#", URL_MATCH_API_BOOKMARK_ID);
223 mUriMatcher.addURI("browser", SEARCHES_PATH, URL_MATCH_API_SEARCHES);
224 mUriMatcher.addURI("browser", SEARCHES_PATH + "/#", URL_MATCH_API_SEARCHES_ID);
226 mUriMatcher.addURI(apiAuthority,
227 BOOKMARKS_PATH + "/" + SearchManager.SUGGEST_URI_PATH_QUERY,
228 URL_MATCH_BOOKMARK_SUGGESTIONS_ID);
229 mUriMatcher.addURI(apiAuthority,
230 SearchManager.SUGGEST_URI_PATH_QUERY,
231 URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID);
236 public boolean onCreate() {
237 // Pre-load shared preferences object, this happens on a separate thread
238 PreferenceManager.getDefaultSharedPreferences(getContext());
243 * Lazily fetches the last modified bookmark folder id.
245 private long getLastModifiedBookmarkFolderId() {
246 if (mLastModifiedBookmarkFolderId == INVALID_BOOKMARK_ID) {
247 SharedPreferences sharedPreferences =
248 PreferenceManager.getDefaultSharedPreferences(getContext());
249 mLastModifiedBookmarkFolderId = sharedPreferences.getLong(
250 LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, INVALID_BOOKMARK_ID);
252 return mLastModifiedBookmarkFolderId;
255 private String buildSuggestWhere(String selection, int argc) {
256 StringBuilder sb = new StringBuilder(selection);
257 for (int i = 0; i < argc - 1; i++) {
259 sb.append(selection);
261 return sb.toString();
264 private String getReadWritePermissionNameForBookmarkFolders() {
265 return getContext().getApplicationContext().getPackageName() + ".permission."
266 + PERMISSION_READ_WRITE_BOOKMARKS;
269 private Cursor getBookmarkHistorySuggestions(String selection, String[] selectionArgs,
270 String sortOrder, boolean excludeHistory) {
271 boolean matchTitles = false;
272 Vector<String> args = new Vector<String>();
273 String like = selectionArgs[0] + "%";
274 if (selectionArgs[0].startsWith("http") || selectionArgs[0].startsWith("file")) {
277 // Match against common URL prefixes.
278 args.add("http://" + like);
279 args.add("https://" + like);
280 args.add("http://www." + like);
281 args.add("https://www." + like);
282 args.add("file://" + like);
286 StringBuilder urlWhere = new StringBuilder("(");
287 urlWhere.append(buildSuggestWhere(selection, args.size()));
290 urlWhere.append(" OR title LIKE ?");
292 urlWhere.append(")");
294 if (excludeHistory) {
295 urlWhere.append(" AND bookmark=?");
299 selectionArgs = args.toArray(selectionArgs);
300 Cursor cursor = queryBookmarkFromAPI(SUGGEST_PROJECTION, urlWhere.toString(),
301 selectionArgs, sortOrder);
302 return new ChromeBrowserProviderSuggestionsCursor(cursor);
306 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
308 if (!canHandleContentProviderApiCall()) return null;
310 // Check for invalid id values if provided.
311 // If it represents a bookmark node then it's the root node. Don't provide access here.
312 // Otherwise it represents a SQLite row id, so 0 is invalid.
313 long bookmarkId = INVALID_CONTENT_PROVIDER_ID;
315 bookmarkId = ContentUris.parseId(uri);
316 if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return null;
317 } catch (Exception e) {
320 int match = mUriMatcher.match(uri);
321 Cursor cursor = null;
323 case URL_MATCH_BOOKMARK_SUGGESTIONS_ID:
324 cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, true);
326 case URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID:
327 cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, false);
329 case URL_MATCH_API_BOOKMARK:
330 cursor = queryBookmarkFromAPI(projection, selection, selectionArgs, sortOrder);
332 case URL_MATCH_API_BOOKMARK_ID:
333 cursor = queryBookmarkFromAPI(projection, buildWhereClause(bookmarkId, selection),
334 selectionArgs, sortOrder);
336 case URL_MATCH_API_SEARCHES:
337 cursor = querySearchTermFromAPI(projection, selection, selectionArgs, sortOrder);
339 case URL_MATCH_API_SEARCHES_ID:
340 cursor = querySearchTermFromAPI(projection, buildWhereClause(bookmarkId, selection),
341 selectionArgs, sortOrder);
343 case URL_MATCH_API_HISTORY_CONTENT:
344 cursor = queryBookmarkFromAPI(projection, buildHistoryWhereClause(selection),
345 selectionArgs, sortOrder);
347 case URL_MATCH_API_HISTORY_CONTENT_ID:
348 cursor = queryBookmarkFromAPI(projection,
349 buildHistoryWhereClause(bookmarkId, selection), selectionArgs, sortOrder);
351 case URL_MATCH_API_BOOKMARK_CONTENT:
352 cursor = queryBookmarkFromAPI(projection, buildBookmarkWhereClause(selection),
353 selectionArgs, sortOrder);
355 case URL_MATCH_API_BOOKMARK_CONTENT_ID:
356 cursor = queryBookmarkFromAPI(projection,
357 buildBookmarkWhereClause(bookmarkId, selection), selectionArgs, sortOrder);
360 throw new IllegalArgumentException(TAG + ": query - unknown URL uri = " + uri);
362 if (cursor == null) {
363 cursor = new MatrixCursor(new String[] { });
365 cursor.setNotificationUri(getContext().getContentResolver(), uri);
370 public Uri insert(Uri uri, ContentValues values) {
371 if (!canHandleContentProviderApiCall()) return null;
373 int match = mUriMatcher.match(uri);
377 case URI_MATCH_BOOKMARKS:
378 id = addBookmark(values);
379 if (id == INVALID_BOOKMARK_ID) return null;
381 case URL_MATCH_API_BOOKMARK_CONTENT:
382 values.put(BookmarkColumns.BOOKMARK, 1);
384 case URL_MATCH_API_BOOKMARK:
385 case URL_MATCH_API_HISTORY_CONTENT:
386 id = addBookmarkFromAPI(values);
387 if (id == INVALID_CONTENT_PROVIDER_ID) return null;
389 case URL_MATCH_API_SEARCHES:
390 id = addSearchTermFromAPI(values);
391 if (id == INVALID_CONTENT_PROVIDER_ID) return null;
394 throw new IllegalArgumentException(TAG + ": insert - unknown URL " + uri);
397 res = ContentUris.withAppendedId(uri, id);
398 getContext().getContentResolver().notifyChange(res, null);
403 public int delete(Uri uri, String selection, String[] selectionArgs) {
404 if (!canHandleContentProviderApiCall()) return 0;
406 // Check for invalid id values if provided.
407 // If it represents a bookmark node then it's the root node and not mutable.
408 // Otherwise it represents a SQLite row id, so 0 is invalid.
409 long bookmarkId = INVALID_CONTENT_PROVIDER_ID;
411 bookmarkId = ContentUris.parseId(uri);
412 if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
413 } catch (Exception e) {
416 int match = mUriMatcher.match(uri);
419 case URI_MATCH_BOOKMARKS_ID :
420 result = nativeRemoveBookmark(mNativeChromeBrowserProvider, bookmarkId);
422 case URL_MATCH_API_BOOKMARK_ID:
423 result = removeBookmarkFromAPI(
424 buildWhereClause(bookmarkId, selection), selectionArgs);
426 case URL_MATCH_API_BOOKMARK:
427 result = removeBookmarkFromAPI(selection, selectionArgs);
429 case URL_MATCH_API_SEARCHES_ID:
430 result = removeSearchFromAPI(buildWhereClause(bookmarkId, selection),
433 case URL_MATCH_API_SEARCHES:
434 result = removeSearchFromAPI(selection, selectionArgs);
436 case URL_MATCH_API_HISTORY_CONTENT:
437 result = removeHistoryFromAPI(selection, selectionArgs);
439 case URL_MATCH_API_HISTORY_CONTENT_ID:
440 result = removeHistoryFromAPI(buildWhereClause(bookmarkId, selection),
443 case URL_MATCH_API_BOOKMARK_CONTENT:
444 result = removeBookmarkFromAPI(buildBookmarkWhereClause(selection), selectionArgs);
446 case URL_MATCH_API_BOOKMARK_CONTENT_ID:
447 result = removeBookmarkFromAPI(buildBookmarkWhereClause(bookmarkId, selection),
451 throw new IllegalArgumentException(TAG + ": delete - unknown URL " + uri);
454 getContext().getContentResolver().notifyChange(uri, null);
460 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
461 if (!canHandleContentProviderApiCall()) return 0;
463 // Check for invalid id values if provided.
464 // If it represents a bookmark node then it's the root node and not mutable.
465 // Otherwise it represents a SQLite row id, so 0 is invalid.
466 long bookmarkId = INVALID_CONTENT_PROVIDER_ID;
468 bookmarkId = ContentUris.parseId(uri);
469 if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
470 } catch (Exception e) {
473 int match = mUriMatcher.match(uri);
476 case URI_MATCH_BOOKMARKS_ID:
478 if (values.containsKey(Browser.BookmarkColumns.URL)) {
479 url = values.getAsString(Browser.BookmarkColumns.URL);
481 String title = values.getAsString(Browser.BookmarkColumns.TITLE);
482 long parentId = INVALID_BOOKMARK_ID;
483 if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
484 parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
486 result = nativeUpdateBookmark(mNativeChromeBrowserProvider, bookmarkId, url, title,
488 updateLastModifiedBookmarkFolder(parentId);
490 case URL_MATCH_API_BOOKMARK_ID:
491 result = updateBookmarkFromAPI(values, buildWhereClause(bookmarkId, selection),
494 case URL_MATCH_API_BOOKMARK:
495 result = updateBookmarkFromAPI(values, selection, selectionArgs);
497 case URL_MATCH_API_SEARCHES_ID:
498 result = updateSearchTermFromAPI(values, buildWhereClause(bookmarkId, selection),
501 case URL_MATCH_API_SEARCHES:
502 result = updateSearchTermFromAPI(values, selection, selectionArgs);
504 case URL_MATCH_API_HISTORY_CONTENT:
505 result = updateBookmarkFromAPI(values, buildHistoryWhereClause(selection),
508 case URL_MATCH_API_HISTORY_CONTENT_ID:
509 result = updateBookmarkFromAPI(values,
510 buildHistoryWhereClause(bookmarkId, selection), selectionArgs);
512 case URL_MATCH_API_BOOKMARK_CONTENT:
513 result = updateBookmarkFromAPI(values, buildBookmarkWhereClause(selection),
516 case URL_MATCH_API_BOOKMARK_CONTENT_ID:
517 result = updateBookmarkFromAPI(values,
518 buildBookmarkWhereClause(bookmarkId, selection), selectionArgs);
521 throw new IllegalArgumentException(TAG + ": update - unknown URL " + uri);
524 getContext().getContentResolver().notifyChange(uri, null);
530 public String getType(Uri uri) {
531 ensureUriMatcherInitialized();
532 int match = mUriMatcher.match(uri);
534 case URI_MATCH_BOOKMARKS:
535 case URL_MATCH_API_BOOKMARK:
536 return "vnd.android.cursor.dir/bookmark";
537 case URI_MATCH_BOOKMARKS_ID:
538 case URL_MATCH_API_BOOKMARK_ID:
539 return "vnd.android.cursor.item/bookmark";
540 case URL_MATCH_API_SEARCHES:
541 return "vnd.android.cursor.dir/searches";
542 case URL_MATCH_API_SEARCHES_ID:
543 return "vnd.android.cursor.item/searches";
544 case URL_MATCH_API_HISTORY_CONTENT:
545 return BROWSER_CONTRACT_HISTORY_CONTENT_TYPE;
546 case URL_MATCH_API_HISTORY_CONTENT_ID:
547 return BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE;
549 throw new IllegalArgumentException(TAG + ": getType - unknown URL " + uri);
553 private long addBookmark(ContentValues values) {
554 String url = values.getAsString(Browser.BookmarkColumns.URL);
555 String title = values.getAsString(Browser.BookmarkColumns.TITLE);
556 boolean isFolder = false;
557 if (values.containsKey(BOOKMARK_IS_FOLDER_PARAM)) {
558 isFolder = values.getAsBoolean(BOOKMARK_IS_FOLDER_PARAM);
560 long parentId = INVALID_BOOKMARK_ID;
561 if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
562 parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
564 long id = nativeAddBookmark(mNativeChromeBrowserProvider, url, title, isFolder, parentId);
565 if (id == INVALID_BOOKMARK_ID) return id;
568 updateLastModifiedBookmarkFolder(id);
570 updateLastModifiedBookmarkFolder(parentId);
575 private void updateLastModifiedBookmarkFolder(long id) {
576 if (getLastModifiedBookmarkFolderId() == id) return;
578 mLastModifiedBookmarkFolderId = id;
579 SharedPreferences sharedPreferences =
580 PreferenceManager.getDefaultSharedPreferences(getContext());
581 sharedPreferences.edit()
582 .putLong(LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, mLastModifiedBookmarkFolderId)
586 public static String getApiAuthority(Context context) {
587 return context.getPackageName() + API_AUTHORITY_SUFFIX;
590 public static String getInternalAuthority(Context context) {
591 return context.getPackageName() + AUTHORITY_SUFFIX;
594 public static Uri getBookmarksUri(Context context) {
595 return buildContentUri(getInternalAuthority(context), BOOKMARKS_PATH);
598 public static Uri getBookmarkFolderUri(Context context) {
599 return buildContentUri(getInternalAuthority(context), BOOKMARK_FOLDER_PATH);
602 public static Uri getBookmarksApiUri(Context context) {
603 return buildContentUri(getApiAuthority(context), BOOKMARKS_PATH);
606 public static Uri getSearchesApiUri(Context context) {
607 return buildContentUri(getApiAuthority(context), SEARCHES_PATH);
610 private boolean bookmarkNodeExists(long nodeId) {
611 if (nodeId < 0) return false;
612 return nativeBookmarkNodeExists(mNativeChromeBrowserProvider, nodeId);
615 private long createBookmarksFolderOnce(String title, long parentId) {
616 return nativeCreateBookmarksFolderOnce(mNativeChromeBrowserProvider, title, parentId);
619 private BookmarkNode getBookmarkFolderHierarchy() {
620 return nativeGetAllBookmarkFolders(mNativeChromeBrowserProvider);
623 protected BookmarkNode getBookmarkNode(long nodeId, boolean getParent, boolean getChildren,
624 boolean getFavicons, boolean getThumbnails) {
625 // Don't allow going up the hierarchy if sync is disabled and the requested node
626 // is the Mobile Bookmarks folder.
627 if (getParent && nodeId == getMobileBookmarksFolderId()
628 && !SyncStatusHelper.get(getContext()).isSyncEnabled()) {
632 BookmarkNode node = nativeGetBookmarkNode(mNativeChromeBrowserProvider, nodeId, getParent,
634 if (!getFavicons && !getThumbnails) return node;
636 // Favicons and thumbnails need to be populated separately as they are provided
637 // asynchronously by Chromium services other than the bookmark model.
638 if (node.parent() != null) populateNodeImages(node.parent(), getFavicons, getThumbnails);
639 for (BookmarkNode child : node.children()) {
640 populateNodeImages(child, getFavicons, getThumbnails);
646 private BookmarkNode getDefaultBookmarkFolder() {
647 // Try to access the bookmark folder last modified by us. If it doesn't exist anymore
648 // then use the synced node (Mobile Bookmarks).
649 BookmarkNode lastModified = getBookmarkNode(getLastModifiedBookmarkFolderId(), false, false,
651 if (lastModified == null) {
652 lastModified = getMobileBookmarksFolder();
653 mLastModifiedBookmarkFolderId = lastModified != null ? lastModified.id() :
659 private void populateNodeImages(BookmarkNode node, boolean favicon, boolean thumbnail) {
660 if (node == null || node.type() != Type.URL) return;
663 node.setFavicon(nativeGetFaviconOrTouchIcon(mNativeChromeBrowserProvider, node.url()));
667 node.setThumbnail(nativeGetThumbnail(mNativeChromeBrowserProvider, node.url()));
671 private BookmarkNode getMobileBookmarksFolder() {
672 if (mMobileBookmarksFolder == null) {
673 mMobileBookmarksFolder = nativeGetMobileBookmarksFolder(mNativeChromeBrowserProvider);
675 return mMobileBookmarksFolder;
678 protected long getMobileBookmarksFolderId() {
679 BookmarkNode mobileBookmarks = getMobileBookmarksFolder();
680 return mobileBookmarks != null ? mobileBookmarks.id() : INVALID_BOOKMARK_ID;
683 private boolean isBookmarkInMobileBookmarksBranch(long nodeId) {
684 if (nodeId <= 0) return false;
685 return nativeIsBookmarkInMobileBookmarksBranch(mNativeChromeBrowserProvider, nodeId);
688 static String argKey(int i) {
693 public Bundle call(String method, String arg, Bundle extras) {
694 // TODO(shashishekhar): Refactor this code into a separate class.
695 // Caller must have the READ_WRITE_BOOKMARK_FOLDERS permission.
696 getContext().enforcePermission(getReadWritePermissionNameForBookmarkFolders(),
697 Binder.getCallingPid(), Binder.getCallingUid(), TAG);
698 if (!canHandleContentProviderApiCall()) return null;
699 if (method == null || extras == null) return null;
701 Bundle result = new Bundle();
702 if (CLIENT_API_BOOKMARK_NODE_EXISTS.equals(method)) {
703 result.putBoolean(CLIENT_API_RESULT_KEY,
704 bookmarkNodeExists(extras.getLong(argKey(0))));
705 } else if (CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE.equals(method)) {
706 result.putLong(CLIENT_API_RESULT_KEY,
707 createBookmarksFolderOnce(extras.getString(argKey(0)),
708 extras.getLong(argKey(1))));
709 } else if (CLIENT_API_GET_BOOKMARK_FOLDER_HIERARCHY.equals(method)) {
710 result.putParcelable(CLIENT_API_RESULT_KEY, getBookmarkFolderHierarchy());
711 } else if (CLIENT_API_GET_BOOKMARK_NODE.equals(method)) {
712 result.putParcelable(CLIENT_API_RESULT_KEY,
713 getBookmarkNode(extras.getLong(argKey(0)),
714 extras.getBoolean(argKey(1)),
715 extras.getBoolean(argKey(2)),
716 extras.getBoolean(argKey(3)),
717 extras.getBoolean(argKey(4))));
718 } else if (CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER.equals(method)) {
719 result.putParcelable(CLIENT_API_RESULT_KEY, getDefaultBookmarkFolder());
720 } else if (method.equals(CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID)) {
721 result.putLong(CLIENT_API_RESULT_KEY, getMobileBookmarksFolderId());
722 } else if (CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH.equals(method)) {
723 result.putBoolean(CLIENT_API_RESULT_KEY,
724 isBookmarkInMobileBookmarksBranch(extras.getLong(argKey(0))));
725 } else if(CLIENT_API_DELETE_ALL_BOOKMARKS.equals(method)) {
726 nativeRemoveAllBookmarks(mNativeChromeBrowserProvider);
728 Log.w(TAG, "Received invalid method " + method);
736 * Checks whether Chrome is sufficiently initialized to handle a call to the
737 * ChromeBrowserProvider.
739 private boolean canHandleContentProviderApiCall() {
740 mContentProviderApiCalled = true;
742 if (isInUiThread()) return false;
743 if (!ensureNativeChromeLoaded()) return false;
748 * The type of a BookmarkNode.
759 * Simple Data Object representing the chrome bookmark node.
761 public static class BookmarkNode implements Parcelable {
762 private final long mId;
763 private final String mName;
764 private final String mUrl;
765 private final Type mType;
766 private final BookmarkNode mParent;
767 private final List<BookmarkNode> mChildren = new ArrayList<BookmarkNode>();
769 // Favicon and thumbnail optionally set in a 2-step procedure.
770 private byte[] mFavicon;
771 private byte[] mThumbnail;
773 /** Used to pass structured data back from the native code. */
775 public BookmarkNode(long id, Type type, String name, String url, BookmarkNode parent) {
784 * @return The id of this bookmark entry.
791 * @return The name of this bookmark entry.
793 public String name() {
798 * @return The URL of this bookmark entry.
800 public String url() {
805 * @return The type of this bookmark entry.
812 * @return The bookmark favicon, if any.
814 public byte[] favicon() {
819 * @return The bookmark thumbnail, if any.
821 public byte[] thumbnail() {
826 * @return The parent folder of this bookmark entry.
828 public BookmarkNode parent() {
833 * Adds a child to this node.
836 * Used solely by the native code.
839 @CalledByNativeUnchecked("BookmarkNode")
840 public void addChild(BookmarkNode child) {
841 mChildren.add(child);
845 * @return The child bookmark nodes of this node.
847 public List<BookmarkNode> children() {
852 * @return Whether this node represents a bookmarked URL or not.
854 public boolean isUrl() {
859 * @return true if the two individual nodes contain the same information.
860 * The existence of parent and children nodes is checked, but their contents are not.
862 public boolean equalContents(BookmarkNode node) {
863 return node != null &&
865 !(mName == null ^ node.mName == null) &&
866 (mName == null || mName.equals(node.mName)) &&
867 !(mUrl == null ^ node.mUrl == null) &&
868 (mUrl == null || mUrl.equals(node.mUrl)) &&
869 mType == node.mType &&
870 byteArrayEqual(mFavicon, node.mFavicon) &&
871 byteArrayEqual(mThumbnail, node.mThumbnail) &&
872 !(mParent == null ^ node.mParent == null) &&
873 children().size() == node.children().size();
876 private static boolean byteArrayEqual(byte[] byte1, byte[] byte2) {
877 if (byte1 == null && byte2 != null) return byte2.length == 0;
878 if (byte2 == null && byte1 != null) return byte1.length == 0;
879 return Arrays.equals(byte1, byte2);
882 @CalledByNative("BookmarkNode")
883 private static BookmarkNode create(
884 long id, int type, String name, String url, BookmarkNode parent) {
885 return new BookmarkNode(id, Type.values()[type], name, url, parent);
889 public void setFavicon(byte[] favicon) {
894 public void setThumbnail(byte[] thumbnail) {
895 mThumbnail = thumbnail;
899 public int describeContents() {
904 public void writeToParcel(Parcel dest, int flags) {
905 // Write the current node id.
908 // Serialize the full hierarchy from the root.
909 getHierarchyRoot().writeNodeContentsRecursive(dest);
913 public BookmarkNode getHierarchyRoot() {
914 BookmarkNode root = this;
915 while (root.parent() != null) {
916 root = root.parent();
921 private void writeNodeContentsRecursive(Parcel dest) {
922 writeNodeContents(dest);
923 dest.writeInt(mChildren.size());
924 for (BookmarkNode child : mChildren) {
925 child.writeNodeContentsRecursive(dest);
929 private void writeNodeContents(Parcel dest) {
931 dest.writeString(mName);
932 dest.writeString(mUrl);
933 dest.writeInt(mType.ordinal());
934 dest.writeByteArray(mFavicon);
935 dest.writeByteArray(mThumbnail);
936 dest.writeLong(mParent != null ? mParent.mId : INVALID_BOOKMARK_ID);
939 public static final Creator<BookmarkNode> CREATOR = new Creator<BookmarkNode>() {
940 private HashMap<Long, BookmarkNode> mNodeMap;
943 public BookmarkNode createFromParcel(Parcel source) {
944 mNodeMap = new HashMap<Long, BookmarkNode>();
945 long currentNodeId = source.readLong();
946 readNodeContentsRecursive(source);
947 BookmarkNode node = getNode(currentNodeId);
953 public BookmarkNode[] newArray(int size) {
954 return new BookmarkNode[size];
957 private BookmarkNode getNode(long id) {
958 if (id == INVALID_BOOKMARK_ID) return null;
959 Long nodeId = Long.valueOf(id);
960 if (!mNodeMap.containsKey(nodeId)) {
961 Log.e(TAG, "Invalid BookmarkNode hierarchy. Unknown id " + id);
964 return mNodeMap.get(nodeId);
967 private BookmarkNode readNodeContents(Parcel source) {
968 long id = source.readLong();
969 String name = source.readString();
970 String url = source.readString();
971 int type = source.readInt();
972 byte[] favicon = source.createByteArray();
973 byte[] thumbnail = source.createByteArray();
974 long parentId = source.readLong();
975 if (type < 0 || type >= Type.values().length) {
976 Log.w(TAG, "Invalid node type ordinal value.");
980 BookmarkNode node = new BookmarkNode(id, Type.values()[type], name, url,
982 node.setFavicon(favicon);
983 node.setThumbnail(thumbnail);
987 private BookmarkNode readNodeContentsRecursive(Parcel source) {
988 BookmarkNode node = readNodeContents(source);
989 if (node == null) return null;
991 Long nodeId = Long.valueOf(node.id());
992 if (mNodeMap.containsKey(nodeId)) {
993 Log.e(TAG, "Invalid BookmarkNode hierarchy. Duplicate id " + node.id());
996 mNodeMap.put(nodeId, node);
998 int numChildren = source.readInt();
999 for (int i = 0; i < numChildren; ++i) {
1000 node.addChild(readNodeContentsRecursive(source));
1008 private long addBookmarkFromAPI(ContentValues values) {
1009 BookmarkRow row = BookmarkRow.fromContentValues(values);
1010 if (row.url == null) {
1011 throw new IllegalArgumentException("Must have a bookmark URL");
1013 return nativeAddBookmarkFromAPI(mNativeChromeBrowserProvider,
1014 row.url, row.created, row.isBookmark, row.date, row.favicon,
1015 row.title, row.visits, row.parentId);
1018 private Cursor queryBookmarkFromAPI(String[] projectionIn, String selection,
1019 String[] selectionArgs, String sortOrder) {
1020 String[] projection = null;
1021 if (projectionIn == null || projectionIn.length == 0) {
1022 projection = BOOKMARK_DEFAULT_PROJECTION;
1024 projection = projectionIn;
1027 return nativeQueryBookmarkFromAPI(mNativeChromeBrowserProvider, projection, selection,
1028 selectionArgs, sortOrder);
1031 private int updateBookmarkFromAPI(ContentValues values, String selection,
1032 String[] selectionArgs) {
1033 BookmarkRow row = BookmarkRow.fromContentValues(values);
1034 return nativeUpdateBookmarkFromAPI(mNativeChromeBrowserProvider,
1035 row.url, row.created, row.isBookmark, row.date,
1036 row.favicon, row.title, row.visits, row.parentId, selection, selectionArgs);
1039 private int removeBookmarkFromAPI(String selection, String[] selectionArgs) {
1040 return nativeRemoveBookmarkFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs);
1043 private int removeHistoryFromAPI(String selection, String[] selectionArgs) {
1044 return nativeRemoveHistoryFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs);
1048 private void onBookmarkChanged() {
1049 getContext().getContentResolver().notifyChange(
1050 buildAPIContentUri(getContext(), BOOKMARKS_PATH), null);
1054 private void onSearchTermChanged() {
1055 getContext().getContentResolver().notifyChange(
1056 buildAPIContentUri(getContext(), SEARCHES_PATH), null);
1059 private long addSearchTermFromAPI(ContentValues values) {
1060 SearchRow row = SearchRow.fromContentValues(values);
1061 if (row.term == null) {
1062 throw new IllegalArgumentException("Must have a search term");
1064 return nativeAddSearchTermFromAPI(mNativeChromeBrowserProvider, row.term, row.date);
1067 private int updateSearchTermFromAPI(ContentValues values, String selection,
1068 String[] selectionArgs) {
1069 SearchRow row = SearchRow.fromContentValues(values);
1070 return nativeUpdateSearchTermFromAPI(mNativeChromeBrowserProvider,
1071 row.term, row.date, selection, selectionArgs);
1074 private Cursor querySearchTermFromAPI(String[] projectionIn, String selection,
1075 String[] selectionArgs, String sortOrder) {
1076 String[] projection = null;
1077 if (projectionIn == null || projectionIn.length == 0) {
1078 projection = android.provider.Browser.SEARCHES_PROJECTION;
1080 projection = projectionIn;
1082 return nativeQuerySearchTermFromAPI(mNativeChromeBrowserProvider, projection, selection,
1083 selectionArgs, sortOrder);
1086 private int removeSearchFromAPI(String selection, String[] selectionArgs) {
1087 return nativeRemoveSearchTermFromAPI(mNativeChromeBrowserProvider,
1088 selection, selectionArgs);
1091 private static boolean isInUiThread() {
1092 if (!ThreadUtils.runningOnUiThread()) return false;
1094 if (!"REL".equals(Build.VERSION.CODENAME)) {
1095 throw new IllegalStateException("Shouldn't run in the UI thread");
1098 Log.w(TAG, "ChromeBrowserProvider methods cannot be called from the UI thread.");
1102 private static Uri buildContentUri(String authority, String path) {
1103 return Uri.parse("content://" + authority + "/" + path);
1106 private static Uri buildAPIContentUri(Context context, String path) {
1107 return buildContentUri(context.getPackageName() + API_AUTHORITY_SUFFIX, path);
1110 private static String buildWhereClause(long id, String selection) {
1111 StringBuffer sb = new StringBuffer();
1112 sb.append(BaseColumns._ID);
1115 if (!TextUtils.isEmpty(selection)) {
1116 sb.append(" AND (");
1117 sb.append(selection);
1120 return sb.toString();
1123 private static String buildHistoryWhereClause(long id, String selection) {
1124 return buildWhereClause(id, buildBookmarkWhereClause(selection, false));
1127 private static String buildHistoryWhereClause(String selection) {
1128 return buildBookmarkWhereClause(selection, false);
1132 * @return a SQL where class which is inserted the bookmark condition.
1134 private static String buildBookmarkWhereClause(String selection, boolean is_bookmark) {
1135 StringBuffer sb = new StringBuffer();
1136 sb.append(BookmarkColumns.BOOKMARK);
1137 sb.append(is_bookmark ? " = 1 " : " = 0");
1138 if (!TextUtils.isEmpty(selection)) {
1139 sb.append(" AND (");
1140 sb.append(selection);
1143 return sb.toString();
1146 private static String buildBookmarkWhereClause(long id, String selection) {
1147 return buildWhereClause(id, buildBookmarkWhereClause(selection, true));
1150 private static String buildBookmarkWhereClause(String selection) {
1151 return buildBookmarkWhereClause(selection, true);
1154 // Wrap the value of BookmarkColumn.
1155 private static class BookmarkRow {
1165 static BookmarkRow fromContentValues(ContentValues values) {
1166 BookmarkRow row = new BookmarkRow();
1167 if (values.containsKey(BookmarkColumns.URL)) {
1168 row.url = values.getAsString(BookmarkColumns.URL);
1170 if (values.containsKey(BookmarkColumns.BOOKMARK)) {
1171 row.isBookmark = values.getAsInteger(BookmarkColumns.BOOKMARK) != 0;
1173 if (values.containsKey(BookmarkColumns.CREATED)) {
1174 row.created = values.getAsLong(BookmarkColumns.CREATED);
1176 if (values.containsKey(BookmarkColumns.DATE)) {
1177 row.date = values.getAsLong(BookmarkColumns.DATE);
1179 if (values.containsKey(BookmarkColumns.FAVICON)) {
1180 row.favicon = values.getAsByteArray(BookmarkColumns.FAVICON);
1181 // We need to know that the caller set the favicon column.
1182 if (row.favicon == null) {
1183 row.favicon = new byte[0];
1186 if (values.containsKey(BookmarkColumns.TITLE)) {
1187 row.title = values.getAsString(BookmarkColumns.TITLE);
1189 if (values.containsKey(BookmarkColumns.VISITS)) {
1190 row.visits = values.getAsInteger(BookmarkColumns.VISITS);
1192 if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
1193 row.parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
1199 // Wrap the value of SearchColumn.
1200 private static class SearchRow {
1204 static SearchRow fromContentValues(ContentValues values) {
1205 SearchRow row = new SearchRow();
1206 if (values.containsKey(SearchColumns.SEARCH)) {
1207 row.term = values.getAsString(SearchColumns.SEARCH);
1209 if (values.containsKey(SearchColumns.DATE)) {
1210 row.date = values.getAsLong(SearchColumns.DATE);
1217 * Returns true if the native side of the class is initialized.
1219 protected boolean isNativeSideInitialized() {
1220 return mNativeChromeBrowserProvider != 0;
1224 * Make sure chrome is running. This method mustn't run on UI thread.
1226 * @return Whether the native chrome process is running successfully once this has returned.
1228 private boolean ensureNativeChromeLoaded() {
1229 ensureUriMatcherInitialized();
1231 synchronized(mLoadNativeLock) {
1232 if (mNativeChromeBrowserProvider != 0) return true;
1234 final AtomicBoolean retVal = new AtomicBoolean(true);
1235 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
1238 retVal.set(ensureNativeChromeLoadedOnUIThread());
1241 return retVal.get();
1246 * This method should only run on UI thread.
1248 protected boolean ensureNativeChromeLoadedOnUIThread() {
1249 if (isNativeSideInitialized()) return true;
1250 mNativeChromeBrowserProvider = nativeInit();
1251 return isNativeSideInitialized();
1255 protected void finalize() throws Throwable {
1257 // Tests might try to destroy this in the wrong thread.
1258 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
1261 ensureNativeChromeDestroyedOnUIThread();
1270 * This method should only run on UI thread.
1272 private void ensureNativeChromeDestroyedOnUIThread() {
1273 if (isNativeSideInitialized()) {
1274 nativeDestroy(mNativeChromeBrowserProvider);
1275 mNativeChromeBrowserProvider = 0;
1280 * Call to get the intent to create a bookmark shortcut on homescreen.
1282 public static Intent getShortcutToBookmark(String url, String title, Bitmap favicon, int rValue,
1283 int gValue, int bValue, Context context) {
1284 return BookmarkUtils.createAddToHomeIntent(
1285 context, url, title, favicon, rValue, gValue, bValue);
1288 private native int nativeInit();
1289 private native void nativeDestroy(int nativeChromeBrowserProvider);
1291 // Public API native methods.
1292 private native long nativeAddBookmark(int nativeChromeBrowserProvider,
1293 String url, String title, boolean isFolder, long parentId);
1295 private native int nativeRemoveBookmark(int nativeChromeBrowserProvider, long id);
1297 private native int nativeUpdateBookmark(int nativeChromeBrowserProvider,
1298 long id, String url, String title, long parentId);
1300 private native long nativeAddBookmarkFromAPI(int nativeChromeBrowserProvider,
1301 String url, Long created, Boolean isBookmark, Long date, byte[] favicon,
1302 String title, Integer visits, long parentId);
1304 private native SQLiteCursor nativeQueryBookmarkFromAPI(int nativeChromeBrowserProvider,
1305 String[] projection, String selection, String[] selectionArgs, String sortOrder);
1307 private native int nativeUpdateBookmarkFromAPI(int nativeChromeBrowserProvider,
1308 String url, Long created, Boolean isBookmark, Long date, byte[] favicon,
1309 String title, Integer visits, long parentId, String selection, String[] selectionArgs);
1311 private native int nativeRemoveBookmarkFromAPI(int nativeChromeBrowserProvider,
1312 String selection, String[] selectionArgs);
1314 private native int nativeRemoveHistoryFromAPI(int nativeChromeBrowserProvider,
1315 String selection, String[] selectionArgs);
1317 private native long nativeAddSearchTermFromAPI(int nativeChromeBrowserProvider,
1318 String term, Long date);
1320 private native SQLiteCursor nativeQuerySearchTermFromAPI(int nativeChromeBrowserProvider,
1321 String[] projection, String selection, String[] selectionArgs, String sortOrder);
1323 private native int nativeUpdateSearchTermFromAPI(int nativeChromeBrowserProvider,
1324 String search, Long date, String selection, String[] selectionArgs);
1326 private native int nativeRemoveSearchTermFromAPI(int nativeChromeBrowserProvider,
1327 String selection, String[] selectionArgs);
1329 // Client API native methods.
1330 private native boolean nativeBookmarkNodeExists(int nativeChromeBrowserProvider, long id);
1332 private native long nativeCreateBookmarksFolderOnce(int nativeChromeBrowserProvider,
1333 String title, long parentId);
1335 private native BookmarkNode nativeGetAllBookmarkFolders(int nativeChromeBrowserProvider);
1337 private native void nativeRemoveAllBookmarks(int nativeChromeBrowserProvider);
1339 private native BookmarkNode nativeGetBookmarkNode(int nativeChromeBrowserProvider,
1340 long id, boolean getParent, boolean getChildren);
1342 private native BookmarkNode nativeGetMobileBookmarksFolder(int nativeChromeBrowserProvider);
1344 private native boolean nativeIsBookmarkInMobileBookmarksBranch(int nativeChromeBrowserProvider,
1347 private native byte[] nativeGetFaviconOrTouchIcon(int nativeChromeBrowserProvider, String url);
1349 private native byte[] nativeGetThumbnail(int nativeChromeBrowserProvider, String url);