This source file includes following definitions.
- ensureUriMatcherInitialized
- onCreate
- getLastModifiedBookmarkFolderId
- buildSuggestWhere
- getReadWritePermissionNameForBookmarkFolders
- getBookmarkHistorySuggestions
- getContentUriId
- query
- insert
- delete
- update
- getType
- addBookmark
- updateLastModifiedBookmarkFolder
- getApiAuthority
- getInternalAuthority
- getBookmarksUri
- getBookmarkFolderUri
- getBookmarksApiUri
- getSearchesApiUri
- bookmarkNodeExists
- createBookmarksFolderOnce
- getBookmarkFolderHierarchy
- getBookmarkNode
- getDefaultBookmarkFolder
- populateNodeImages
- getMobileBookmarksFolder
- getMobileBookmarksFolderId
- isBookmarkInMobileBookmarksBranch
- argKey
- call
- canHandleContentProviderApiCall
- id
- name
- url
- type
- favicon
- thumbnail
- parent
- CalledByNativeUnchecked
- addChild
- children
- isUrl
- equalContents
- byteArrayEqual
- CalledByNative
- create
- setFavicon
- setThumbnail
- describeContents
- writeToParcel
- getHierarchyRoot
- writeNodeContentsRecursive
- writeNodeContents
- addBookmarkFromAPI
- queryBookmarkFromAPI
- updateBookmarkFromAPI
- removeBookmarkFromAPI
- removeHistoryFromAPI
- onBookmarkChanged
- onSearchTermChanged
- addSearchTermFromAPI
- updateSearchTermFromAPI
- querySearchTermFromAPI
- removeSearchFromAPI
- isInUiThread
- buildContentUri
- buildAPIContentUri
- buildWhereClause
- buildHistoryWhereClause
- buildHistoryWhereClause
- buildBookmarkWhereClause
- buildBookmarkWhereClause
- buildBookmarkWhereClause
- fromContentValues
- fromContentValues
- isNativeSideInitialized
- ensureNativeChromeLoaded
- ensureNativeChromeLoadedOnUIThread
- finalize
- ensureNativeChromeDestroyedOnUIThread
- getShortcutToBookmark
- SuppressLint
- notifyChange
- nativeInit
- nativeDestroy
- nativeAddBookmark
- nativeRemoveBookmark
- nativeUpdateBookmark
- nativeAddBookmarkFromAPI
- nativeQueryBookmarkFromAPI
- nativeUpdateBookmarkFromAPI
- nativeRemoveBookmarkFromAPI
- nativeRemoveHistoryFromAPI
- nativeAddSearchTermFromAPI
- nativeQuerySearchTermFromAPI
- nativeUpdateSearchTermFromAPI
- nativeRemoveSearchTermFromAPI
- nativeBookmarkNodeExists
- nativeCreateBookmarksFolderOnce
- nativeGetAllBookmarkFolders
- nativeRemoveAllBookmarks
- nativeGetBookmarkNode
- nativeGetMobileBookmarksFolder
- nativeIsBookmarkInMobileBookmarksBranch
- nativeGetFaviconOrTouchIcon
- nativeGetThumbnail
package org.chromium.chrome.browser;
import android.annotation.SuppressLint;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.provider.Browser;
import android.provider.Browser.BookmarkColumns;
import android.provider.Browser.SearchColumns;
import android.text.TextUtils;
import android.util.Log;
import com.google.common.annotations.VisibleForTesting;
import org.chromium.base.CalledByNative;
import org.chromium.base.CalledByNativeUnchecked;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.browser.database.SQLiteCursor;
import org.chromium.sync.notifier.SyncStatusHelper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
public class ChromeBrowserProvider extends ContentProvider {
private static final String TAG = "ChromeBrowserProvider";
private static final String PERMISSION_READ_WRITE_BOOKMARKS = "READ_WRITE_BOOKMARK_FOLDERS";
static final String CLIENT_API_BOOKMARK_NODE_EXISTS = "BOOKMARK_NODE_EXISTS";
static final String CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE = "CREATE_BOOKMARKS_FOLDER_ONCE";
static final String CLIENT_API_GET_BOOKMARK_FOLDER_HIERARCHY = "GET_BOOKMARK_FOLDER_HIERARCHY";
static final String CLIENT_API_GET_BOOKMARK_NODE = "GET_BOOKMARK_NODE";
static final String CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER = "GET_DEFAULT_BOOKMARK_FOLDER";
static final String CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID =
"GET_MOBILE_BOOKMARKS_FOLDER_ID";
static final String CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH =
"IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH";
static final String CLIENT_API_DELETE_ALL_BOOKMARKS = "DELETE_ALL_BOOKMARKS";
static final String CLIENT_API_RESULT_KEY = "result";
private static final String API_AUTHORITY_SUFFIX = ".browser";
private static final String BROWSER_CONTRACT_API_AUTHORITY =
"com.google.android.apps.chrome.browser-contract";
private static final String BROWSER_CONTRACT_AUTHORITY = "com.android.browser";
private static final String BROWSER_CONTRACT_HISTORY_CONTENT_TYPE =
"vnd.android.cursor.dir/browser-history";
private static final String BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/browser-history";
private static final String AUTHORITY_SUFFIX = ".ChromeBrowserProvider";
private static final String BOOKMARKS_PATH = "bookmarks";
private static final String SEARCHES_PATH = "searches";
private static final String HISTORY_PATH = "history";
private static final String COMBINED_PATH = "combined";
private static final String BOOKMARK_FOLDER_PATH = "hierarchy";
public static final Uri BROWSER_CONTRACTS_BOOKMAKRS_API_URI = buildContentUri(
BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH);
public static final Uri BROWSER_CONTRACTS_SEARCHES_API_URI = buildContentUri(
BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH);
public static final Uri BROWSER_CONTRACTS_HISTORY_API_URI = buildContentUri(
BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH);
public static final Uri BROWSER_CONTRACTS_COMBINED_API_URI = buildContentUri(
BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH);
public static final String BOOKMARK_PARENT_ID_PARAM = "parentId";
public static final String BOOKMARK_IS_FOLDER_PARAM = "isFolder";
public static final long INVALID_CONTENT_PROVIDER_ID = 0;
static final long INVALID_BOOKMARK_ID = -1;
private static final String LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY = "last_bookmark_folder_id";
private static final int URI_MATCH_BOOKMARKS = 0;
private static final int URI_MATCH_BOOKMARKS_ID = 1;
private static final int URL_MATCH_API_BOOKMARK = 2;
private static final int URL_MATCH_API_BOOKMARK_ID = 3;
private static final int URL_MATCH_API_SEARCHES = 4;
private static final int URL_MATCH_API_SEARCHES_ID = 5;
private static final int URL_MATCH_API_HISTORY_CONTENT = 6;
private static final int URL_MATCH_API_HISTORY_CONTENT_ID = 7;
private static final int URL_MATCH_API_BOOKMARK_CONTENT = 8;
private static final int URL_MATCH_API_BOOKMARK_CONTENT_ID = 9;
private static final int URL_MATCH_BOOKMARK_SUGGESTIONS_ID = 10;
private static final int URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID = 11;
private static final String[] BOOKMARK_DEFAULT_PROJECTION = new String[] {
BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS,
BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITLE,
BookmarkColumns.FAVICON, BookmarkColumns.CREATED
};
private static final String[] SUGGEST_PROJECTION = new String[] {
BookmarkColumns._ID,
BookmarkColumns.TITLE,
BookmarkColumns.URL,
BookmarkColumns.DATE,
BookmarkColumns.BOOKMARK
};
private final Object mInitializeUriMatcherLock = new Object();
private final Object mLoadNativeLock = new Object();
private UriMatcher mUriMatcher;
private long mLastModifiedBookmarkFolderId = INVALID_BOOKMARK_ID;
private long mNativeChromeBrowserProvider;
private BookmarkNode mMobileBookmarksFolder;
protected boolean mContentProviderApiCalled;
private void ensureUriMatcherInitialized() {
synchronized (mInitializeUriMatcherLock) {
if (mUriMatcher != null) return;
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
String authority = getContext().getPackageName() + AUTHORITY_SUFFIX;
mUriMatcher.addURI(authority, BOOKMARKS_PATH, URI_MATCH_BOOKMARKS);
mUriMatcher.addURI(authority, BOOKMARKS_PATH + "/#", URI_MATCH_BOOKMARKS_ID);
String apiAuthority = getContext().getPackageName() + API_AUTHORITY_SUFFIX;
mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH, URL_MATCH_API_BOOKMARK);
mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH + "/#", URL_MATCH_API_BOOKMARK_ID);
mUriMatcher.addURI(apiAuthority, SEARCHES_PATH, URL_MATCH_API_SEARCHES);
mUriMatcher.addURI(apiAuthority, SEARCHES_PATH + "/#", URL_MATCH_API_SEARCHES_ID);
mUriMatcher.addURI(apiAuthority, HISTORY_PATH, URL_MATCH_API_HISTORY_CONTENT);
mUriMatcher.addURI(apiAuthority, HISTORY_PATH + "/#", URL_MATCH_API_HISTORY_CONTENT_ID);
mUriMatcher.addURI(apiAuthority, COMBINED_PATH, URL_MATCH_API_BOOKMARK);
mUriMatcher.addURI(apiAuthority, COMBINED_PATH + "/#", URL_MATCH_API_BOOKMARK_ID);
mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH,
URL_MATCH_API_HISTORY_CONTENT);
mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH + "/#",
URL_MATCH_API_HISTORY_CONTENT_ID);
mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH,
URL_MATCH_API_BOOKMARK);
mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH + "/#",
URL_MATCH_API_BOOKMARK_ID);
mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH,
URL_MATCH_API_SEARCHES);
mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH + "/#",
URL_MATCH_API_SEARCHES_ID);
mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH,
URL_MATCH_API_BOOKMARK_CONTENT);
mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH + "/#",
URL_MATCH_API_BOOKMARK_CONTENT_ID);
mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH,
URL_MATCH_API_HISTORY_CONTENT);
mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH + "/#",
URL_MATCH_API_HISTORY_CONTENT_ID);
mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined", URL_MATCH_API_BOOKMARK);
mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined/#", URL_MATCH_API_BOOKMARK_ID);
mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH, URL_MATCH_API_SEARCHES);
mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH + "/#",
URL_MATCH_API_SEARCHES_ID);
mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH,
URL_MATCH_API_BOOKMARK_CONTENT);
mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH + "/#",
URL_MATCH_API_BOOKMARK_CONTENT_ID);
mUriMatcher.addURI("browser", BOOKMARKS_PATH, URL_MATCH_API_BOOKMARK);
mUriMatcher.addURI("browser", BOOKMARKS_PATH + "/#", URL_MATCH_API_BOOKMARK_ID);
mUriMatcher.addURI("browser", SEARCHES_PATH, URL_MATCH_API_SEARCHES);
mUriMatcher.addURI("browser", SEARCHES_PATH + "/#", URL_MATCH_API_SEARCHES_ID);
mUriMatcher.addURI(apiAuthority,
BOOKMARKS_PATH + "/" + SearchManager.SUGGEST_URI_PATH_QUERY,
URL_MATCH_BOOKMARK_SUGGESTIONS_ID);
mUriMatcher.addURI(apiAuthority,
SearchManager.SUGGEST_URI_PATH_QUERY,
URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID);
}
}
@Override
public boolean onCreate() {
PreferenceManager.getDefaultSharedPreferences(getContext());
return true;
}
private long getLastModifiedBookmarkFolderId() {
if (mLastModifiedBookmarkFolderId == INVALID_BOOKMARK_ID) {
SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(getContext());
mLastModifiedBookmarkFolderId = sharedPreferences.getLong(
LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, INVALID_BOOKMARK_ID);
}
return mLastModifiedBookmarkFolderId;
}
private String buildSuggestWhere(String selection, int argc) {
StringBuilder sb = new StringBuilder(selection);
for (int i = 0; i < argc - 1; i++) {
sb.append(" OR ");
sb.append(selection);
}
return sb.toString();
}
private String getReadWritePermissionNameForBookmarkFolders() {
return getContext().getApplicationContext().getPackageName() + ".permission."
+ PERMISSION_READ_WRITE_BOOKMARKS;
}
private Cursor getBookmarkHistorySuggestions(String selection, String[] selectionArgs,
String sortOrder, boolean excludeHistory) {
boolean matchTitles = false;
Vector<String> args = new Vector<String>();
String like = selectionArgs[0] + "%";
if (selectionArgs[0].startsWith("http") || selectionArgs[0].startsWith("file")) {
args.add(like);
} else {
args.add("http://" + like);
args.add("https://" + like);
args.add("http://www." + like);
args.add("https://www." + like);
args.add("file://" + like);
matchTitles = true;
}
StringBuilder urlWhere = new StringBuilder("(");
urlWhere.append(buildSuggestWhere(selection, args.size()));
if (matchTitles) {
args.add(like);
urlWhere.append(" OR title LIKE ?");
}
urlWhere.append(")");
if (excludeHistory) {
urlWhere.append(" AND bookmark=?");
args.add("1");
}
selectionArgs = args.toArray(selectionArgs);
Cursor cursor = queryBookmarkFromAPI(SUGGEST_PROJECTION, urlWhere.toString(),
selectionArgs, sortOrder);
return new ChromeBrowserProviderSuggestionsCursor(cursor);
}
private static long getContentUriId(Uri uri) {
try {
return ContentUris.parseId(uri);
} catch (UnsupportedOperationException e) {
return -1;
} catch (NumberFormatException e) {
return -1;
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
if (!canHandleContentProviderApiCall()) return null;
long bookmarkId = getContentUriId(uri);
if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return null;
int match = mUriMatcher.match(uri);
Cursor cursor = null;
switch (match) {
case URL_MATCH_BOOKMARK_SUGGESTIONS_ID:
cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, true);
break;
case URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID:
cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, false);
break;
case URL_MATCH_API_BOOKMARK:
cursor = queryBookmarkFromAPI(projection, selection, selectionArgs, sortOrder);
break;
case URL_MATCH_API_BOOKMARK_ID:
cursor = queryBookmarkFromAPI(projection, buildWhereClause(bookmarkId, selection),
selectionArgs, sortOrder);
break;
case URL_MATCH_API_SEARCHES:
cursor = querySearchTermFromAPI(projection, selection, selectionArgs, sortOrder);
break;
case URL_MATCH_API_SEARCHES_ID:
cursor = querySearchTermFromAPI(projection, buildWhereClause(bookmarkId, selection),
selectionArgs, sortOrder);
break;
case URL_MATCH_API_HISTORY_CONTENT:
cursor = queryBookmarkFromAPI(projection, buildHistoryWhereClause(selection),
selectionArgs, sortOrder);
break;
case URL_MATCH_API_HISTORY_CONTENT_ID:
cursor = queryBookmarkFromAPI(projection,
buildHistoryWhereClause(bookmarkId, selection), selectionArgs, sortOrder);
break;
case URL_MATCH_API_BOOKMARK_CONTENT:
cursor = queryBookmarkFromAPI(projection, buildBookmarkWhereClause(selection),
selectionArgs, sortOrder);
break;
case URL_MATCH_API_BOOKMARK_CONTENT_ID:
cursor = queryBookmarkFromAPI(projection,
buildBookmarkWhereClause(bookmarkId, selection), selectionArgs, sortOrder);
break;
default:
throw new IllegalArgumentException(TAG + ": query - unknown URL uri = " + uri);
}
if (cursor == null) {
cursor = new MatrixCursor(new String[] { });
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
if (!canHandleContentProviderApiCall()) return null;
int match = mUriMatcher.match(uri);
Uri res = null;
long id;
switch (match) {
case URI_MATCH_BOOKMARKS:
id = addBookmark(values);
if (id == INVALID_BOOKMARK_ID) return null;
break;
case URL_MATCH_API_BOOKMARK_CONTENT:
values.put(BookmarkColumns.BOOKMARK, 1);
case URL_MATCH_API_BOOKMARK:
case URL_MATCH_API_HISTORY_CONTENT:
id = addBookmarkFromAPI(values);
if (id == INVALID_CONTENT_PROVIDER_ID) return null;
break;
case URL_MATCH_API_SEARCHES:
id = addSearchTermFromAPI(values);
if (id == INVALID_CONTENT_PROVIDER_ID) return null;
break;
default:
throw new IllegalArgumentException(TAG + ": insert - unknown URL " + uri);
}
res = ContentUris.withAppendedId(uri, id);
notifyChange(res);
return res;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
if (!canHandleContentProviderApiCall()) return 0;
long bookmarkId = getContentUriId(uri);
if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
int match = mUriMatcher.match(uri);
int result;
switch (match) {
case URI_MATCH_BOOKMARKS_ID :
result = nativeRemoveBookmark(mNativeChromeBrowserProvider, bookmarkId);
break;
case URL_MATCH_API_BOOKMARK_ID:
result = removeBookmarkFromAPI(
buildWhereClause(bookmarkId, selection), selectionArgs);
break;
case URL_MATCH_API_BOOKMARK:
result = removeBookmarkFromAPI(selection, selectionArgs);
break;
case URL_MATCH_API_SEARCHES_ID:
result = removeSearchFromAPI(buildWhereClause(bookmarkId, selection),
selectionArgs);
break;
case URL_MATCH_API_SEARCHES:
result = removeSearchFromAPI(selection, selectionArgs);
break;
case URL_MATCH_API_HISTORY_CONTENT:
result = removeHistoryFromAPI(selection, selectionArgs);
break;
case URL_MATCH_API_HISTORY_CONTENT_ID:
result = removeHistoryFromAPI(buildWhereClause(bookmarkId, selection),
selectionArgs);
break;
case URL_MATCH_API_BOOKMARK_CONTENT:
result = removeBookmarkFromAPI(buildBookmarkWhereClause(selection), selectionArgs);
break;
case URL_MATCH_API_BOOKMARK_CONTENT_ID:
result = removeBookmarkFromAPI(buildBookmarkWhereClause(bookmarkId, selection),
selectionArgs);
break;
default:
throw new IllegalArgumentException(TAG + ": delete - unknown URL " + uri);
}
if (result != 0) notifyChange(uri);
return result;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
if (!canHandleContentProviderApiCall()) return 0;
long bookmarkId = getContentUriId(uri);
if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
int match = mUriMatcher.match(uri);
int result;
switch (match) {
case URI_MATCH_BOOKMARKS_ID:
String url = null;
if (values.containsKey(Browser.BookmarkColumns.URL)) {
url = values.getAsString(Browser.BookmarkColumns.URL);
}
String title = values.getAsString(Browser.BookmarkColumns.TITLE);
long parentId = INVALID_BOOKMARK_ID;
if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
}
result = nativeUpdateBookmark(mNativeChromeBrowserProvider, bookmarkId, url, title,
parentId);
updateLastModifiedBookmarkFolder(parentId);
break;
case URL_MATCH_API_BOOKMARK_ID:
result = updateBookmarkFromAPI(values, buildWhereClause(bookmarkId, selection),
selectionArgs);
break;
case URL_MATCH_API_BOOKMARK:
result = updateBookmarkFromAPI(values, selection, selectionArgs);
break;
case URL_MATCH_API_SEARCHES_ID:
result = updateSearchTermFromAPI(values, buildWhereClause(bookmarkId, selection),
selectionArgs);
break;
case URL_MATCH_API_SEARCHES:
result = updateSearchTermFromAPI(values, selection, selectionArgs);
break;
case URL_MATCH_API_HISTORY_CONTENT:
result = updateBookmarkFromAPI(values, buildHistoryWhereClause(selection),
selectionArgs);
break;
case URL_MATCH_API_HISTORY_CONTENT_ID:
result = updateBookmarkFromAPI(values,
buildHistoryWhereClause(bookmarkId, selection), selectionArgs);
break;
case URL_MATCH_API_BOOKMARK_CONTENT:
result = updateBookmarkFromAPI(values, buildBookmarkWhereClause(selection),
selectionArgs);
break;
case URL_MATCH_API_BOOKMARK_CONTENT_ID:
result = updateBookmarkFromAPI(values,
buildBookmarkWhereClause(bookmarkId, selection), selectionArgs);
break;
default:
throw new IllegalArgumentException(TAG + ": update - unknown URL " + uri);
}
if (result != 0) notifyChange(uri);
return result;
}
@Override
public String getType(Uri uri) {
ensureUriMatcherInitialized();
int match = mUriMatcher.match(uri);
switch (match) {
case URI_MATCH_BOOKMARKS:
case URL_MATCH_API_BOOKMARK:
return "vnd.android.cursor.dir/bookmark";
case URI_MATCH_BOOKMARKS_ID:
case URL_MATCH_API_BOOKMARK_ID:
return "vnd.android.cursor.item/bookmark";
case URL_MATCH_API_SEARCHES:
return "vnd.android.cursor.dir/searches";
case URL_MATCH_API_SEARCHES_ID:
return "vnd.android.cursor.item/searches";
case URL_MATCH_API_HISTORY_CONTENT:
return BROWSER_CONTRACT_HISTORY_CONTENT_TYPE;
case URL_MATCH_API_HISTORY_CONTENT_ID:
return BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException(TAG + ": getType - unknown URL " + uri);
}
}
private long addBookmark(ContentValues values) {
String url = values.getAsString(Browser.BookmarkColumns.URL);
String title = values.getAsString(Browser.BookmarkColumns.TITLE);
boolean isFolder = false;
if (values.containsKey(BOOKMARK_IS_FOLDER_PARAM)) {
isFolder = values.getAsBoolean(BOOKMARK_IS_FOLDER_PARAM);
}
long parentId = INVALID_BOOKMARK_ID;
if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
}
long id = nativeAddBookmark(mNativeChromeBrowserProvider, url, title, isFolder, parentId);
if (id == INVALID_BOOKMARK_ID) return id;
if (isFolder) {
updateLastModifiedBookmarkFolder(id);
} else {
updateLastModifiedBookmarkFolder(parentId);
}
return id;
}
private void updateLastModifiedBookmarkFolder(long id) {
if (getLastModifiedBookmarkFolderId() == id) return;
mLastModifiedBookmarkFolderId = id;
SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(getContext());
sharedPreferences.edit()
.putLong(LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, mLastModifiedBookmarkFolderId)
.apply();
}
public static String getApiAuthority(Context context) {
return context.getPackageName() + API_AUTHORITY_SUFFIX;
}
public static String getInternalAuthority(Context context) {
return context.getPackageName() + AUTHORITY_SUFFIX;
}
public static Uri getBookmarksUri(Context context) {
return buildContentUri(getInternalAuthority(context), BOOKMARKS_PATH);
}
public static Uri getBookmarkFolderUri(Context context) {
return buildContentUri(getInternalAuthority(context), BOOKMARK_FOLDER_PATH);
}
public static Uri getBookmarksApiUri(Context context) {
return buildContentUri(getApiAuthority(context), BOOKMARKS_PATH);
}
public static Uri getSearchesApiUri(Context context) {
return buildContentUri(getApiAuthority(context), SEARCHES_PATH);
}
private boolean bookmarkNodeExists(long nodeId) {
if (nodeId < 0) return false;
return nativeBookmarkNodeExists(mNativeChromeBrowserProvider, nodeId);
}
private long createBookmarksFolderOnce(String title, long parentId) {
return nativeCreateBookmarksFolderOnce(mNativeChromeBrowserProvider, title, parentId);
}
private BookmarkNode getBookmarkFolderHierarchy() {
return nativeGetAllBookmarkFolders(mNativeChromeBrowserProvider);
}
protected BookmarkNode getBookmarkNode(long nodeId, boolean getParent, boolean getChildren,
boolean getFavicons, boolean getThumbnails) {
if (getParent && nodeId == getMobileBookmarksFolderId()
&& !SyncStatusHelper.get(getContext()).isSyncEnabled()) {
getParent = false;
}
BookmarkNode node = nativeGetBookmarkNode(mNativeChromeBrowserProvider, nodeId, getParent,
getChildren);
if (!getFavicons && !getThumbnails) return node;
if (node.parent() != null) populateNodeImages(node.parent(), getFavicons, getThumbnails);
for (BookmarkNode child : node.children()) {
populateNodeImages(child, getFavicons, getThumbnails);
}
return node;
}
private BookmarkNode getDefaultBookmarkFolder() {
BookmarkNode lastModified = getBookmarkNode(getLastModifiedBookmarkFolderId(), false, false,
false, false);
if (lastModified == null) {
lastModified = getMobileBookmarksFolder();
mLastModifiedBookmarkFolderId = lastModified != null ? lastModified.id() :
INVALID_BOOKMARK_ID;
}
return lastModified;
}
private void populateNodeImages(BookmarkNode node, boolean favicon, boolean thumbnail) {
if (node == null || node.type() != Type.URL) return;
if (favicon) {
node.setFavicon(nativeGetFaviconOrTouchIcon(mNativeChromeBrowserProvider, node.url()));
}
if (thumbnail) {
node.setThumbnail(nativeGetThumbnail(mNativeChromeBrowserProvider, node.url()));
}
}
private BookmarkNode getMobileBookmarksFolder() {
if (mMobileBookmarksFolder == null) {
mMobileBookmarksFolder = nativeGetMobileBookmarksFolder(mNativeChromeBrowserProvider);
}
return mMobileBookmarksFolder;
}
protected long getMobileBookmarksFolderId() {
BookmarkNode mobileBookmarks = getMobileBookmarksFolder();
return mobileBookmarks != null ? mobileBookmarks.id() : INVALID_BOOKMARK_ID;
}
private boolean isBookmarkInMobileBookmarksBranch(long nodeId) {
if (nodeId <= 0) return false;
return nativeIsBookmarkInMobileBookmarksBranch(mNativeChromeBrowserProvider, nodeId);
}
static String argKey(int i) {
return "arg" + i;
}
@Override
public Bundle call(String method, String arg, Bundle extras) {
getContext().enforcePermission(getReadWritePermissionNameForBookmarkFolders(),
Binder.getCallingPid(), Binder.getCallingUid(), TAG);
if (!canHandleContentProviderApiCall()) return null;
if (method == null || extras == null) return null;
Bundle result = new Bundle();
if (CLIENT_API_BOOKMARK_NODE_EXISTS.equals(method)) {
result.putBoolean(CLIENT_API_RESULT_KEY,
bookmarkNodeExists(extras.getLong(argKey(0))));
} else if (CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE.equals(method)) {
result.putLong(CLIENT_API_RESULT_KEY,
createBookmarksFolderOnce(extras.getString(argKey(0)),
extras.getLong(argKey(1))));
} else if (CLIENT_API_GET_BOOKMARK_FOLDER_HIERARCHY.equals(method)) {
result.putParcelable(CLIENT_API_RESULT_KEY, getBookmarkFolderHierarchy());
} else if (CLIENT_API_GET_BOOKMARK_NODE.equals(method)) {
result.putParcelable(CLIENT_API_RESULT_KEY,
getBookmarkNode(extras.getLong(argKey(0)),
extras.getBoolean(argKey(1)),
extras.getBoolean(argKey(2)),
extras.getBoolean(argKey(3)),
extras.getBoolean(argKey(4))));
} else if (CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER.equals(method)) {
result.putParcelable(CLIENT_API_RESULT_KEY, getDefaultBookmarkFolder());
} else if (method.equals(CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID)) {
result.putLong(CLIENT_API_RESULT_KEY, getMobileBookmarksFolderId());
} else if (CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH.equals(method)) {
result.putBoolean(CLIENT_API_RESULT_KEY,
isBookmarkInMobileBookmarksBranch(extras.getLong(argKey(0))));
} else if (CLIENT_API_DELETE_ALL_BOOKMARKS.equals(method)) {
nativeRemoveAllBookmarks(mNativeChromeBrowserProvider);
} else {
Log.w(TAG, "Received invalid method " + method);
return null;
}
return result;
}
private boolean canHandleContentProviderApiCall() {
mContentProviderApiCalled = true;
if (isInUiThread()) return false;
if (!ensureNativeChromeLoaded()) return false;
return true;
}
public enum Type {
URL,
FOLDER,
BOOKMARK_BAR,
OTHER_NODE,
MOBILE
}
public static class BookmarkNode implements Parcelable {
private final long mId;
private final String mName;
private final String mUrl;
private final Type mType;
private final BookmarkNode mParent;
private final List<BookmarkNode> mChildren = new ArrayList<BookmarkNode>();
private byte[] mFavicon;
private byte[] mThumbnail;
@VisibleForTesting
public BookmarkNode(long id, Type type, String name, String url, BookmarkNode parent) {
mId = id;
mName = name;
mUrl = url;
mType = type;
mParent = parent;
}
public long id() {
return mId;
}
public String name() {
return mName;
}
public String url() {
return mUrl;
}
public Type type() {
return mType;
}
public byte[] favicon() {
return mFavicon;
}
public byte[] thumbnail() {
return mThumbnail;
}
public BookmarkNode parent() {
return mParent;
}
@VisibleForTesting
@CalledByNativeUnchecked("BookmarkNode")
public void addChild(BookmarkNode child) {
mChildren.add(child);
}
public List<BookmarkNode> children() {
return mChildren;
}
public boolean isUrl() {
return mUrl != null;
}
public boolean equalContents(BookmarkNode node) {
return node != null &&
mId == node.mId &&
!(mName == null ^ node.mName == null) &&
(mName == null || mName.equals(node.mName)) &&
!(mUrl == null ^ node.mUrl == null) &&
(mUrl == null || mUrl.equals(node.mUrl)) &&
mType == node.mType &&
byteArrayEqual(mFavicon, node.mFavicon) &&
byteArrayEqual(mThumbnail, node.mThumbnail) &&
!(mParent == null ^ node.mParent == null) &&
children().size() == node.children().size();
}
private static boolean byteArrayEqual(byte[] byte1, byte[] byte2) {
if (byte1 == null && byte2 != null) return byte2.length == 0;
if (byte2 == null && byte1 != null) return byte1.length == 0;
return Arrays.equals(byte1, byte2);
}
@CalledByNative("BookmarkNode")
private static BookmarkNode create(
long id, int type, String name, String url, BookmarkNode parent) {
return new BookmarkNode(id, Type.values()[type], name, url, parent);
}
@VisibleForTesting
public void setFavicon(byte[] favicon) {
mFavicon = favicon;
}
@VisibleForTesting
public void setThumbnail(byte[] thumbnail) {
mThumbnail = thumbnail;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mId);
getHierarchyRoot().writeNodeContentsRecursive(dest);
}
@VisibleForTesting
public BookmarkNode getHierarchyRoot() {
BookmarkNode root = this;
while (root.parent() != null) {
root = root.parent();
}
return root;
}
private void writeNodeContentsRecursive(Parcel dest) {
writeNodeContents(dest);
dest.writeInt(mChildren.size());
for (BookmarkNode child : mChildren) {
child.writeNodeContentsRecursive(dest);
}
}
private void writeNodeContents(Parcel dest) {
dest.writeLong(mId);
dest.writeString(mName);
dest.writeString(mUrl);
dest.writeInt(mType.ordinal());
dest.writeByteArray(mFavicon);
dest.writeByteArray(mThumbnail);
dest.writeLong(mParent != null ? mParent.mId : INVALID_BOOKMARK_ID);
}
public static final Creator<BookmarkNode> CREATOR = new Creator<BookmarkNode>() {
private HashMap<Long, BookmarkNode> mNodeMap;
@Override
public BookmarkNode createFromParcel(Parcel source) {
mNodeMap = new HashMap<Long, BookmarkNode>();
long currentNodeId = source.readLong();
readNodeContentsRecursive(source);
BookmarkNode node = getNode(currentNodeId);
mNodeMap.clear();
return node;
}
@Override
public BookmarkNode[] newArray(int size) {
return new BookmarkNode[size];
}
private BookmarkNode getNode(long id) {
if (id == INVALID_BOOKMARK_ID) return null;
Long nodeId = Long.valueOf(id);
if (!mNodeMap.containsKey(nodeId)) {
Log.e(TAG, "Invalid BookmarkNode hierarchy. Unknown id " + id);
return null;
}
return mNodeMap.get(nodeId);
}
private BookmarkNode readNodeContents(Parcel source) {
long id = source.readLong();
String name = source.readString();
String url = source.readString();
int type = source.readInt();
byte[] favicon = source.createByteArray();
byte[] thumbnail = source.createByteArray();
long parentId = source.readLong();
if (type < 0 || type >= Type.values().length) {
Log.w(TAG, "Invalid node type ordinal value.");
return null;
}
BookmarkNode node = new BookmarkNode(id, Type.values()[type], name, url,
getNode(parentId));
node.setFavicon(favicon);
node.setThumbnail(thumbnail);
return node;
}
private BookmarkNode readNodeContentsRecursive(Parcel source) {
BookmarkNode node = readNodeContents(source);
if (node == null) return null;
Long nodeId = Long.valueOf(node.id());
if (mNodeMap.containsKey(nodeId)) {
Log.e(TAG, "Invalid BookmarkNode hierarchy. Duplicate id " + node.id());
return null;
}
mNodeMap.put(nodeId, node);
int numChildren = source.readInt();
for (int i = 0; i < numChildren; ++i) {
node.addChild(readNodeContentsRecursive(source));
}
return node;
}
};
}
private long addBookmarkFromAPI(ContentValues values) {
BookmarkRow row = BookmarkRow.fromContentValues(values);
if (row.url == null) {
throw new IllegalArgumentException("Must have a bookmark URL");
}
return nativeAddBookmarkFromAPI(mNativeChromeBrowserProvider,
row.url, row.created, row.isBookmark, row.date, row.favicon,
row.title, row.visits, row.parentId);
}
private Cursor queryBookmarkFromAPI(String[] projectionIn, String selection,
String[] selectionArgs, String sortOrder) {
String[] projection = null;
if (projectionIn == null || projectionIn.length == 0) {
projection = BOOKMARK_DEFAULT_PROJECTION;
} else {
projection = projectionIn;
}
return nativeQueryBookmarkFromAPI(mNativeChromeBrowserProvider, projection, selection,
selectionArgs, sortOrder);
}
private int updateBookmarkFromAPI(ContentValues values, String selection,
String[] selectionArgs) {
BookmarkRow row = BookmarkRow.fromContentValues(values);
return nativeUpdateBookmarkFromAPI(mNativeChromeBrowserProvider,
row.url, row.created, row.isBookmark, row.date,
row.favicon, row.title, row.visits, row.parentId, selection, selectionArgs);
}
private int removeBookmarkFromAPI(String selection, String[] selectionArgs) {
return nativeRemoveBookmarkFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs);
}
private int removeHistoryFromAPI(String selection, String[] selectionArgs) {
return nativeRemoveHistoryFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs);
}
@CalledByNative
private void onBookmarkChanged() {
notifyChange(buildAPIContentUri(getContext(), BOOKMARKS_PATH));
}
@CalledByNative
private void onSearchTermChanged() {
notifyChange(buildAPIContentUri(getContext(), SEARCHES_PATH));
}
private long addSearchTermFromAPI(ContentValues values) {
SearchRow row = SearchRow.fromContentValues(values);
if (row.term == null) {
throw new IllegalArgumentException("Must have a search term");
}
return nativeAddSearchTermFromAPI(mNativeChromeBrowserProvider, row.term, row.date);
}
private int updateSearchTermFromAPI(ContentValues values, String selection,
String[] selectionArgs) {
SearchRow row = SearchRow.fromContentValues(values);
return nativeUpdateSearchTermFromAPI(mNativeChromeBrowserProvider,
row.term, row.date, selection, selectionArgs);
}
private Cursor querySearchTermFromAPI(String[] projectionIn, String selection,
String[] selectionArgs, String sortOrder) {
String[] projection = null;
if (projectionIn == null || projectionIn.length == 0) {
projection = android.provider.Browser.SEARCHES_PROJECTION;
} else {
projection = projectionIn;
}
return nativeQuerySearchTermFromAPI(mNativeChromeBrowserProvider, projection, selection,
selectionArgs, sortOrder);
}
private int removeSearchFromAPI(String selection, String[] selectionArgs) {
return nativeRemoveSearchTermFromAPI(mNativeChromeBrowserProvider,
selection, selectionArgs);
}
private static boolean isInUiThread() {
if (!ThreadUtils.runningOnUiThread()) return false;
if (!"REL".equals(Build.VERSION.CODENAME)) {
throw new IllegalStateException("Shouldn't run in the UI thread");
}
Log.w(TAG, "ChromeBrowserProvider methods cannot be called from the UI thread.");
return true;
}
private static Uri buildContentUri(String authority, String path) {
return Uri.parse("content://" + authority + "/" + path);
}
private static Uri buildAPIContentUri(Context context, String path) {
return buildContentUri(context.getPackageName() + API_AUTHORITY_SUFFIX, path);
}
private static String buildWhereClause(long id, String selection) {
StringBuffer sb = new StringBuffer();
sb.append(BaseColumns._ID);
sb.append(" = ");
sb.append(id);
if (!TextUtils.isEmpty(selection)) {
sb.append(" AND (");
sb.append(selection);
sb.append(")");
}
return sb.toString();
}
private static String buildHistoryWhereClause(long id, String selection) {
return buildWhereClause(id, buildBookmarkWhereClause(selection, false));
}
private static String buildHistoryWhereClause(String selection) {
return buildBookmarkWhereClause(selection, false);
}
private static String buildBookmarkWhereClause(String selection, boolean isBookmark) {
StringBuffer sb = new StringBuffer();
sb.append(BookmarkColumns.BOOKMARK);
sb.append(isBookmark ? " = 1 " : " = 0");
if (!TextUtils.isEmpty(selection)) {
sb.append(" AND (");
sb.append(selection);
sb.append(")");
}
return sb.toString();
}
private static String buildBookmarkWhereClause(long id, String selection) {
return buildWhereClause(id, buildBookmarkWhereClause(selection, true));
}
private static String buildBookmarkWhereClause(String selection) {
return buildBookmarkWhereClause(selection, true);
}
private static class BookmarkRow {
Boolean isBookmark;
Long created;
String url;
Long date;
byte[] favicon;
String title;
Integer visits;
long parentId;
static BookmarkRow fromContentValues(ContentValues values) {
BookmarkRow row = new BookmarkRow();
if (values.containsKey(BookmarkColumns.URL)) {
row.url = values.getAsString(BookmarkColumns.URL);
}
if (values.containsKey(BookmarkColumns.BOOKMARK)) {
row.isBookmark = values.getAsInteger(BookmarkColumns.BOOKMARK) != 0;
}
if (values.containsKey(BookmarkColumns.CREATED)) {
row.created = values.getAsLong(BookmarkColumns.CREATED);
}
if (values.containsKey(BookmarkColumns.DATE)) {
row.date = values.getAsLong(BookmarkColumns.DATE);
}
if (values.containsKey(BookmarkColumns.FAVICON)) {
row.favicon = values.getAsByteArray(BookmarkColumns.FAVICON);
if (row.favicon == null) {
row.favicon = new byte[0];
}
}
if (values.containsKey(BookmarkColumns.TITLE)) {
row.title = values.getAsString(BookmarkColumns.TITLE);
}
if (values.containsKey(BookmarkColumns.VISITS)) {
row.visits = values.getAsInteger(BookmarkColumns.VISITS);
}
if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
row.parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
}
return row;
}
}
private static class SearchRow {
String term;
Long date;
static SearchRow fromContentValues(ContentValues values) {
SearchRow row = new SearchRow();
if (values.containsKey(SearchColumns.SEARCH)) {
row.term = values.getAsString(SearchColumns.SEARCH);
}
if (values.containsKey(SearchColumns.DATE)) {
row.date = values.getAsLong(SearchColumns.DATE);
}
return row;
}
}
protected boolean isNativeSideInitialized() {
return mNativeChromeBrowserProvider != 0;
}
private boolean ensureNativeChromeLoaded() {
ensureUriMatcherInitialized();
synchronized (mLoadNativeLock) {
if (mNativeChromeBrowserProvider != 0) return true;
final AtomicBoolean retVal = new AtomicBoolean(true);
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
retVal.set(ensureNativeChromeLoadedOnUIThread());
}
});
return retVal.get();
}
}
protected boolean ensureNativeChromeLoadedOnUIThread() {
if (isNativeSideInitialized()) return true;
mNativeChromeBrowserProvider = nativeInit();
return isNativeSideInitialized();
}
@Override
protected void finalize() throws Throwable {
try {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
ensureNativeChromeDestroyedOnUIThread();
}
});
} finally {
super.finalize();
}
}
private void ensureNativeChromeDestroyedOnUIThread() {
if (isNativeSideInitialized()) {
nativeDestroy(mNativeChromeBrowserProvider);
mNativeChromeBrowserProvider = 0;
}
}
public static Intent getShortcutToBookmark(String url, String title, Bitmap favicon, int rValue,
int gValue, int bValue, Context context) {
return BookmarkUtils.createAddToHomeIntent(
context, url, title, favicon, rValue, gValue, bValue);
}
@SuppressLint("NewApi")
private void notifyChange(final Uri uri) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
UserHandle callingUserHandle = Binder.getCallingUserHandle();
if (callingUserHandle != null &&
!callingUserHandle.equals(android.os.Process.myUserHandle())) {
ThreadUtils.postOnUiThread(new Runnable() {
@Override
public void run() {
getContext().getContentResolver().notifyChange(uri, null);
}
});
return;
}
}
getContext().getContentResolver().notifyChange(uri, null);
}
private native long nativeInit();
private native void nativeDestroy(long nativeChromeBrowserProvider);
private native long nativeAddBookmark(long nativeChromeBrowserProvider,
String url, String title, boolean isFolder, long parentId);
private native int nativeRemoveBookmark(long nativeChromeBrowserProvider, long id);
private native int nativeUpdateBookmark(long nativeChromeBrowserProvider,
long id, String url, String title, long parentId);
private native long nativeAddBookmarkFromAPI(long nativeChromeBrowserProvider,
String url, Long created, Boolean isBookmark, Long date, byte[] favicon,
String title, Integer visits, long parentId);
private native SQLiteCursor nativeQueryBookmarkFromAPI(long nativeChromeBrowserProvider,
String[] projection, String selection, String[] selectionArgs, String sortOrder);
private native int nativeUpdateBookmarkFromAPI(long nativeChromeBrowserProvider,
String url, Long created, Boolean isBookmark, Long date, byte[] favicon,
String title, Integer visits, long parentId, String selection, String[] selectionArgs);
private native int nativeRemoveBookmarkFromAPI(long nativeChromeBrowserProvider,
String selection, String[] selectionArgs);
private native int nativeRemoveHistoryFromAPI(long nativeChromeBrowserProvider,
String selection, String[] selectionArgs);
private native long nativeAddSearchTermFromAPI(long nativeChromeBrowserProvider,
String term, Long date);
private native SQLiteCursor nativeQuerySearchTermFromAPI(long nativeChromeBrowserProvider,
String[] projection, String selection, String[] selectionArgs, String sortOrder);
private native int nativeUpdateSearchTermFromAPI(long nativeChromeBrowserProvider,
String search, Long date, String selection, String[] selectionArgs);
private native int nativeRemoveSearchTermFromAPI(long nativeChromeBrowserProvider,
String selection, String[] selectionArgs);
private native boolean nativeBookmarkNodeExists(long nativeChromeBrowserProvider, long id);
private native long nativeCreateBookmarksFolderOnce(long nativeChromeBrowserProvider,
String title, long parentId);
private native BookmarkNode nativeGetAllBookmarkFolders(long nativeChromeBrowserProvider);
private native void nativeRemoveAllBookmarks(long nativeChromeBrowserProvider);
private native BookmarkNode nativeGetBookmarkNode(long nativeChromeBrowserProvider,
long id, boolean getParent, boolean getChildren);
private native BookmarkNode nativeGetMobileBookmarksFolder(long nativeChromeBrowserProvider);
private native boolean nativeIsBookmarkInMobileBookmarksBranch(long nativeChromeBrowserProvider,
long id);
private native byte[] nativeGetFaviconOrTouchIcon(long nativeChromeBrowserProvider, String url);
private native byte[] nativeGetThumbnail(long nativeChromeBrowserProvider, String url);
}