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.Nullable; 20 import android.annotation.SystemApi; 21 import android.app.slice.Slice; 22 import android.content.ContentProvider; 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.content.UriMatcher; 26 import android.content.pm.ProviderInfo; 27 import android.database.Cursor; 28 import android.net.Uri; 29 import android.util.Log; 30 31 /** 32 * Base class for a search indexable provider. Such provider offers data to be indexed either 33 * as a reference to an XML file (like a {@link android.preference.PreferenceScreen}) or either 34 * as some raw data. 35 * 36 * @see SearchIndexableResource 37 * @see SearchIndexableData 38 * @see SearchIndexablesContract 39 * 40 * To create a search indexables provider, extend this class, then implement the abstract methods, 41 * and add it to your manifest like this: 42 * 43 * <pre class="prettyprint"><manifest> 44 * ... 45 * <application> 46 * ... 47 * <provider 48 * android:name="com.example.MyIndexablesProvider" 49 * android:authorities="com.example.myindexablesprovider" 50 * android:exported="true" 51 * android:grantUriPermissions="true" 52 * android:permission="android.permission.READ_SEARCH_INDEXABLES" 53 * <intent-filter> 54 * <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" /> 55 * </intent-filter> 56 * </provider> 57 * ... 58 * </application> 59 *</manifest></pre> 60 * <p> 61 * When defining your provider, you must protect it with 62 * {@link android.Manifest.permission#READ_SEARCH_INDEXABLES}, which is a permission only the system 63 * can obtain. 64 * </p> 65 * 66 * @hide 67 */ 68 @SystemApi 69 public abstract class SearchIndexablesProvider extends ContentProvider { 70 private static final String TAG = "IndexablesProvider"; 71 72 private String mAuthority; 73 private UriMatcher mMatcher; 74 75 private static final int MATCH_RES_CODE = 1; 76 private static final int MATCH_RAW_CODE = 2; 77 private static final int MATCH_NON_INDEXABLE_KEYS_CODE = 3; 78 private static final int MATCH_SITE_MAP_PAIRS_CODE = 4; 79 private static final int MATCH_SLICE_URI_PAIRS_CODE = 5; 80 81 /** 82 * Implementation is provided by the parent class. 83 */ 84 @Override attachInfo(Context context, ProviderInfo info)85 public void attachInfo(Context context, ProviderInfo info) { 86 mAuthority = info.authority; 87 88 mMatcher = new UriMatcher(UriMatcher.NO_MATCH); 89 mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_XML_RES_PATH, 90 MATCH_RES_CODE); 91 mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_RAW_PATH, 92 MATCH_RAW_CODE); 93 mMatcher.addURI(mAuthority, SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH, 94 MATCH_NON_INDEXABLE_KEYS_CODE); 95 mMatcher.addURI(mAuthority, SearchIndexablesContract.SITE_MAP_PAIRS_PATH, 96 MATCH_SITE_MAP_PAIRS_CODE); 97 mMatcher.addURI(mAuthority, SearchIndexablesContract.SLICE_URI_PAIRS_PATH, 98 MATCH_SLICE_URI_PAIRS_CODE); 99 100 // Sanity check our setup 101 if (!info.exported) { 102 throw new SecurityException("Provider must be exported"); 103 } 104 if (!info.grantUriPermissions) { 105 throw new SecurityException("Provider must grantUriPermissions"); 106 } 107 if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(info.readPermission)) { 108 throw new SecurityException("Provider must be protected by READ_SEARCH_INDEXABLES"); 109 } 110 111 super.attachInfo(context, info); 112 } 113 114 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)115 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 116 String sortOrder) { 117 try { 118 switch (mMatcher.match(uri)) { 119 case MATCH_RES_CODE: 120 return queryXmlResources(null); 121 case MATCH_RAW_CODE: 122 return queryRawData(null); 123 case MATCH_NON_INDEXABLE_KEYS_CODE: 124 return queryNonIndexableKeys(null); 125 case MATCH_SITE_MAP_PAIRS_CODE: 126 return querySiteMapPairs(); 127 case MATCH_SLICE_URI_PAIRS_CODE: 128 return querySliceUriPairs(); 129 default: 130 throw new UnsupportedOperationException("Unknown Uri " + uri); 131 } 132 } catch (UnsupportedOperationException e) { 133 throw e; 134 } catch (Exception e) { 135 Log.e(TAG, "Provider querying exception:", e); 136 return null; 137 } 138 } 139 140 /** 141 * Returns all {@link android.provider.SearchIndexablesContract.XmlResource}. 142 * 143 * Those are Xml resource IDs to some {@link android.preference.PreferenceScreen}. 144 * 145 * @param projection list of {@link android.provider.SearchIndexablesContract.XmlResource} 146 * columns to put into the cursor. If {@code null} all supported columns 147 * should be included. 148 */ queryXmlResources(String[] projection)149 public abstract Cursor queryXmlResources(String[] projection); 150 151 /** 152 * Returns all {@link android.provider.SearchIndexablesContract.RawData}. 153 * 154 * Those are the raw indexable data. 155 * 156 * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns 157 * to put into the cursor. If {@code null} all supported columns should be 158 * included. 159 */ queryRawData(String[] projection)160 public abstract Cursor queryRawData(String[] projection); 161 162 /** 163 * Returns all {@link android.provider.SearchIndexablesContract.NonIndexableKey}. 164 * 165 * Those are the non indexable data keys. 166 * 167 * @param projection list of {@link android.provider.SearchIndexablesContract.NonIndexableKey} 168 * columns to put into the cursor. If {@code null} all supported columns 169 * should be included. 170 */ queryNonIndexableKeys(String[] projection)171 public abstract Cursor queryNonIndexableKeys(String[] projection); 172 173 /** 174 * Returns a {@link Cursor}, where rows are [parent class, child class] entries to form a site 175 * map. The list of pairs should be as complete as possible. 176 * 177 * @hide 178 */ querySiteMapPairs()179 public Cursor querySiteMapPairs() { 180 // By default no-op. 181 return null; 182 } 183 184 /** 185 * Returns a {@link Cursor} linking {@link Slice} {@link Uri Uris} to the 186 * corresponding Settings key. 187 */ 188 @Nullable querySliceUriPairs()189 public Cursor querySliceUriPairs() { 190 // By default no-op; 191 return null; 192 } 193 194 @Override getType(Uri uri)195 public String getType(Uri uri) { 196 switch (mMatcher.match(uri)) { 197 case MATCH_RES_CODE: 198 return SearchIndexablesContract.XmlResource.MIME_TYPE; 199 case MATCH_RAW_CODE: 200 return SearchIndexablesContract.RawData.MIME_TYPE; 201 case MATCH_NON_INDEXABLE_KEYS_CODE: 202 return SearchIndexablesContract.NonIndexableKey.MIME_TYPE; 203 default: 204 throw new IllegalArgumentException("Unknown URI " + uri); 205 } 206 } 207 208 /** 209 * Implementation is provided by the parent class. Throws by default, and cannot be overridden. 210 */ 211 @Override insert(Uri uri, ContentValues values)212 public final Uri insert(Uri uri, ContentValues values) { 213 throw new UnsupportedOperationException("Insert not supported"); 214 } 215 216 /** 217 * Implementation is provided by the parent class. Throws by default, and cannot be overridden. 218 */ 219 @Override delete(Uri uri, String selection, String[] selectionArgs)220 public final int delete(Uri uri, String selection, String[] selectionArgs) { 221 throw new UnsupportedOperationException("Delete not supported"); 222 } 223 224 /** 225 * Implementation is provided by the parent class. Throws by default, and cannot be overridden. 226 */ 227 @Override update( Uri uri, ContentValues values, String selection, String[] selectionArgs)228 public final int update( 229 Uri uri, ContentValues values, String selection, String[] selectionArgs) { 230 throw new UnsupportedOperationException("Update not supported"); 231 } 232 } 233