EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.chrome.browser]

COVERAGE SUMMARY FOR SOURCE FILE [ChromeBrowserProvider.java]

nameclass, %method, %block, %line, %
ChromeBrowserProvider.java38%  (3/8)22%  (22/100)17%  (437/2583)16%  (78.3/497)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ChromeBrowserProvider0%   (0/1)0%   (0/60)0%   (0/1911)0%   (0/372)
<static initializer> 0%   (0/1)0%   (0/75)0%   (0/6)
ChromeBrowserProvider (): void 0%   (0/1)0%   (0/16)0%   (0/5)
access$000 (ChromeBrowserProvider): void 0%   (0/1)0%   (0/3)0%   (0/1)
addBookmark (ContentValues): long 0%   (0/1)0%   (0/56)0%   (0/14)
addBookmarkFromAPI (ContentValues): long 0%   (0/1)0%   (0/32)0%   (0/4)
addSearchTermFromAPI (ContentValues): long 0%   (0/1)0%   (0/20)0%   (0/4)
argKey (int): String 0%   (0/1)0%   (0/9)0%   (0/1)
bookmarkNodeExists (long): boolean 0%   (0/1)0%   (0/12)0%   (0/2)
buildAPIContentUri (Context, String): Uri 0%   (0/1)0%   (0/12)0%   (0/1)
buildBookmarkWhereClause (String): String 0%   (0/1)0%   (0/4)0%   (0/1)
buildBookmarkWhereClause (String, boolean): String 0%   (0/1)0%   (0/34)0%   (0/8)
buildBookmarkWhereClause (long, String): String 0%   (0/1)0%   (0/6)0%   (0/1)
buildContentUri (String, String): Uri 0%   (0/1)0%   (0/14)0%   (0/1)
buildHistoryWhereClause (String): String 0%   (0/1)0%   (0/4)0%   (0/1)
buildHistoryWhereClause (long, String): String 0%   (0/1)0%   (0/6)0%   (0/1)
buildSuggestWhere (String, int): String 0%   (0/1)0%   (0/25)0%   (0/5)
buildWhereClause (long, String): String 0%   (0/1)0%   (0/34)0%   (0/9)
call (String, String, Bundle): Bundle 0%   (0/1)0%   (0/153)0%   (0/23)
canHandleContentProviderApiCall (): boolean 0%   (0/1)0%   (0/14)0%   (0/4)
createBookmarksFolderOnce (String, long): long 0%   (0/1)0%   (0/7)0%   (0/1)
delete (Uri, String, String []): int 0%   (0/1)0%   (0/111)0%   (0/30)
ensureNativeChromeDestroyedOnUIThread (): void 0%   (0/1)0%   (0/11)0%   (0/4)
ensureNativeChromeLoaded (): boolean 0%   (0/1)0%   (0/35)0%   (0/7)
ensureNativeChromeLoadedOnUIThread (): boolean 0%   (0/1)0%   (0/12)0%   (0/3)
ensureUriMatcherInitialized (): void 0%   (0/1)0%   (0/240)0%   (0/39)
finalize (): void 0%   (0/1)0%   (0/14)0%   (0/4)
getApiAuthority (Context): String 0%   (0/1)0%   (0/10)0%   (0/1)
getBookmarkFolderHierarchy (): ChromeBrowserProvider$BookmarkNode 0%   (0/1)0%   (0/5)0%   (0/1)
getBookmarkFolderUri (Context): Uri 0%   (0/1)0%   (0/5)0%   (0/1)
getBookmarkHistorySuggestions (String, String [], String, boolean): Cursor 0%   (0/1)0%   (0/146)0%   (0/23)
getBookmarkNode (long, boolean, boolean, boolean, boolean): ChromeBrowserProv... 0%   (0/1)0%   (0/56)0%   (0/8)
getBookmarksApiUri (Context): Uri 0%   (0/1)0%   (0/5)0%   (0/1)
getBookmarksUri (Context): Uri 0%   (0/1)0%   (0/5)0%   (0/1)
getDefaultBookmarkFolder (): ChromeBrowserProvider$BookmarkNode 0%   (0/1)0%   (0/24)0%   (0/5)
getInternalAuthority (Context): String 0%   (0/1)0%   (0/10)0%   (0/1)
getLastModifiedBookmarkFolderId (): long 0%   (0/1)0%   (0/18)0%   (0/4)
getMobileBookmarksFolder (): ChromeBrowserProvider$BookmarkNode 0%   (0/1)0%   (0/12)0%   (0/3)
getMobileBookmarksFolderId (): long 0%   (0/1)0%   (0/10)0%   (0/2)
getReadWritePermissionNameForBookmarkFolders (): String 0%   (0/1)0%   (0/14)0%   (0/1)
getSearchesApiUri (Context): Uri 0%   (0/1)0%   (0/5)0%   (0/1)
getShortcutToBookmark (String, String, Bitmap, int, int, int, Activity): Intent 0%   (0/1)0%   (0/9)0%   (0/1)
getType (Uri): String 0%   (0/1)0%   (0/33)0%   (0/10)
insert (Uri, ContentValues): Uri 0%   (0/1)0%   (0/73)0%   (0/15)
isBookmarkInMobileBookmarksBranch (long): boolean 0%   (0/1)0%   (0/12)0%   (0/2)
isInUiThread (): boolean 0%   (0/1)0%   (0/19)0%   (0/5)
isNativeSideInitialized (): boolean 0%   (0/1)0%   (0/7)0%   (0/1)
onBookmarkChanged (): void 0%   (0/1)0%   (0/10)0%   (0/2)
onCreate (): boolean 0%   (0/1)0%   (0/6)0%   (0/2)
onSearchTermChanged (): void 0%   (0/1)0%   (0/10)0%   (0/2)
populateNodeImages (ChromeBrowserProvider$BookmarkNode, boolean, boolean): void 0%   (0/1)0%   (0/28)0%   (0/6)
query (Uri, String [], String, String [], String): Cursor 0%   (0/1)0%   (0/145)0%   (0/34)
queryBookmarkFromAPI (String [], String, String [], String): Cursor 0%   (0/1)0%   (0/21)0%   (0/5)
querySearchTermFromAPI (String [], String, String [], String): Cursor 0%   (0/1)0%   (0/21)0%   (0/5)
removeBookmarkFromAPI (String, String []): int 0%   (0/1)0%   (0/7)0%   (0/1)
removeHistoryFromAPI (String, String []): int 0%   (0/1)0%   (0/7)0%   (0/1)
removeSearchFromAPI (String, String []): int 0%   (0/1)0%   (0/7)0%   (0/1)
update (Uri, ContentValues, String, String []): int 0%   (0/1)0%   (0/151)0%   (0/38)
updateBookmarkFromAPI (ContentValues, String, String []): int 0%   (0/1)0%   (0/26)0%   (0/2)
updateLastModifiedBookmarkFolder (long): void 0%   (0/1)0%   (0/21)0%   (0/5)
updateSearchTermFromAPI (ContentValues, String, String []): int 0%   (0/1)0%   (0/14)0%   (0/2)
     
