1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.provider; 18 19 import android.annotation.SystemApi; 20 import android.content.ContentProvider; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.content.UriMatcher; 24 import android.content.pm.ProviderInfo; 25 import android.database.Cursor; 26 import android.net.Uri; 27 28 /** 29 * Base class for a search indexable provider. Such provider offers data to be indexed either 30 * as a reference to an XML file (like a {@link android.preference.PreferenceScreen}) or either 31 * as some raw data. 32 * 33 * @see SearchIndexableResource 34 * @see SearchIndexableData 35 * @see SearchIndexablesContract 36 * 37 * To create a search indexables provider, extend this class, then implement the abstract methods, 38 * and add it to your manifest like this: 39 * 40 * <pre class="prettyprint"><manifest> 41 * ... 42 * <application> 43 * ... 44 * <provider 45 * android:name="com.example.MyIndexablesProvider" 46 * android:authorities="com.example.myindexablesprovider" 47 * android:exported="true" 48 * android:grantUriPermissions="true" 49 * android:permission="android.permission.READ_SEARCH_INDEXABLES" 50 * <intent-filter> 51 * <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" /> 52 * </intent-filter> 53 * </provider> 54 * ... 55 * </application> 56 *</manifest></pre> 57 * <p> 58 * When defining your provider, you must protect it with 59 * {@link android.Manifest.permission#READ_SEARCH_INDEXABLES}, which is a permission only the system 60 * can obtain. 61 * </p> 62 * 63 * @hide 64 */ 65 @SystemApi 66 public abstract class SearchIndexablesProvider extends ContentProvider { 67 private static final String TAG = "IndexablesProvider"; 68 69 private String mAuthority; 70 private UriMatcher mMatcher; 71 72 private static final int MATCH_RES_CODE = 1; 73 private static final int MATCH_RAW_CODE = 2; 74 private static final int MATCH_NON_INDEXABLE_KEYS_CODE = 3; 75 private static final int MATCH_SITE_MAP_PAIRS_CODE = 4; 76 77 /** 78 * Implementation is provided by the parent class. 79 */ 80 @Override attachInfo(Context context, ProviderInfo info)81 public void attachInfo(Context context, ProviderInfo info) { 82 mAuthority = info.authority; 83 84 mMatcher = new UriMatcher(UriMatcher.NO_MATCH); 85 mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_XML_RES_PATH, 86 MATCH_RES_CODE); 87 mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_RAW_PATH, 88 MATCH_RAW_CODE); 89 mMatcher.addURI(mAuthority, SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH, 90 MATCH_NON_INDEXABLE_KEYS_CODE); 91 mMatcher.addURI(mAuthority, SearchIndexablesContract.SITE_MAP_PAIRS_PATH, 92 MATCH_SITE_MAP_PAIRS_CODE); 93 94 // Sanity check our setup 95 if (!info.exported) { 96 throw new SecurityException("Provider must be exported"); 97 } 98 if (!info.grantUriPermissions) { 99 throw new SecurityException("Provider must grantUriPermissions"); 100 } 101 if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(info.readPermission)) { 102 throw new SecurityException("Provider must be protected by READ_SEARCH_INDEXABLES"); 103 } 104 105 super.attachInfo(context, info); 106 } 107 108 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)109 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 110 String sortOrder) { 111 switch (mMatcher.match(uri)) { 112 case MATCH_RES_CODE: 113 return queryXmlResources(null); 114 case MATCH_RAW_CODE: 115 return queryRawData(null); 116 case MATCH_NON_INDEXABLE_KEYS_CODE: 117 return queryNonIndexableKeys(null); 118 case MATCH_SITE_MAP_PAIRS_CODE: 119 return querySiteMapPairs(); 120 default: 121 throw new UnsupportedOperationException("Unknown Uri " + uri); 122 } 123 } 124 125 /** 126 * Returns all {@link android.provider.SearchIndexablesContract.XmlResource}. 127 * 128 * Those are Xml resource IDs to some {@link android.preference.PreferenceScreen}. 129 * 130 * @param projection list of {@link android.provider.SearchIndexablesContract.XmlResource} 131 * columns to put into the cursor. If {@code null} all supported columns 132 * should be included. 133 */ queryXmlResources(String[] projection)134 public abstract Cursor queryXmlResources(String[] projection); 135 136 /** 137 * Returns all {@link android.provider.SearchIndexablesContract.RawData}. 138 * 139 * Those are the raw indexable data. 140 * 141 * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns 142 * to put into the cursor. If {@code null} all supported columns should be 143 * included. 144 */ queryRawData(String[] projection)145 public abstract Cursor queryRawData(String[] projection); 146 147 /** 148 * Returns all {@link android.provider.SearchIndexablesContract.NonIndexableKey}. 149 * 150 * Those are the non indexable data keys. 151 * 152 * @param projection list of {@link android.provider.SearchIndexablesContract.NonIndexableKey} 153 * columns to put into the cursor. If {@code null} all supported columns 154 * should be included. 155 */ queryNonIndexableKeys(String[] projection)156 public abstract Cursor queryNonIndexableKeys(String[] projection); 157 158 /** 159 * Returns a {@link Cursor}, where rows are [parent class, child class] entries to form a site 160 * map. The list of pairs should be as complete as possible. 161 * 162 * @hide 163 */ querySiteMapPairs()164 public Cursor querySiteMapPairs() { 165 // By default no-op. 166 return null; 167 } 168 169 @Override getType(Uri uri)170 public String getType(Uri uri) { 171 switch (mMatcher.match(uri)) { 172 case MATCH_RES_CODE: 173 return SearchIndexablesContract.XmlResource.MIME_TYPE; 174 case MATCH_RAW_CODE: 175 return SearchIndexablesContract.RawData.MIME_TYPE; 176 case MATCH_NON_INDEXABLE_KEYS_CODE: 177 return SearchIndexablesContract.NonIndexableKey.MIME_TYPE; 178 default: 179 throw new IllegalArgumentException("Unknown URI " + uri); 180 } 181 } 182 183 /** 184 * Implementation is provided by the parent class. Throws by default, and cannot be overriden. 185 */ 186 @Override insert(Uri uri, ContentValues values)187 public final Uri insert(Uri uri, ContentValues values) { 188 throw new UnsupportedOperationException("Insert not supported"); 189 } 190 191 /** 192 * Implementation is provided by the parent class. Throws by default, and cannot be overriden. 193 */ 194 @Override delete(Uri uri, String selection, String[] selectionArgs)195 public final int delete(Uri uri, String selection, String[] selectionArgs) { 196 throw new UnsupportedOperationException("Delete not supported"); 197 } 198 199 /** 200 * Implementation is provided by the parent class. Throws by default, and cannot be overriden. 201 */ 202 @Override update( Uri uri, ContentValues values, String selection, String[] selectionArgs)203 public final int update( 204 Uri uri, ContentValues values, String selection, String[] selectionArgs) { 205 throw new UnsupportedOperationException("Update not supported"); 206 } 207 } 208