1 /* 2 * Copyright (C) 2006 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.content; 18 19 import android.content.res.AssetFileDescriptor; 20 import android.database.BulkCursorNative; 21 import android.database.BulkCursorToCursorAdaptor; 22 import android.database.Cursor; 23 import android.database.CursorWindow; 24 import android.database.DatabaseUtils; 25 import android.database.IBulkCursor; 26 import android.database.IContentObserver; 27 import android.net.Uri; 28 import android.os.Binder; 29 import android.os.Bundle; 30 import android.os.RemoteException; 31 import android.os.IBinder; 32 import android.os.Parcel; 33 import android.os.ParcelFileDescriptor; 34 import android.os.Parcelable; 35 36 import java.io.FileNotFoundException; 37 import java.util.ArrayList; 38 39 /** 40 * {@hide} 41 */ 42 abstract public class ContentProviderNative extends Binder implements IContentProvider { 43 private static final String TAG = "ContentProvider"; 44 ContentProviderNative()45 public ContentProviderNative() 46 { 47 attachInterface(this, descriptor); 48 } 49 50 /** 51 * Cast a Binder object into a content resolver interface, generating 52 * a proxy if needed. 53 */ asInterface(IBinder obj)54 static public IContentProvider asInterface(IBinder obj) 55 { 56 if (obj == null) { 57 return null; 58 } 59 IContentProvider in = 60 (IContentProvider)obj.queryLocalInterface(descriptor); 61 if (in != null) { 62 return in; 63 } 64 65 return new ContentProviderProxy(obj); 66 } 67 68 @Override onTransact(int code, Parcel data, Parcel reply, int flags)69 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 70 throws RemoteException { 71 try { 72 switch (code) { 73 case QUERY_TRANSACTION: 74 { 75 data.enforceInterface(IContentProvider.descriptor); 76 77 Uri url = Uri.CREATOR.createFromParcel(data); 78 79 // String[] projection 80 int num = data.readInt(); 81 String[] projection = null; 82 if (num > 0) { 83 projection = new String[num]; 84 for (int i = 0; i < num; i++) { 85 projection[i] = data.readString(); 86 } 87 } 88 89 // String selection, String[] selectionArgs... 90 String selection = data.readString(); 91 num = data.readInt(); 92 String[] selectionArgs = null; 93 if (num > 0) { 94 selectionArgs = new String[num]; 95 for (int i = 0; i < num; i++) { 96 selectionArgs[i] = data.readString(); 97 } 98 } 99 100 String sortOrder = data.readString(); 101 IContentObserver observer = IContentObserver.Stub. 102 asInterface(data.readStrongBinder()); 103 CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); 104 105 // Flag for whether caller wants the number of 106 // rows in the cursor and the position of the 107 // "_id" column index (or -1 if non-existent) 108 // Only to be returned if binder != null. 109 boolean wantsCursorMetadata = data.readInt() != 0; 110 111 IBulkCursor bulkCursor = bulkQuery(url, projection, selection, 112 selectionArgs, sortOrder, observer, window); 113 reply.writeNoException(); 114 if (bulkCursor != null) { 115 reply.writeStrongBinder(bulkCursor.asBinder()); 116 117 if (wantsCursorMetadata) { 118 reply.writeInt(bulkCursor.count()); 119 reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex( 120 bulkCursor.getColumnNames())); 121 } 122 } else { 123 reply.writeStrongBinder(null); 124 } 125 126 return true; 127 } 128 129 case GET_TYPE_TRANSACTION: 130 { 131 data.enforceInterface(IContentProvider.descriptor); 132 Uri url = Uri.CREATOR.createFromParcel(data); 133 String type = getType(url); 134 reply.writeNoException(); 135 reply.writeString(type); 136 137 return true; 138 } 139 140 case INSERT_TRANSACTION: 141 { 142 data.enforceInterface(IContentProvider.descriptor); 143 Uri url = Uri.CREATOR.createFromParcel(data); 144 ContentValues values = ContentValues.CREATOR.createFromParcel(data); 145 146 Uri out = insert(url, values); 147 reply.writeNoException(); 148 Uri.writeToParcel(reply, out); 149 return true; 150 } 151 152 case BULK_INSERT_TRANSACTION: 153 { 154 data.enforceInterface(IContentProvider.descriptor); 155 Uri url = Uri.CREATOR.createFromParcel(data); 156 ContentValues[] values = data.createTypedArray(ContentValues.CREATOR); 157 158 int count = bulkInsert(url, values); 159 reply.writeNoException(); 160 reply.writeInt(count); 161 return true; 162 } 163 164 case APPLY_BATCH_TRANSACTION: 165 { 166 data.enforceInterface(IContentProvider.descriptor); 167 final int numOperations = data.readInt(); 168 final ArrayList<ContentProviderOperation> operations = 169 new ArrayList<ContentProviderOperation>(numOperations); 170 for (int i = 0; i < numOperations; i++) { 171 operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data)); 172 } 173 final ContentProviderResult[] results = applyBatch(operations); 174 reply.writeNoException(); 175 reply.writeTypedArray(results, 0); 176 return true; 177 } 178 179 case DELETE_TRANSACTION: 180 { 181 data.enforceInterface(IContentProvider.descriptor); 182 Uri url = Uri.CREATOR.createFromParcel(data); 183 String selection = data.readString(); 184 String[] selectionArgs = data.readStringArray(); 185 186 int count = delete(url, selection, selectionArgs); 187 188 reply.writeNoException(); 189 reply.writeInt(count); 190 return true; 191 } 192 193 case UPDATE_TRANSACTION: 194 { 195 data.enforceInterface(IContentProvider.descriptor); 196 Uri url = Uri.CREATOR.createFromParcel(data); 197 ContentValues values = ContentValues.CREATOR.createFromParcel(data); 198 String selection = data.readString(); 199 String[] selectionArgs = data.readStringArray(); 200 201 int count = update(url, values, selection, selectionArgs); 202 203 reply.writeNoException(); 204 reply.writeInt(count); 205 return true; 206 } 207 208 case OPEN_FILE_TRANSACTION: 209 { 210 data.enforceInterface(IContentProvider.descriptor); 211 Uri url = Uri.CREATOR.createFromParcel(data); 212 String mode = data.readString(); 213 214 ParcelFileDescriptor fd; 215 fd = openFile(url, mode); 216 reply.writeNoException(); 217 if (fd != null) { 218 reply.writeInt(1); 219 fd.writeToParcel(reply, 220 Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 221 } else { 222 reply.writeInt(0); 223 } 224 return true; 225 } 226 227 case OPEN_ASSET_FILE_TRANSACTION: 228 { 229 data.enforceInterface(IContentProvider.descriptor); 230 Uri url = Uri.CREATOR.createFromParcel(data); 231 String mode = data.readString(); 232 233 AssetFileDescriptor fd; 234 fd = openAssetFile(url, mode); 235 reply.writeNoException(); 236 if (fd != null) { 237 reply.writeInt(1); 238 fd.writeToParcel(reply, 239 Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 240 } else { 241 reply.writeInt(0); 242 } 243 return true; 244 } 245 246 case CALL_TRANSACTION: 247 { 248 data.enforceInterface(IContentProvider.descriptor); 249 250 String method = data.readString(); 251 String stringArg = data.readString(); 252 Bundle args = data.readBundle(); 253 254 Bundle responseBundle = call(method, stringArg, args); 255 256 reply.writeNoException(); 257 reply.writeBundle(responseBundle); 258 return true; 259 } 260 } 261 } catch (Exception e) { 262 DatabaseUtils.writeExceptionToParcel(reply, e); 263 return true; 264 } 265 266 return super.onTransact(code, data, reply, flags); 267 } 268 asBinder()269 public IBinder asBinder() 270 { 271 return this; 272 } 273 } 274 275 276 final class ContentProviderProxy implements IContentProvider 277 { ContentProviderProxy(IBinder remote)278 public ContentProviderProxy(IBinder remote) 279 { 280 mRemote = remote; 281 } 282 asBinder()283 public IBinder asBinder() 284 { 285 return mRemote; 286 } 287 288 // Like bulkQuery() but sets up provided 'adaptor' if not null. bulkQueryInternal( Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, CursorWindow window, BulkCursorToCursorAdaptor adaptor)289 private IBulkCursor bulkQueryInternal( 290 Uri url, String[] projection, 291 String selection, String[] selectionArgs, String sortOrder, 292 IContentObserver observer, CursorWindow window, 293 BulkCursorToCursorAdaptor adaptor) throws RemoteException { 294 Parcel data = Parcel.obtain(); 295 Parcel reply = Parcel.obtain(); 296 297 data.writeInterfaceToken(IContentProvider.descriptor); 298 299 url.writeToParcel(data, 0); 300 int length = 0; 301 if (projection != null) { 302 length = projection.length; 303 } 304 data.writeInt(length); 305 for (int i = 0; i < length; i++) { 306 data.writeString(projection[i]); 307 } 308 data.writeString(selection); 309 if (selectionArgs != null) { 310 length = selectionArgs.length; 311 } else { 312 length = 0; 313 } 314 data.writeInt(length); 315 for (int i = 0; i < length; i++) { 316 data.writeString(selectionArgs[i]); 317 } 318 data.writeString(sortOrder); 319 data.writeStrongBinder(observer.asBinder()); 320 window.writeToParcel(data, 0); 321 322 // Flag for whether or not we want the number of rows in the 323 // cursor and the position of the "_id" column index (or -1 if 324 // non-existent). Only to be returned if binder != null. 325 final boolean wantsCursorMetadata = (adaptor != null); 326 data.writeInt(wantsCursorMetadata ? 1 : 0); 327 328 mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); 329 330 DatabaseUtils.readExceptionFromParcel(reply); 331 332 IBulkCursor bulkCursor = null; 333 IBinder bulkCursorBinder = reply.readStrongBinder(); 334 if (bulkCursorBinder != null) { 335 bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); 336 337 if (wantsCursorMetadata) { 338 int rowCount = reply.readInt(); 339 int idColumnPosition = reply.readInt(); 340 if (bulkCursor != null) { 341 adaptor.set(bulkCursor, rowCount, idColumnPosition); 342 } 343 } 344 } 345 346 data.recycle(); 347 reply.recycle(); 348 349 return bulkCursor; 350 } 351 bulkQuery(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, CursorWindow window)352 public IBulkCursor bulkQuery(Uri url, String[] projection, 353 String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, 354 CursorWindow window) throws RemoteException { 355 return bulkQueryInternal( 356 url, projection, selection, selectionArgs, sortOrder, 357 observer, window, 358 null /* BulkCursorToCursorAdaptor */); 359 } 360 query(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder)361 public Cursor query(Uri url, String[] projection, String selection, 362 String[] selectionArgs, String sortOrder) throws RemoteException { 363 //TODO make a pool of windows so we can reuse memory dealers 364 CursorWindow window = new CursorWindow(false /* window will be used remotely */); 365 BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); 366 IBulkCursor bulkCursor = bulkQueryInternal( 367 url, projection, selection, selectionArgs, sortOrder, 368 adaptor.getObserver(), window, 369 adaptor); 370 if (bulkCursor == null) { 371 return null; 372 } 373 return adaptor; 374 } 375 getType(Uri url)376 public String getType(Uri url) throws RemoteException 377 { 378 Parcel data = Parcel.obtain(); 379 Parcel reply = Parcel.obtain(); 380 381 data.writeInterfaceToken(IContentProvider.descriptor); 382 383 url.writeToParcel(data, 0); 384 385 mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0); 386 387 DatabaseUtils.readExceptionFromParcel(reply); 388 String out = reply.readString(); 389 390 data.recycle(); 391 reply.recycle(); 392 393 return out; 394 } 395 insert(Uri url, ContentValues values)396 public Uri insert(Uri url, ContentValues values) throws RemoteException 397 { 398 Parcel data = Parcel.obtain(); 399 Parcel reply = Parcel.obtain(); 400 401 data.writeInterfaceToken(IContentProvider.descriptor); 402 403 url.writeToParcel(data, 0); 404 values.writeToParcel(data, 0); 405 406 mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0); 407 408 DatabaseUtils.readExceptionFromParcel(reply); 409 Uri out = Uri.CREATOR.createFromParcel(reply); 410 411 data.recycle(); 412 reply.recycle(); 413 414 return out; 415 } 416 bulkInsert(Uri url, ContentValues[] values)417 public int bulkInsert(Uri url, ContentValues[] values) throws RemoteException { 418 Parcel data = Parcel.obtain(); 419 Parcel reply = Parcel.obtain(); 420 421 data.writeInterfaceToken(IContentProvider.descriptor); 422 423 url.writeToParcel(data, 0); 424 data.writeTypedArray(values, 0); 425 426 mRemote.transact(IContentProvider.BULK_INSERT_TRANSACTION, data, reply, 0); 427 428 DatabaseUtils.readExceptionFromParcel(reply); 429 int count = reply.readInt(); 430 431 data.recycle(); 432 reply.recycle(); 433 434 return count; 435 } 436 applyBatch(ArrayList<ContentProviderOperation> operations)437 public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) 438 throws RemoteException, OperationApplicationException { 439 Parcel data = Parcel.obtain(); 440 Parcel reply = Parcel.obtain(); 441 442 data.writeInterfaceToken(IContentProvider.descriptor); 443 data.writeInt(operations.size()); 444 for (ContentProviderOperation operation : operations) { 445 operation.writeToParcel(data, 0); 446 } 447 mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0); 448 449 DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply); 450 final ContentProviderResult[] results = 451 reply.createTypedArray(ContentProviderResult.CREATOR); 452 453 data.recycle(); 454 reply.recycle(); 455 456 return results; 457 } 458 delete(Uri url, String selection, String[] selectionArgs)459 public int delete(Uri url, String selection, String[] selectionArgs) 460 throws RemoteException { 461 Parcel data = Parcel.obtain(); 462 Parcel reply = Parcel.obtain(); 463 464 data.writeInterfaceToken(IContentProvider.descriptor); 465 466 url.writeToParcel(data, 0); 467 data.writeString(selection); 468 data.writeStringArray(selectionArgs); 469 470 mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0); 471 472 DatabaseUtils.readExceptionFromParcel(reply); 473 int count = reply.readInt(); 474 475 data.recycle(); 476 reply.recycle(); 477 478 return count; 479 } 480 update(Uri url, ContentValues values, String selection, String[] selectionArgs)481 public int update(Uri url, ContentValues values, String selection, 482 String[] selectionArgs) throws RemoteException { 483 Parcel data = Parcel.obtain(); 484 Parcel reply = Parcel.obtain(); 485 486 data.writeInterfaceToken(IContentProvider.descriptor); 487 488 url.writeToParcel(data, 0); 489 values.writeToParcel(data, 0); 490 data.writeString(selection); 491 data.writeStringArray(selectionArgs); 492 493 mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0); 494 495 DatabaseUtils.readExceptionFromParcel(reply); 496 int count = reply.readInt(); 497 498 data.recycle(); 499 reply.recycle(); 500 501 return count; 502 } 503 openFile(Uri url, String mode)504 public ParcelFileDescriptor openFile(Uri url, String mode) 505 throws RemoteException, FileNotFoundException { 506 Parcel data = Parcel.obtain(); 507 Parcel reply = Parcel.obtain(); 508 509 data.writeInterfaceToken(IContentProvider.descriptor); 510 511 url.writeToParcel(data, 0); 512 data.writeString(mode); 513 514 mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0); 515 516 DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); 517 int has = reply.readInt(); 518 ParcelFileDescriptor fd = has != 0 ? reply.readFileDescriptor() : null; 519 520 data.recycle(); 521 reply.recycle(); 522 523 return fd; 524 } 525 openAssetFile(Uri url, String mode)526 public AssetFileDescriptor openAssetFile(Uri url, String mode) 527 throws RemoteException, FileNotFoundException { 528 Parcel data = Parcel.obtain(); 529 Parcel reply = Parcel.obtain(); 530 531 data.writeInterfaceToken(IContentProvider.descriptor); 532 533 url.writeToParcel(data, 0); 534 data.writeString(mode); 535 536 mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0); 537 538 DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); 539 int has = reply.readInt(); 540 AssetFileDescriptor fd = has != 0 541 ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; 542 543 data.recycle(); 544 reply.recycle(); 545 546 return fd; 547 } 548 call(String method, String request, Bundle args)549 public Bundle call(String method, String request, Bundle args) 550 throws RemoteException { 551 Parcel data = Parcel.obtain(); 552 Parcel reply = Parcel.obtain(); 553 554 data.writeInterfaceToken(IContentProvider.descriptor); 555 556 data.writeString(method); 557 data.writeString(request); 558 data.writeBundle(args); 559 560 mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0); 561 562 DatabaseUtils.readExceptionFromParcel(reply); 563 Bundle bundle = reply.readBundle(); 564 565 data.recycle(); 566 reply.recycle(); 567 568 return bundle; 569 } 570 571 private IBinder mRemote; 572 } 573