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.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.base.VisibleForTesting;
import org.chromium.chrome.browser.database.SQLiteCursor;
import org.chromium.sync.notifier.SyncStatusHelper;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * This class provides various information of Chrome, like bookmarks, most
- * visited page etc. It is used to support android.provider.Browser.
- *
+ * This class provides access to user data stored in Chrome, such as bookmarks, most visited pages,
+ * etc. It is used to support android.provider.Browser.
*/
public class ChromeBrowserProvider extends ContentProvider {
private static final String TAG = "ChromeBrowserProvider";
// Defines the API methods that the Client can call by name.
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_EDITABLE_BOOKMARK_FOLDER_HIERARCHY =
+ "GET_EDITABLE_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_DELETE_ALL_USER_BOOKMARKS = "DELETE_ALL_USER_BOOKMARKS";
static final String CLIENT_API_RESULT_KEY = "result";
/** The parameter used to specify whether this is a bookmark folder. */
public static final String BOOKMARK_IS_FOLDER_PARAM = "isFolder";
- /** Invalid id value for the Android ContentProvider API calls. */
+ /**
+ * Invalid ID value for the Android ContentProvider API calls.
+ * The value 0 is intentional: if the ID represents a bookmark node then it's the root node
+ * and not accessible. Otherwise it represents a SQLite row id, so 0 is also invalid.
+ */
public static final long INVALID_CONTENT_PROVIDER_ID = 0;
// ID used to indicate an invalid id for bookmark nodes.
return new ChromeBrowserProviderSuggestionsCursor(cursor);
}
+ /**
+ * @see android.content.ContentUris#parseId(Uri)
+ * @return The id from a content URI or -1 if the URI has no id or is malformed.
+ */
+ 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;
// Check for invalid id values if provided.
- // If it represents a bookmark node then it's the root node. Don't provide access here.
- // Otherwise it represents a SQLite row id, so 0 is invalid.
- long bookmarkId = INVALID_CONTENT_PROVIDER_ID;
- try {
- bookmarkId = ContentUris.parseId(uri);
- if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return null;
- } catch (Exception e) {
- }
+ long bookmarkId = getContentUriId(uri);
+ if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return null;
int match = mUriMatcher.match(uri);
Cursor cursor = null;
if (!canHandleContentProviderApiCall()) return 0;
// Check for invalid id values if provided.
- // If it represents a bookmark node then it's the root node and not mutable.
- // Otherwise it represents a SQLite row id, so 0 is invalid.
- long bookmarkId = INVALID_CONTENT_PROVIDER_ID;
- try {
- bookmarkId = ContentUris.parseId(uri);
- if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
- } catch (Exception e) {
- }
+ long bookmarkId = getContentUriId(uri);
+ if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
int match = mUriMatcher.match(uri);
int result;
if (!canHandleContentProviderApiCall()) return 0;
// Check for invalid id values if provided.
- // If it represents a bookmark node then it's the root node and not mutable.
- // Otherwise it represents a SQLite row id, so 0 is invalid.
- long bookmarkId = INVALID_CONTENT_PROVIDER_ID;
- try {
- bookmarkId = ContentUris.parseId(uri);
- if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
- } catch (Exception e) {
- }
+ long bookmarkId = getContentUriId(uri);
+ if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
int match = mUriMatcher.match(uri);
int result;
return nativeCreateBookmarksFolderOnce(mNativeChromeBrowserProvider, title, parentId);
}
- private BookmarkNode getBookmarkFolderHierarchy() {
- return nativeGetAllBookmarkFolders(mNativeChromeBrowserProvider);
+ private BookmarkNode getEditableBookmarkFolderHierarchy() {
+ return nativeGetEditableBookmarkFolders(mNativeChromeBrowserProvider);
}
protected BookmarkNode getBookmarkNode(long nodeId, boolean getParent, boolean getChildren,
// then use the synced node (Mobile Bookmarks).
BookmarkNode lastModified = getBookmarkNode(getLastModifiedBookmarkFolderId(), false, false,
false, false);
- if (lastModified == null) {
+ if (lastModified == null || lastModified.isUrl()) {
lastModified = getMobileBookmarksFolder();
mLastModifiedBookmarkFolderId = lastModified != null ? lastModified.id() :
INVALID_BOOKMARK_ID;
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_EDITABLE_BOOKMARK_FOLDER_HIERARCHY.equals(method)) {
+ result.putParcelable(CLIENT_API_RESULT_KEY, getEditableBookmarkFolderHierarchy());
} else if (CLIENT_API_GET_BOOKMARK_NODE.equals(method)) {
result.putParcelable(CLIENT_API_RESULT_KEY,
getBookmarkNode(extras.getLong(argKey(0)),
} 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 if (CLIENT_API_DELETE_ALL_USER_BOOKMARKS.equals(method)) {
+ nativeRemoveAllUserBookmarks(mNativeChromeBrowserProvider);
} else {
Log.w(TAG, "Received invalid method " + method);
return null;
private long addBookmarkFromAPI(ContentValues values) {
BookmarkRow row = BookmarkRow.fromContentValues(values);
- if (row.url == null) {
+ if (row.mUrl == 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);
+ row.mUrl, row.mCreated, row.mIsBookmark, row.mDate, row.mFavicon,
+ row.mTitle, row.mVisits, row.mParentId);
}
private Cursor queryBookmarkFromAPI(String[] projectionIn, 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);
+ row.mUrl, row.mCreated, row.mIsBookmark, row.mDate,
+ row.mFavicon, row.mTitle, row.mVisits, row.mParentId, selection, selectionArgs);
}
private int removeBookmarkFromAPI(String selection, String[] selectionArgs) {
}
@CalledByNative
+ private void onHistoryChanged() {
+ notifyChange(buildAPIContentUri(getContext(), HISTORY_PATH));
+ }
+
+ @CalledByNative
private void onSearchTermChanged() {
notifyChange(buildAPIContentUri(getContext(), SEARCHES_PATH));
}
private long addSearchTermFromAPI(ContentValues values) {
SearchRow row = SearchRow.fromContentValues(values);
- if (row.term == null) {
+ if (row.mTerm == null) {
throw new IllegalArgumentException("Must have a search term");
}
- return nativeAddSearchTermFromAPI(mNativeChromeBrowserProvider, row.term, row.date);
+ return nativeAddSearchTermFromAPI(mNativeChromeBrowserProvider, row.mTerm, row.mDate);
}
private int updateSearchTermFromAPI(ContentValues values, String selection,
String[] selectionArgs) {
SearchRow row = SearchRow.fromContentValues(values);
return nativeUpdateSearchTermFromAPI(mNativeChromeBrowserProvider,
- row.term, row.date, selection, selectionArgs);
+ row.mTerm, row.mDate, selection, selectionArgs);
}
private Cursor querySearchTermFromAPI(String[] projectionIn, String selection,
// Wrap the value of BookmarkColumn.
private static class BookmarkRow {
- Boolean isBookmark;
- Long created;
- String url;
- Long date;
- byte[] favicon;
- String title;
- Integer visits;
- long parentId;
+ Boolean mIsBookmark;
+ Long mCreated;
+ String mUrl;
+ Long mDate;
+ byte[] mFavicon;
+ String mTitle;
+ Integer mVisits;
+ long mParentId;
static BookmarkRow fromContentValues(ContentValues values) {
BookmarkRow row = new BookmarkRow();
if (values.containsKey(BookmarkColumns.URL)) {
- row.url = values.getAsString(BookmarkColumns.URL);
+ row.mUrl = values.getAsString(BookmarkColumns.URL);
}
if (values.containsKey(BookmarkColumns.BOOKMARK)) {
- row.isBookmark = values.getAsInteger(BookmarkColumns.BOOKMARK) != 0;
+ row.mIsBookmark = values.getAsInteger(BookmarkColumns.BOOKMARK) != 0;
}
if (values.containsKey(BookmarkColumns.CREATED)) {
- row.created = values.getAsLong(BookmarkColumns.CREATED);
+ row.mCreated = values.getAsLong(BookmarkColumns.CREATED);
}
if (values.containsKey(BookmarkColumns.DATE)) {
- row.date = values.getAsLong(BookmarkColumns.DATE);
+ row.mDate = values.getAsLong(BookmarkColumns.DATE);
}
if (values.containsKey(BookmarkColumns.FAVICON)) {
- row.favicon = values.getAsByteArray(BookmarkColumns.FAVICON);
+ row.mFavicon = values.getAsByteArray(BookmarkColumns.FAVICON);
// We need to know that the caller set the favicon column.
- if (row.favicon == null) {
- row.favicon = new byte[0];
+ if (row.mFavicon == null) {
+ row.mFavicon = new byte[0];
}
}
if (values.containsKey(BookmarkColumns.TITLE)) {
- row.title = values.getAsString(BookmarkColumns.TITLE);
+ row.mTitle = values.getAsString(BookmarkColumns.TITLE);
}
if (values.containsKey(BookmarkColumns.VISITS)) {
- row.visits = values.getAsInteger(BookmarkColumns.VISITS);
+ row.mVisits = values.getAsInteger(BookmarkColumns.VISITS);
}
if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
- row.parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
+ row.mParentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
}
return row;
}
// Wrap the value of SearchColumn.
private static class SearchRow {
- String term;
- Long date;
+ String mTerm;
+ Long mDate;
static SearchRow fromContentValues(ContentValues values) {
SearchRow row = new SearchRow();
if (values.containsKey(SearchColumns.SEARCH)) {
- row.term = values.getAsString(SearchColumns.SEARCH);
+ row.mTerm = values.getAsString(SearchColumns.SEARCH);
}
if (values.containsKey(SearchColumns.DATE)) {
- row.date = values.getAsLong(SearchColumns.DATE);
+ row.mDate = values.getAsLong(SearchColumns.DATE);
}
return row;
}
}
}
- /**
- * Call to get the intent to create a bookmark shortcut on homescreen.
- */
- 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 the calling user is different than current one, we need to post a
// task to notify change, otherwise, a system level hidden permission
private native long nativeCreateBookmarksFolderOnce(long nativeChromeBrowserProvider,
String title, long parentId);
- private native BookmarkNode nativeGetAllBookmarkFolders(long nativeChromeBrowserProvider);
+ private native BookmarkNode nativeGetEditableBookmarkFolders(long nativeChromeBrowserProvider);
- private native void nativeRemoveAllBookmarks(long nativeChromeBrowserProvider);
+ private native void nativeRemoveAllUserBookmarks(long nativeChromeBrowserProvider);
private native BookmarkNode nativeGetBookmarkNode(long nativeChromeBrowserProvider,
long id, boolean getParent, boolean getChildren);