class ChromeBrowserProvider$10%   (0/1)0%   (0/2)0%   (0/16)0%   (0/3)
ChromeBrowserProvider$1 (ChromeBrowserProvider, AtomicBoolean): void 0%   (0/1)0%   (0/9)0%   (0/1)
run (): void 0%   (0/1)0%   (0/7)0%   (0/2)
     
class ChromeBrowserProvider$20%   (0/1)0%   (0/2)0%   (0/10)0%   (0/3)
ChromeBrowserProvider$2 (ChromeBrowserProvider): void 0%   (0/1)0%   (0/6)0%   (0/1)
run (): void 0%   (0/1)0%   (0/4)0%   (0/2)
     
class ChromeBrowserProvider$BookmarkRow0%   (0/1)0%   (0/2)0%   (0/95)0%   (0/21)
ChromeBrowserProvider$BookmarkRow (): void 0%   (0/1)0%   (0/3)0%   (0/1)
fromContentValues (ContentValues): ChromeBrowserProvider$BookmarkRow 0%   (0/1)0%   (0/92)0%   (0/20)
     
class ChromeBrowserProvider$SearchRow0%   (0/1)0%   (0/2)0%   (0/27)0%   (0/7)
ChromeBrowserProvider$SearchRow (): void 0%   (0/1)0%   (0/3)0%   (0/1)
fromContentValues (ContentValues): ChromeBrowserProvider$SearchRow 0%   (0/1)0%   (0/24)0%   (0/6)
     
class ChromeBrowserProvider$BookmarkNode100% (1/1)64%  (14/22)80%  (230/288)79%  (39.7/50)
create (long, int, String, String, ChromeBrowserProvider$BookmarkNode): Chrom... 0%   (0/1)0%   (0/11)0%   (0/1)
describeContents (): int 0%   (0/1)0%   (0/2)0%   (0/1)
favicon (): byte [] 0%   (0/1)0%   (0/3)0%   (0/1)
isUrl (): boolean 0%   (0/1)0%   (0/7)0%   (0/1)
name (): String 0%   (0/1)0%   (0/3)0%   (0/1)
thumbnail (): byte [] 0%   (0/1)0%   (0/3)0%   (0/1)
type (): ChromeBrowserProvider$Type 0%   (0/1)0%   (0/3)0%   (0/1)
url (): String 0%   (0/1)0%   (0/3)0%   (0/1)
byteArrayEqual (byte [], byte []): boolean 100% (1/1)46%  (12/26)57%  (1.7/3)
getHierarchyRoot (): ChromeBrowserProvider$BookmarkNode 100% (1/1)64%  (7/11)75%  (3/4)
equalContents (ChromeBrowserProvider$BookmarkNode): boolean 100% (1/1)95%  (91/96)94%  (0.9/1)
<static initializer> 100% (1/1)100% (5/5)100% (1/1)
ChromeBrowserProvider$BookmarkNode (long, ChromeBrowserProvider$Type, String,... 100% (1/1)100% (23/23)100% (8/8)
addChild (ChromeBrowserProvider$BookmarkNode): void 100% (1/1)100% (6/6)100% (2/2)
children (): List 100% (1/1)100% (3/3)100% (1/1)
id (): long 100% (1/1)100% (3/3)100% (1/1)
parent (): ChromeBrowserProvider$BookmarkNode 100% (1/1)100% (3/3)100% (1/1)
setFavicon (byte []): void 100% (1/1)100% (4/4)100% (2/2)
setThumbnail (byte []): void 100% (1/1)100% (4/4)100% (2/2)
writeNodeContents (Parcel): void 100% (1/1)100% (36/36)100% (8/8)
writeNodeContentsRecursive (Parcel): void 100% (1/1)100% (24/24)100% (5/5)
writeToParcel (Parcel, int): void 100% (1/1)100% (9/9)100% (3/3)
     
class ChromeBrowserProvider$BookmarkNode$1100% (1/1)83%  (5/6)86%  (144/168)87%  (33.8/39)
newArray (int): ChromeBrowserProvider$BookmarkNode [] 0%   (0/1)0%   (0/3)0%   (0/1)
getNode (long): ChromeBrowserProvider$BookmarkNode 100% (1/1)61%  (20/33)67%  (4/6)
readNodeContents (Parcel): ChromeBrowserProvider$BookmarkNode 100% (1/1)89%  (48/54)86%  (12/14)
readNodeContentsRecursive (Parcel): ChromeBrowserProvider$BookmarkNode 100% (1/1)96%  (52/54)98%  (10.8/11)
ChromeBrowserProvider$BookmarkNode$1 (): void 100% (1/1)100% (3/3)100% (1/1)
createFromParcel (Parcel): ChromeBrowserProvider$BookmarkNode 100% (1/1)100% (21/21)100% (6/6)
     
class ChromeBrowserProvider$Type100% (1/1)75%  (3/4)93%  (63/68)99%  (5.9/6)
valueOf (String): ChromeBrowserProvider$Type 0%   (0/1)0%   (0/5)0%   (0/1)
<static initializer> 100% (1/1)100% (54/54)100% (6/6)
ChromeBrowserProvider$Type (String, int): void 100% (1/1)100% (5/5)100% (1/1)
values (): ChromeBrowserProvider$Type [] 100% (1/1)100% (4/4)100% (1/1)

1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4 
5package org.chromium.chrome.browser;
6 
7import android.app.Activity;
8import android.app.SearchManager;
9import android.content.ContentProvider;
10import android.content.ContentUris;
11import android.content.ContentValues;
12import android.content.Context;
13import android.content.Intent;
14import android.content.SharedPreferences;
15import android.content.UriMatcher;
16import android.database.Cursor;
17import android.database.MatrixCursor;
18import android.graphics.Bitmap;
19import android.net.Uri;
20import android.os.Binder;
21import android.os.Build;
22import android.os.Bundle;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.preference.PreferenceManager;
26import android.provider.BaseColumns;
27import android.provider.Browser;
28import android.provider.Browser.BookmarkColumns;
29import android.provider.Browser.SearchColumns;
30import android.text.TextUtils;
31import android.util.Log;
32 
33import com.google.common.annotations.VisibleForTesting;
34 
35import org.chromium.base.CalledByNative;
36import org.chromium.base.CalledByNativeUnchecked;
37import org.chromium.base.ThreadUtils;
38import org.chromium.chrome.browser.database.SQLiteCursor;
39import org.chromium.sync.notifier.SyncStatusHelper;
40 
41import java.util.ArrayList;
42import java.util.Arrays;
43import java.util.HashMap;
44import java.util.List;
45import java.util.Vector;
46import java.util.concurrent.atomic.AtomicBoolean;
47 
48/**
49 * This class provides various information of Chrome, like bookmarks, most
50 * visited page etc. It is used to support android.provider.Browser.
51 *
52 */
53public class ChromeBrowserProvider extends ContentProvider {
54    private static final String TAG = "ChromeBrowserProvider";
55 
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";
60 
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";
73 
74 
75    // Defines Chrome's API authority, so it can be run and tested
76    // independently.
77    private static final String API_AUTHORITY_SUFFIX = ".browser";
78 
79    private static final String BROWSER_CONTRACT_API_AUTHORITY =
80        "com.google.android.apps.chrome.browser-contract";
81 
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";
89 
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";
99 
100    public static final Uri BROWSER_CONTRACTS_BOOKMAKRS_API_URI = buildContentUri(
101            BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH);
102 
103    public static final Uri BROWSER_CONTRACTS_SEARCHES_API_URI = buildContentUri(
104            BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH);
105 
106    public static final Uri BROWSER_CONTRACTS_HISTORY_API_URI = buildContentUri(
107            BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH);
108 
109    public static final Uri BROWSER_CONTRACTS_COMBINED_API_URI = buildContentUri(
110            BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH);
111 
112    /** The parameter used to specify a bookmark parent ID in ContentValues. */
113    public static final String BOOKMARK_PARENT_ID_PARAM = "parentId";
114 
115    /** The parameter used to specify whether this is a bookmark folder. */
116    public static final String BOOKMARK_IS_FOLDER_PARAM = "isFolder";
117 
118    /** Invalid id value for the Android ContentProvider API calls. */
119    public static final long INVALID_CONTENT_PROVIDER_ID = 0;
120 
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;
124 
125    private static final String LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY = "last_bookmark_folder_id";
126 
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;
139 
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};
146 
147    private static final String[] SUGGEST_PROJECTION = new String[] {
148        BookmarkColumns._ID,
149        BookmarkColumns.TITLE,
150        BookmarkColumns.URL,
151        BookmarkColumns.DATE,
152        BookmarkColumns.BOOKMARK
153    };
154 
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;
161 
162    /**
163     * Records whether we've received a call to one of the public ContentProvider APIs.
164     */
165    protected boolean mContentProviderApiCalled;
166 
167    private void ensureUriMatcherInitialized() {
168        synchronized (mInitializeUriMatcherLock) {
169            if (mUriMatcher != null) return;
170 
171            mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
172            // The internal URIs
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
220            // SearchColumns
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);
225 
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);
232        }
233    }
234 
235    @Override
236    public boolean onCreate() {
237        // Pre-load shared preferences object, this happens on a separate thread
238        PreferenceManager.getDefaultSharedPreferences(getContext());
239        return true;
240    }
241 
242    /**
243     * Lazily fetches the last modified bookmark folder id.
244     */
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);
251        }
252        return mLastModifiedBookmarkFolderId;
253    }
254 
255    private String buildSuggestWhere(String selection, int argc) {
256        StringBuilder sb = new StringBuilder(selection);
257        for (int i = 0; i < argc - 1; i++) {
258            sb.append(" OR ");
259            sb.append(selection);
260        }
261        return sb.toString();
262    }
263 
264    private String getReadWritePermissionNameForBookmarkFolders() {
265        return getContext().getApplicationContext().getPackageName() + ".permission."
266                + PERMISSION_READ_WRITE_BOOKMARKS;
267    }
268 
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")) {
275            args.add(like);
276        } else {
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);
283            matchTitles = true;
284        }
285 
286        StringBuilder urlWhere = new StringBuilder("(");
287        urlWhere.append(buildSuggestWhere(selection, args.size()));
288        if (matchTitles) {
289            args.add(like);
290            urlWhere.append(" OR title LIKE ?");
291        }
292        urlWhere.append(")");
293 
294        if (excludeHistory) {
295            urlWhere.append(" AND bookmark=?");
296            args.add("1");
297        }
298 
299        selectionArgs = args.toArray(selectionArgs);
300        Cursor cursor = queryBookmarkFromAPI(SUGGEST_PROJECTION, urlWhere.toString(),
301                selectionArgs, sortOrder);
302        return new ChromeBrowserProviderSuggestionsCursor(cursor);
303    }
304 
305    @Override
306    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
307            String sortOrder) {
308        if (!canHandleContentProviderApiCall()) return null;
309 
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;
314        try {
315            bookmarkId = ContentUris.parseId(uri);
316            if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return null;
317        } catch (Exception e) {
318        }
319 
320        int match = mUriMatcher.match(uri);
321        Cursor cursor = null;
322        switch (match) {
323            case URL_MATCH_BOOKMARK_SUGGESTIONS_ID:
324                cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, true);
325                break;
326            case URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID:
327                cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, false);
328                break;
329            case URL_MATCH_API_BOOKMARK:
330                cursor = queryBookmarkFromAPI(projection, selection, selectionArgs, sortOrder);
331                break;
332            case URL_MATCH_API_BOOKMARK_ID:
333                cursor = queryBookmarkFromAPI(projection, buildWhereClause(bookmarkId, selection),
334                        selectionArgs, sortOrder);
335                break;
336            case URL_MATCH_API_SEARCHES:
337                cursor = querySearchTermFromAPI(projection, selection, selectionArgs, sortOrder);
338                break;
339            case URL_MATCH_API_SEARCHES_ID:
340                cursor = querySearchTermFromAPI(projection, buildWhereClause(bookmarkId, selection),
341                        selectionArgs, sortOrder);
342                break;
343            case URL_MATCH_API_HISTORY_CONTENT:
344                cursor = queryBookmarkFromAPI(projection, buildHistoryWhereClause(selection),
345                        selectionArgs, sortOrder);
346                break;
347            case URL_MATCH_API_HISTORY_CONTENT_ID:
348                cursor = queryBookmarkFromAPI(projection,
349                        buildHistoryWhereClause(bookmarkId, selection), selectionArgs, sortOrder);
350                break;
351            case URL_MATCH_API_BOOKMARK_CONTENT:
352                cursor = queryBookmarkFromAPI(projection, buildBookmarkWhereClause(selection),
353                        selectionArgs, sortOrder);
354                break;
355            case URL_MATCH_API_BOOKMARK_CONTENT_ID:
356                cursor = queryBookmarkFromAPI(projection,
357                        buildBookmarkWhereClause(bookmarkId, selection), selectionArgs, sortOrder);
358                break;
359            default:
360                throw new IllegalArgumentException(TAG + ": query - unknown URL uri = " + uri);
361        }
362        if (cursor == null) {
363            cursor = new MatrixCursor(new String[] { });
364        }
365        cursor.setNotificationUri(getContext().getContentResolver(), uri);
366        return cursor;
367    }
368 
369    @Override
370    public Uri insert(Uri uri, ContentValues values) {
371        if (!canHandleContentProviderApiCall()) return null;
372 
373        int match = mUriMatcher.match(uri);
374        Uri res = null;
375        long id;
376        switch (match) {
377            case URI_MATCH_BOOKMARKS:
378                id = addBookmark(values);
379                if (id == INVALID_BOOKMARK_ID) return null;
380                break;
381            case URL_MATCH_API_BOOKMARK_CONTENT:
382                values.put(BookmarkColumns.BOOKMARK, 1);
383                //$FALL-THROUGH$
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;
388                break;
389            case URL_MATCH_API_SEARCHES:
390                id = addSearchTermFromAPI(values);
391                if (id == INVALID_CONTENT_PROVIDER_ID) return null;
392                break;
393            default:
394                throw new IllegalArgumentException(TAG + ": insert - unknown URL " + uri);
395        }
396 
397        res = ContentUris.withAppendedId(uri, id);
398        getContext().getContentResolver().notifyChange(res, null);
399        return res;
400    }
401 
402    @Override
403    public int delete(Uri uri, String selection, String[] selectionArgs) {
404        if (!canHandleContentProviderApiCall()) return 0;
405 
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;
410        try {
411            bookmarkId = ContentUris.parseId(uri);
412            if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
413        } catch (Exception e) {
414        }
415 
416        int match = mUriMatcher.match(uri);
417        int result;
418        switch (match) {
419            case URI_MATCH_BOOKMARKS_ID :
420                result = nativeRemoveBookmark(mNativeChromeBrowserProvider, bookmarkId);
421                break;
422            case URL_MATCH_API_BOOKMARK_ID:
423                result = removeBookmarkFromAPI(
424                        buildWhereClause(bookmarkId, selection), selectionArgs);
425                break;
426            case URL_MATCH_API_BOOKMARK:
427                result = removeBookmarkFromAPI(selection, selectionArgs);
428                break;
429            case URL_MATCH_API_SEARCHES_ID:
430                result = removeSearchFromAPI(buildWhereClause(bookmarkId, selection),
431                        selectionArgs);
432                break;
433            case URL_MATCH_API_SEARCHES:
434                result = removeSearchFromAPI(selection, selectionArgs);
435                break;
436            case URL_MATCH_API_HISTORY_CONTENT:
437                result = removeHistoryFromAPI(selection, selectionArgs);
438                break;
439            case URL_MATCH_API_HISTORY_CONTENT_ID:
440                result = removeHistoryFromAPI(buildWhereClause(bookmarkId, selection),
441                        selectionArgs);
442                break;
443            case URL_MATCH_API_BOOKMARK_CONTENT:
444                result = removeBookmarkFromAPI(buildBookmarkWhereClause(selection), selectionArgs);
445                break;
446            case URL_MATCH_API_BOOKMARK_CONTENT_ID:
447                result = removeBookmarkFromAPI(buildBookmarkWhereClause(bookmarkId, selection),
448                        selectionArgs);
449                break;
450            default:
451                throw new IllegalArgumentException(TAG + ": delete - unknown URL " + uri);
452        }
453        if (result != 0) {
454            getContext().getContentResolver().notifyChange(uri, null);
455        }
456        return result;
457    }
458 
459    @Override
460    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
461        if (!canHandleContentProviderApiCall()) return 0;
462 
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;
467        try {
468            bookmarkId = ContentUris.parseId(uri);
469            if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
470        } catch (Exception e) {
471        }
472 
473        int match = mUriMatcher.match(uri);
474        int result;
475        switch (match) {
476            case URI_MATCH_BOOKMARKS_ID:
477                String url = null;
478                if (values.containsKey(Browser.BookmarkColumns.URL)) {
479                    url = values.getAsString(Browser.BookmarkColumns.URL);
480                }
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);
485                }
486                result = nativeUpdateBookmark(mNativeChromeBrowserProvider, bookmarkId, url, title,
487                        parentId);
488                updateLastModifiedBookmarkFolder(parentId);
489                break;
490            case URL_MATCH_API_BOOKMARK_ID:
491                result = updateBookmarkFromAPI(values, buildWhereClause(bookmarkId, selection),
492                        selectionArgs);
493                break;
494            case URL_MATCH_API_BOOKMARK:
495                result = updateBookmarkFromAPI(values, selection, selectionArgs);
496                break;
497            case URL_MATCH_API_SEARCHES_ID:
498                result = updateSearchTermFromAPI(values, buildWhereClause(bookmarkId, selection),
499                        selectionArgs);
500                break;
501            case URL_MATCH_API_SEARCHES:
502                result = updateSearchTermFromAPI(values, selection, selectionArgs);
503                break;
504            case URL_MATCH_API_HISTORY_CONTENT:
505                result = updateBookmarkFromAPI(values, buildHistoryWhereClause(selection),
506                        selectionArgs);
507                break;
508            case URL_MATCH_API_HISTORY_CONTENT_ID:
509                result = updateBookmarkFromAPI(values,
510                        buildHistoryWhereClause(bookmarkId, selection), selectionArgs);
511                break;
512            case URL_MATCH_API_BOOKMARK_CONTENT:
513                result = updateBookmarkFromAPI(values, buildBookmarkWhereClause(selection),
514                        selectionArgs);
515                break;
516            case URL_MATCH_API_BOOKMARK_CONTENT_ID:
517                result = updateBookmarkFromAPI(values,
518                        buildBookmarkWhereClause(bookmarkId, selection), selectionArgs);
519                break;
520            default:
521                throw new IllegalArgumentException(TAG + ": update - unknown URL " + uri);
522        }
523        if (result != 0) {
524            getContext().getContentResolver().notifyChange(uri, null);
525        }
526        return result;
527    }
528 
529    @Override
530    public String getType(Uri uri) {
531        ensureUriMatcherInitialized();
532        int match = mUriMatcher.match(uri);
533        switch (match) {
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;
548            default:
549                throw new IllegalArgumentException(TAG + ": getType - unknown URL " + uri);
550        }
551    }
552 
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);
559        }
560        long parentId = INVALID_BOOKMARK_ID;
561        if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
562            parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
563        }
564        long id = nativeAddBookmark(mNativeChromeBrowserProvider, url, title, isFolder, parentId);
565        if (id == INVALID_BOOKMARK_ID) return id;
566 
567        if (isFolder) {
568            updateLastModifiedBookmarkFolder(id);
569        } else {
570            updateLastModifiedBookmarkFolder(parentId);
571        }
572        return id;
573    }
574 
575    private void updateLastModifiedBookmarkFolder(long id) {
576        if (getLastModifiedBookmarkFolderId() == id) return;
577 
578        mLastModifiedBookmarkFolderId = id;
579        SharedPreferences sharedPreferences =
580                PreferenceManager.getDefaultSharedPreferences(getContext());
581        sharedPreferences.edit()
582                .putLong(LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, mLastModifiedBookmarkFolderId)
583                .apply();
584    }
585 
586    public static String getApiAuthority(Context context) {
587        return context.getPackageName() + API_AUTHORITY_SUFFIX;
588    }
589 
590    public static String getInternalAuthority(Context context) {
591        return context.getPackageName() + AUTHORITY_SUFFIX;
592    }
593 
594    public static Uri getBookmarksUri(Context context) {
595        return buildContentUri(getInternalAuthority(context), BOOKMARKS_PATH);
596    }
597 
598    public static Uri getBookmarkFolderUri(Context context) {
599        return buildContentUri(getInternalAuthority(context), BOOKMARK_FOLDER_PATH);
600    }
601 
602    public static Uri getBookmarksApiUri(Context context) {
603        return buildContentUri(getApiAuthority(context), BOOKMARKS_PATH);
604    }
605 
606    public static Uri getSearchesApiUri(Context context) {
607        return buildContentUri(getApiAuthority(context), SEARCHES_PATH);
608    }
609 
610    private boolean bookmarkNodeExists(long nodeId) {
611        if (nodeId < 0) return false;
612        return nativeBookmarkNodeExists(mNativeChromeBrowserProvider, nodeId);
613    }
614 
615    private long createBookmarksFolderOnce(String title, long parentId) {
616        return nativeCreateBookmarksFolderOnce(mNativeChromeBrowserProvider, title, parentId);
617    }
618 
619    private BookmarkNode getBookmarkFolderHierarchy() {
620        return nativeGetAllBookmarkFolders(mNativeChromeBrowserProvider);
621    }
622 
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()) {
629            getParent = false;
630        }
631 
632        BookmarkNode node = nativeGetBookmarkNode(mNativeChromeBrowserProvider, nodeId, getParent,
633                getChildren);
634        if (!getFavicons && !getThumbnails) return node;
635 
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);
641        }
642 
643        return node;
644    }
645 
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,
650                false, false);
651        if (lastModified == null) {
652            lastModified = getMobileBookmarksFolder();
653            mLastModifiedBookmarkFolderId = lastModified != null ? lastModified.id() :
654                    INVALID_BOOKMARK_ID;
655        }
656        return lastModified;
657    }
658 
659    private void populateNodeImages(BookmarkNode node, boolean favicon, boolean thumbnail) {
660        if (node == null || node.type() != Type.URL) return;
661 
662        if (favicon) {
663            node.setFavicon(nativeGetFaviconOrTouchIcon(mNativeChromeBrowserProvider, node.url()));
664        }
665 
666        if (thumbnail) {
667            node.setThumbnail(nativeGetThumbnail(mNativeChromeBrowserProvider, node.url()));
668        }
669    }
670 
671    private BookmarkNode getMobileBookmarksFolder() {
672        if (mMobileBookmarksFolder == null) {
673            mMobileBookmarksFolder = nativeGetMobileBookmarksFolder(mNativeChromeBrowserProvider);
674        }
675        return mMobileBookmarksFolder;
676    }
677 
678    protected long getMobileBookmarksFolderId() {
679        BookmarkNode mobileBookmarks = getMobileBookmarksFolder();
680        return mobileBookmarks != null ? mobileBookmarks.id() : INVALID_BOOKMARK_ID;
681    }
682 
683    private boolean isBookmarkInMobileBookmarksBranch(long nodeId) {
684        if (nodeId <= 0) return false;
685        return nativeIsBookmarkInMobileBookmarksBranch(mNativeChromeBrowserProvider, nodeId);
686    }
687 
688    static String argKey(int i) {
689        return "arg" + i;
690    }
691 
692    @Override
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;
700 
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);
727        } else {
728            Log.w(TAG, "Received invalid method " + method);
729            return null;
730        }
731 
732        return result;
733    }
734 
735    /**
736     * Checks whether Chrome is sufficiently initialized to handle a call to the
737     * ChromeBrowserProvider.
738     */
739    private boolean canHandleContentProviderApiCall() {
740        mContentProviderApiCalled = true;
741 
742        if (isInUiThread()) return false;
743        if (!ensureNativeChromeLoaded()) return false;
744        return true;
745    }
746 
747    /**
748     * The type of a BookmarkNode.
749     */
750    public enum Type {
751        URL,
752        FOLDER,
753        BOOKMARK_BAR,
754        OTHER_NODE,
755        MOBILE
756    }
757 
758    /**
759     * Simple Data Object representing the chrome bookmark node.
760     */
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>();
768 
769        // Favicon and thumbnail optionally set in a 2-step procedure.
770        private byte[] mFavicon;
771        private byte[] mThumbnail;
772 
773        /** Used to pass structured data back from the native code. */
774        @VisibleForTesting
775        public BookmarkNode(long id, Type type, String name, String url, BookmarkNode parent) {
776            mId = id;
777            mName = name;
778            mUrl = url;
779            mType = type;
780            mParent = parent;
781        }
782 
783        /**
784         * @return The id of this bookmark entry.
785         */
786        public long id() {
787            return mId;
788        }
789 
790        /**
791         * @return The name of this bookmark entry.
792         */
793        public String name() {
794            return mName;
795        }
796 
797        /**
798         * @return The URL of this bookmark entry.
799         */
800        public String url() {
801            return mUrl;
802        }
803 
804        /**
805         * @return The type of this bookmark entry.
806         */
807        public Type type() {
808            return mType;
809        }
810 
811        /**
812         * @return The bookmark favicon, if any.
813         */
814        public byte[] favicon() {
815            return mFavicon;
816        }
817 
818        /**
819         * @return The bookmark thumbnail, if any.
820         */
821        public byte[] thumbnail() {
822            return mThumbnail;
823        }
824 
825        /**
826         * @return The parent folder of this bookmark entry.
827         */
828        public BookmarkNode parent() {
829            return mParent;
830        }
831 
832        /**
833         * Adds a child to this node.
834         *
835         * <p>
836         * Used solely by the native code.
837         */
838        @VisibleForTesting
839        @CalledByNativeUnchecked("BookmarkNode")
840        public void addChild(BookmarkNode child) {
841            mChildren.add(child);
842        }
843 
844        /**
845         * @return The child bookmark nodes of this node.
846         */
847        public List<BookmarkNode> children() {
848            return mChildren;
849        }
850 
851        /**
852         * @return Whether this node represents a bookmarked URL or not.
853         */
854        public boolean isUrl() {
855            return mUrl != null;
856        }
857 
858        /**
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.
861         */
862        public boolean equalContents(BookmarkNode node) {
863            return node != null &&
864                    mId == node.mId &&
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();
874        }
875 
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);
880        }
881 
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);
886        }
887 
888        @VisibleForTesting
889        public void setFavicon(byte[] favicon) {
890            mFavicon = favicon;
891        }
892 
893        @VisibleForTesting
894        public void setThumbnail(byte[] thumbnail) {
895            mThumbnail = thumbnail;
896        }
897 
898        @Override
899        public int describeContents() {
900            return 0;
901        }
902 
903        @Override
904        public void writeToParcel(Parcel dest, int flags) {
905            // Write the current node id.
906            dest.writeLong(mId);
907 
908            // Serialize the full hierarchy from the root.
909            getHierarchyRoot().writeNodeContentsRecursive(dest);
910        }
911 
912        @VisibleForTesting
913        public BookmarkNode getHierarchyRoot() {
914            BookmarkNode root = this;
915            while (root.parent() != null) {
916                root = root.parent();
917            }
918            return root;
919        }
920 
921        private void writeNodeContentsRecursive(Parcel dest) {
922            writeNodeContents(dest);
923            dest.writeInt(mChildren.size());
924            for (BookmarkNode child : mChildren) {
925                child.writeNodeContentsRecursive(dest);
926            }
927        }
928 
929        private void writeNodeContents(Parcel dest) {
930            dest.writeLong(mId);
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);
937        }
938 
939        public static final Creator<BookmarkNode> CREATOR = new Creator<BookmarkNode>() {
940            private HashMap<Long, BookmarkNode> mNodeMap;
941 
942            @Override
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);
948                mNodeMap.clear();
949                return node;
950            }
951 
952            @Override
953            public BookmarkNode[] newArray(int size) {
954                return new BookmarkNode[size];
955            }
956 
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);
962                    return null;
963                }
964                return mNodeMap.get(nodeId);
965            }
966 
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.");
977                    return null;
978                }
979 
980                BookmarkNode node = new BookmarkNode(id, Type.values()[type], name, url,
981                        getNode(parentId));
982                node.setFavicon(favicon);
983                node.setThumbnail(thumbnail);
984                return node;
985            }
986 
987            private BookmarkNode readNodeContentsRecursive(Parcel source) {
988                BookmarkNode node = readNodeContents(source);
989                if (node == null) return null;
990 
991                Long nodeId = Long.valueOf(node.id());
992                if (mNodeMap.containsKey(nodeId)) {
993                    Log.e(TAG, "Invalid BookmarkNode hierarchy. Duplicate id " + node.id());
994                    return null;
995                }
996                mNodeMap.put(nodeId, node);
997 
998                int numChildren = source.readInt();
999                for (int i = 0; i < numChildren; ++i) {
1000                    node.addChild(readNodeContentsRecursive(source));
1001                }
1002 
1003                return node;
1004            }
1005        };
1006    }
1007 
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");
1012        }
1013        return nativeAddBookmarkFromAPI(mNativeChromeBrowserProvider,
1014                row.url, row.created, row.isBookmark, row.date, row.favicon,
1015                row.title, row.visits, row.parentId);
1016    }
1017 
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;
1023        } else {
1024            projection = projectionIn;
1025        }
1026 
1027        return nativeQueryBookmarkFromAPI(mNativeChromeBrowserProvider, projection, selection,
1028                selectionArgs, sortOrder);
1029    }
1030 
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);
1037    }
1038 
1039    private int removeBookmarkFromAPI(String selection, String[] selectionArgs) {
1040        return nativeRemoveBookmarkFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs);
1041    }
1042 
1043    private int removeHistoryFromAPI(String selection, String[] selectionArgs) {
1044        return nativeRemoveHistoryFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs);
1045    }
1046 
1047    @CalledByNative
1048    private void onBookmarkChanged() {
1049        getContext().getContentResolver().notifyChange(
1050                buildAPIContentUri(getContext(), BOOKMARKS_PATH), null);
1051    }
1052 
1053    @CalledByNative
1054    private void onSearchTermChanged() {
1055        getContext().getContentResolver().notifyChange(
1056                buildAPIContentUri(getContext(), SEARCHES_PATH), null);
1057    }
1058 
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");
1063        }
1064        return nativeAddSearchTermFromAPI(mNativeChromeBrowserProvider, row.term, row.date);
1065    }
1066 
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);
1072    }
1073 
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;
1079        } else {
1080            projection = projectionIn;
1081        }
1082        return nativeQuerySearchTermFromAPI(mNativeChromeBrowserProvider, projection, selection,
1083                selectionArgs, sortOrder);
1084    }
1085 
1086    private int removeSearchFromAPI(String selection, String[] selectionArgs) {
1087        return nativeRemoveSearchTermFromAPI(mNativeChromeBrowserProvider,
1088                selection, selectionArgs);
1089    }
1090 
1091    private static boolean isInUiThread() {
1092        if (!ThreadUtils.runningOnUiThread()) return false;
1093 
1094        if (!"REL".equals(Build.VERSION.CODENAME)) {
1095            throw new IllegalStateException("Shouldn't run in the UI thread");
1096        }
1097 
1098        Log.w(TAG, "ChromeBrowserProvider methods cannot be called from the UI thread.");
1099        return true;
1100    }
1101 
1102    private static Uri buildContentUri(String authority, String path) {
1103        return Uri.parse("content://" + authority + "/" + path);
1104    }
1105 
1106    private static Uri buildAPIContentUri(Context context, String path) {
1107        return buildContentUri(context.getPackageName() + API_AUTHORITY_SUFFIX, path);
1108    }
1109 
1110    private static String buildWhereClause(long id, String selection) {
1111        StringBuffer sb = new StringBuffer();
1112        sb.append(BaseColumns._ID);
1113        sb.append(" = ");
1114        sb.append(id);
1115        if (!TextUtils.isEmpty(selection)) {
1116            sb.append(" AND (");
1117            sb.append(selection);
1118            sb.append(")");
1119        }
1120        return sb.toString();
1121    }
1122 
1123    private static String buildHistoryWhereClause(long id, String selection) {
1124        return buildWhereClause(id, buildBookmarkWhereClause(selection, false));
1125    }
1126 
1127    private static String buildHistoryWhereClause(String selection) {
1128        return buildBookmarkWhereClause(selection, false);
1129    }
1130 
1131    /**
1132     * @return a SQL where class which is inserted the bookmark condition.
1133     */
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);
1141            sb.append(")");
1142        }
1143        return sb.toString();
1144    }
1145 
1146    private static String buildBookmarkWhereClause(long id, String selection) {
1147        return buildWhereClause(id, buildBookmarkWhereClause(selection, true));
1148    }
1149 
1150    private static String buildBookmarkWhereClause(String selection) {
1151        return buildBookmarkWhereClause(selection, true);
1152    }
1153 
1154    // Wrap the value of BookmarkColumn.
1155    private static class BookmarkRow {
1156        Boolean isBookmark;
1157        Long created;
1158        String url;
1159        Long date;
1160        byte[] favicon;
1161        String title;
1162        Integer visits;
1163        long parentId;
1164 
1165        static BookmarkRow fromContentValues(ContentValues values) {
1166            BookmarkRow row = new BookmarkRow();
1167            if (values.containsKey(BookmarkColumns.URL)) {
1168                row.url = values.getAsString(BookmarkColumns.URL);
1169            }
1170            if (values.containsKey(BookmarkColumns.BOOKMARK)) {
1171                row.isBookmark = values.getAsInteger(BookmarkColumns.BOOKMARK) != 0;
1172            }
1173            if (values.containsKey(BookmarkColumns.CREATED)) {
1174                row.created = values.getAsLong(BookmarkColumns.CREATED);
1175            }
1176            if (values.containsKey(BookmarkColumns.DATE)) {
1177                row.date = values.getAsLong(BookmarkColumns.DATE);
1178            }
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];
1184                }
1185            }
1186            if (values.containsKey(BookmarkColumns.TITLE)) {
1187                row.title = values.getAsString(BookmarkColumns.TITLE);
1188            }
1189            if (values.containsKey(BookmarkColumns.VISITS)) {
1190                row.visits = values.getAsInteger(BookmarkColumns.VISITS);
1191            }
1192            if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
1193                row.parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
1194            }
1195            return row;
1196        }
1197    }
1198 
1199    // Wrap the value of SearchColumn.
1200    private static class SearchRow {
1201        String term;
1202        Long date;
1203 
1204        static SearchRow fromContentValues(ContentValues values) {
1205            SearchRow row = new SearchRow();
1206            if (values.containsKey(SearchColumns.SEARCH)) {
1207                row.term = values.getAsString(SearchColumns.SEARCH);
1208            }
1209            if (values.containsKey(SearchColumns.DATE)) {
1210                row.date = values.getAsLong(SearchColumns.DATE);
1211            }
1212            return row;
1213        }
1214    }
1215 
1216    /**
1217     * Returns true if the native side of the class is initialized.
1218     */
1219    protected boolean isNativeSideInitialized() {
1220        return mNativeChromeBrowserProvider != 0;
1221    }
1222 
1223    /**
1224     * Make sure chrome is running. This method mustn't run on UI thread.
1225     *
1226     * @return Whether the native chrome process is running successfully once this has returned.
1227     */
1228    private boolean ensureNativeChromeLoaded() {
1229        ensureUriMatcherInitialized();
1230 
1231        synchronized(mLoadNativeLock) {
1232            if (mNativeChromeBrowserProvider != 0) return true;
1233 
1234            final AtomicBoolean retVal = new AtomicBoolean(true);
1235            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
1236                @Override
1237                public void run() {
1238                    retVal.set(ensureNativeChromeLoadedOnUIThread());
1239                }
1240            });
1241            return retVal.get();
1242        }
1243    }
1244 
1245    /**
1246     * This method should only run on UI thread.
1247     */
1248    protected boolean ensureNativeChromeLoadedOnUIThread() {
1249        if (isNativeSideInitialized()) return true;
1250        mNativeChromeBrowserProvider = nativeInit();
1251        return isNativeSideInitialized();
1252    }
1253 
1254    @Override
1255    protected void finalize() throws Throwable {
1256        try {
1257            // Tests might try to destroy this in the wrong thread.
1258            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
1259                @Override
1260                public void run() {
1261                    ensureNativeChromeDestroyedOnUIThread();
1262                }
1263            });
1264        } finally {
1265            super.finalize();
1266        }
1267    }
1268 
1269    /**
1270     * This method should only run on UI thread.
1271     */
1272    private void ensureNativeChromeDestroyedOnUIThread() {
1273        if (isNativeSideInitialized()) {
1274            nativeDestroy(mNativeChromeBrowserProvider);
1275            mNativeChromeBrowserProvider = 0;
1276        }
1277    }
1278 
1279    /**
1280     * Call to get the intent to create a bookmark shortcut on homescreen.
1281     */
1282    public static Intent getShortcutToBookmark(String url, String title, Bitmap favicon, int rValue,
1283            int gValue, int bValue, Activity activity) {
1284        return BookmarkUtils.createAddToHomeIntent(activity, url, title, favicon, rValue, gValue,
1285                bValue);
1286    }
1287 
1288    private native int nativeInit();
1289    private native void nativeDestroy(int nativeChromeBrowserProvider);
1290 
1291    // Public API native methods.
1292    private native long nativeAddBookmark(int nativeChromeBrowserProvider,
1293            String url, String title, boolean isFolder, long parentId);
1294 
1295    private native int nativeRemoveBookmark(int nativeChromeBrowserProvider, long id);
1296 
1297    private native int nativeUpdateBookmark(int nativeChromeBrowserProvider,
1298            long id, String url, String title, long parentId);
1299 
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);
1303 
1304    private native SQLiteCursor nativeQueryBookmarkFromAPI(int nativeChromeBrowserProvider,
1305            String[] projection, String selection, String[] selectionArgs, String sortOrder);
1306 
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);
1310 
1311    private native int nativeRemoveBookmarkFromAPI(int nativeChromeBrowserProvider,
1312            String selection, String[] selectionArgs);
1313 
1314    private native int nativeRemoveHistoryFromAPI(int nativeChromeBrowserProvider,
1315            String selection, String[] selectionArgs);
1316 
1317    private native long nativeAddSearchTermFromAPI(int nativeChromeBrowserProvider,
1318            String term, Long date);
1319 
1320    private native SQLiteCursor nativeQuerySearchTermFromAPI(int nativeChromeBrowserProvider,
1321            String[] projection, String selection, String[] selectionArgs, String sortOrder);
1322 
1323    private native int nativeUpdateSearchTermFromAPI(int nativeChromeBrowserProvider,
1324            String search, Long date, String selection, String[] selectionArgs);
1325 
1326    private native int nativeRemoveSearchTermFromAPI(int nativeChromeBrowserProvider,
1327            String selection, String[] selectionArgs);
1328 
1329    // Client API native methods.
1330    private native boolean nativeBookmarkNodeExists(int nativeChromeBrowserProvider, long id);
1331 
1332    private native long nativeCreateBookmarksFolderOnce(int nativeChromeBrowserProvider,
1333            String title, long parentId);
1334 
1335    private native BookmarkNode nativeGetAllBookmarkFolders(int nativeChromeBrowserProvider);
1336 
1337    private native void nativeRemoveAllBookmarks(int nativeChromeBrowserProvider);
1338 
1339    private native BookmarkNode nativeGetBookmarkNode(int nativeChromeBrowserProvider,
1340            long id, boolean getParent, boolean getChildren);
1341 
1342    private native BookmarkNode nativeGetMobileBookmarksFolder(int nativeChromeBrowserProvider);
1343 
1344    private native boolean nativeIsBookmarkInMobileBookmarksBranch(int nativeChromeBrowserProvider,
1345            long id);
1346 
1347    private native byte[] nativeGetFaviconOrTouchIcon(int nativeChromeBrowserProvider, String url);
1348 
1349    private native byte[] nativeGetThumbnail(int nativeChromeBrowserProvider, String url);
1350}

[all classes][org.chromium.chrome.browser]
EMMA 2.0.5312 (C) Vladimir Roubtsov