ContentProviderNative.java revision eca53640a863b40ea9f96f280a90ce8aa538f9d1
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 17package android.content; 18 19import android.content.res.AssetFileDescriptor; 20import android.database.BulkCursorNative; 21import android.database.BulkCursorToCursorAdaptor; 22import android.database.Cursor; 23import android.database.CursorWindow; 24import android.database.DatabaseUtils; 25import android.database.IBulkCursor; 26import android.database.IContentObserver; 27import android.net.Uri; 28import android.os.Binder; 29import android.os.Bundle; 30import android.os.RemoteException; 31import android.os.IBinder; 32import android.os.Parcel; 33import android.os.ParcelFileDescriptor; 34import android.os.Parcelable; 35 36import java.io.FileNotFoundException; 37import java.util.ArrayList; 38 39/** 40 * {@hide} 41 */ 42abstract public class ContentProviderNative extends Binder implements IContentProvider { 43 private static final String TAG = "ContentProvider"; 44 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 */ 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 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 if (bulkCursor != null) { 114 final IBinder binder = bulkCursor.asBinder(); 115 if (wantsCursorMetadata) { 116 final int count = bulkCursor.count(); 117 final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex( 118 bulkCursor.getColumnNames()); 119 120 reply.writeNoException(); 121 reply.writeStrongBinder(binder); 122 reply.writeInt(count); 123 reply.writeInt(index); 124 } else { 125 reply.writeNoException(); 126 reply.writeStrongBinder(binder); 127 } 128 } else { 129 reply.writeNoException(); 130 reply.writeStrongBinder(null); 131 } 132 133 return true; 134 } 135 136 case GET_TYPE_TRANSACTION: 137 { 138 data.enforceInterface(IContentProvider.descriptor); 139 Uri url = Uri.CREATOR.createFromParcel(data); 140 String type = getType(url); 141 reply.writeNoException(); 142 reply.writeString(type); 143 144 return true; 145 } 146 147 case INSERT_TRANSACTION: 148 { 149 data.enforceInterface(IContentProvider.descriptor); 150 Uri url = Uri.CREATOR.createFromParcel(data); 151 ContentValues values = ContentValues.CREATOR.createFromParcel(data); 152 153 Uri out = insert(url, values); 154 reply.writeNoException(); 155 Uri.writeToParcel(reply, out); 156 return true; 157 } 158 159 case BULK_INSERT_TRANSACTION: 160 { 161 data.enforceInterface(IContentProvider.descriptor); 162 Uri url = Uri.CREATOR.createFromParcel(data); 163 ContentValues[] values = data.createTypedArray(ContentValues.CREATOR); 164 165 int count = bulkInsert(url, values); 166 reply.writeNoException(); 167 reply.writeInt(count); 168 return true; 169 } 170 171 case APPLY_BATCH_TRANSACTION: 172 { 173 data.enforceInterface(IContentProvider.descriptor); 174 final int numOperations = data.readInt(); 175 final ArrayList<ContentProviderOperation> operations = 176 new ArrayList<ContentProviderOperation>(numOperations); 177 for (int i = 0; i < numOperations; i++) { 178 operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data)); 179 } 180 final ContentProviderResult[] results = applyBatch(operations); 181 reply.writeNoException(); 182 reply.writeTypedArray(results, 0); 183 return true; 184 } 185 186 case DELETE_TRANSACTION: 187 { 188 data.enforceInterface(IContentProvider.descriptor); 189 Uri url = Uri.CREATOR.createFromParcel(data); 190 String selection = data.readString(); 191 String[] selectionArgs = data.readStringArray(); 192 193 int count = delete(url, selection, selectionArgs); 194 195 reply.writeNoException(); 196 reply.writeInt(count); 197 return true; 198 } 199 200 case UPDATE_TRANSACTION: 201 { 202 data.enforceInterface(IContentProvider.descriptor); 203 Uri url = Uri.CREATOR.createFromParcel(data); 204 ContentValues values = ContentValues.CREATOR.createFromParcel(data); 205 String selection = data.readString(); 206 String[] selectionArgs = data.readStringArray(); 207 208 int count = update(url, values, selection, selectionArgs); 209 210 reply.writeNoException(); 211 reply.writeInt(count); 212 return true; 213 } 214 215 case OPEN_FILE_TRANSACTION: 216 { 217 data.enforceInterface(IContentProvider.descriptor); 218 Uri url = Uri.CREATOR.createFromParcel(data); 219 String mode = data.readString(); 220 221 ParcelFileDescriptor fd; 222 fd = openFile(url, mode); 223 reply.writeNoException(); 224 if (fd != null) { 225 reply.writeInt(1); 226 fd.writeToParcel(reply, 227 Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 228 } else { 229 reply.writeInt(0); 230 } 231 return true; 232 } 233 234 case OPEN_ASSET_FILE_TRANSACTION: 235 { 236 data.enforceInterface(IContentProvider.descriptor); 237 Uri url = Uri.CREATOR.createFromParcel(data); 238 String mode = data.readString(); 239 240 AssetFileDescriptor fd; 241 fd = openAssetFile(url, mode); 242 reply.writeNoException(); 243 if (fd != null) { 244 reply.writeInt(1); 245 fd.writeToParcel(reply, 246 Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 247 } else { 248 reply.writeInt(0); 249 } 250 return true; 251 } 252 253 case CALL_TRANSACTION: 254 { 255 data.enforceInterface(IContentProvider.descriptor); 256 257 String method = data.readString(); 258 String stringArg = data.readString(); 259 Bundle args = data.readBundle(); 260 261 Bundle responseBundle = call(method, stringArg, args); 262 263 reply.writeNoException(); 264 reply.writeBundle(responseBundle); 265 return true; 266 } 267 268 case GET_STREAM_TYPES_TRANSACTION: 269 { 270 data.enforceInterface(IContentProvider.descriptor); 271 Uri url = Uri.CREATOR.createFromParcel(data); 272 String mimeTypeFilter = data.readString(); 273 String[] types = getStreamTypes(url, mimeTypeFilter); 274 reply.writeNoException(); 275 reply.writeStringArray(types); 276 277 return true; 278 } 279 280 case OPEN_TYPED_ASSET_FILE_TRANSACTION: 281 { 282 data.enforceInterface(IContentProvider.descriptor); 283 Uri url = Uri.CREATOR.createFromParcel(data); 284 String mimeType = data.readString(); 285 Bundle opts = data.readBundle(); 286 287 AssetFileDescriptor fd; 288 fd = openTypedAssetFile(url, mimeType, opts); 289 reply.writeNoException(); 290 if (fd != null) { 291 reply.writeInt(1); 292 fd.writeToParcel(reply, 293 Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 294 } else { 295 reply.writeInt(0); 296 } 297 return true; 298 } 299 } 300 } catch (Exception e) { 301 DatabaseUtils.writeExceptionToParcel(reply, e); 302 return true; 303 } 304 305 return super.onTransact(code, data, reply, flags); 306 } 307 308 public IBinder asBinder() 309 { 310 return this; 311 } 312} 313 314 315final class ContentProviderProxy implements IContentProvider 316{ 317 public ContentProviderProxy(IBinder remote) 318 { 319 mRemote = remote; 320 } 321 322 public IBinder asBinder() 323 { 324 return mRemote; 325 } 326 327 // Like bulkQuery() but sets up provided 'adaptor' if not null. 328 private IBulkCursor bulkQueryInternal( 329 Uri url, String[] projection, 330 String selection, String[] selectionArgs, String sortOrder, 331 IContentObserver observer, CursorWindow window, 332 BulkCursorToCursorAdaptor adaptor) throws RemoteException { 333 Parcel data = Parcel.obtain(); 334 Parcel reply = Parcel.obtain(); 335 336 data.writeInterfaceToken(IContentProvider.descriptor); 337 338 url.writeToParcel(data, 0); 339 int length = 0; 340 if (projection != null) { 341 length = projection.length; 342 } 343 data.writeInt(length); 344 for (int i = 0; i < length; i++) { 345 data.writeString(projection[i]); 346 } 347 data.writeString(selection); 348 if (selectionArgs != null) { 349 length = selectionArgs.length; 350 } else { 351 length = 0; 352 } 353 data.writeInt(length); 354 for (int i = 0; i < length; i++) { 355 data.writeString(selectionArgs[i]); 356 } 357 data.writeString(sortOrder); 358 data.writeStrongBinder(observer.asBinder()); 359 window.writeToParcel(data, 0); 360 361 // Flag for whether or not we want the number of rows in the 362 // cursor and the position of the "_id" column index (or -1 if 363 // non-existent). Only to be returned if binder != null. 364 final boolean wantsCursorMetadata = (adaptor != null); 365 data.writeInt(wantsCursorMetadata ? 1 : 0); 366 367 mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); 368 369 DatabaseUtils.readExceptionFromParcel(reply); 370 371 IBulkCursor bulkCursor = null; 372 IBinder bulkCursorBinder = reply.readStrongBinder(); 373 if (bulkCursorBinder != null) { 374 bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); 375 376 if (wantsCursorMetadata) { 377 int rowCount = reply.readInt(); 378 int idColumnPosition = reply.readInt(); 379 if (bulkCursor != null) { 380 adaptor.set(bulkCursor, rowCount, idColumnPosition); 381 } 382 } 383 } 384 385 data.recycle(); 386 reply.recycle(); 387 388 return bulkCursor; 389 } 390 391 public IBulkCursor bulkQuery(Uri url, String[] projection, 392 String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, 393 CursorWindow window) throws RemoteException { 394 return bulkQueryInternal( 395 url, projection, selection, selectionArgs, sortOrder, 396 observer, window, 397 null /* BulkCursorToCursorAdaptor */); 398 } 399 400 public Cursor query(Uri url, String[] projection, String selection, 401 String[] selectionArgs, String sortOrder) throws RemoteException { 402 //TODO make a pool of windows so we can reuse memory dealers 403 CursorWindow window = new CursorWindow(false /* window will be used remotely */); 404 BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); 405 IBulkCursor bulkCursor = bulkQueryInternal( 406 url, projection, selection, selectionArgs, sortOrder, 407 adaptor.getObserver(), window, 408 adaptor); 409 if (bulkCursor == null) { 410 return null; 411 } 412 return adaptor; 413 } 414 415 public String getType(Uri url) throws RemoteException 416 { 417 Parcel data = Parcel.obtain(); 418 Parcel reply = Parcel.obtain(); 419 420 data.writeInterfaceToken(IContentProvider.descriptor); 421 422 url.writeToParcel(data, 0); 423 424 mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0); 425 426 DatabaseUtils.readExceptionFromParcel(reply); 427 String out = reply.readString(); 428 429 data.recycle(); 430 reply.recycle(); 431 432 return out; 433 } 434 435 public Uri insert(Uri url, ContentValues values) throws RemoteException 436 { 437 Parcel data = Parcel.obtain(); 438 Parcel reply = Parcel.obtain(); 439 440 data.writeInterfaceToken(IContentProvider.descriptor); 441 442 url.writeToParcel(data, 0); 443 values.writeToParcel(data, 0); 444 445 mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0); 446 447 DatabaseUtils.readExceptionFromParcel(reply); 448 Uri out = Uri.CREATOR.createFromParcel(reply); 449 450 data.recycle(); 451 reply.recycle(); 452 453 return out; 454 } 455 456 public int bulkInsert(Uri url, ContentValues[] values) throws RemoteException { 457 Parcel data = Parcel.obtain(); 458 Parcel reply = Parcel.obtain(); 459 460 data.writeInterfaceToken(IContentProvider.descriptor); 461 462 url.writeToParcel(data, 0); 463 data.writeTypedArray(values, 0); 464 465 mRemote.transact(IContentProvider.BULK_INSERT_TRANSACTION, data, reply, 0); 466 467 DatabaseUtils.readExceptionFromParcel(reply); 468 int count = reply.readInt(); 469 470 data.recycle(); 471 reply.recycle(); 472 473 return count; 474 } 475 476 public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) 477 throws RemoteException, OperationApplicationException { 478 Parcel data = Parcel.obtain(); 479 Parcel reply = Parcel.obtain(); 480 481 data.writeInterfaceToken(IContentProvider.descriptor); 482 data.writeInt(operations.size()); 483 for (ContentProviderOperation operation : operations) { 484 operation.writeToParcel(data, 0); 485 } 486 mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0); 487 488 DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply); 489 final ContentProviderResult[] results = 490 reply.createTypedArray(ContentProviderResult.CREATOR); 491 492 data.recycle(); 493 reply.recycle(); 494 495 return results; 496 } 497 498 public int delete(Uri url, String selection, String[] selectionArgs) 499 throws RemoteException { 500 Parcel data = Parcel.obtain(); 501 Parcel reply = Parcel.obtain(); 502 503 data.writeInterfaceToken(IContentProvider.descriptor); 504 505 url.writeToParcel(data, 0); 506 data.writeString(selection); 507 data.writeStringArray(selectionArgs); 508 509 mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0); 510 511 DatabaseUtils.readExceptionFromParcel(reply); 512 int count = reply.readInt(); 513 514 data.recycle(); 515 reply.recycle(); 516 517 return count; 518 } 519 520 public int update(Uri url, ContentValues values, String selection, 521 String[] selectionArgs) throws RemoteException { 522 Parcel data = Parcel.obtain(); 523 Parcel reply = Parcel.obtain(); 524 525 data.writeInterfaceToken(IContentProvider.descriptor); 526 527 url.writeToParcel(data, 0); 528 values.writeToParcel(data, 0); 529 data.writeString(selection); 530 data.writeStringArray(selectionArgs); 531 532 mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0); 533 534 DatabaseUtils.readExceptionFromParcel(reply); 535 int count = reply.readInt(); 536 537 data.recycle(); 538 reply.recycle(); 539 540 return count; 541 } 542 543 public ParcelFileDescriptor openFile(Uri url, String mode) 544 throws RemoteException, FileNotFoundException { 545 Parcel data = Parcel.obtain(); 546 Parcel reply = Parcel.obtain(); 547 548 data.writeInterfaceToken(IContentProvider.descriptor); 549 550 url.writeToParcel(data, 0); 551 data.writeString(mode); 552 553 mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0); 554 555 DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); 556 int has = reply.readInt(); 557 ParcelFileDescriptor fd = has != 0 ? reply.readFileDescriptor() : null; 558 559 data.recycle(); 560 reply.recycle(); 561 562 return fd; 563 } 564 565 public AssetFileDescriptor openAssetFile(Uri url, String mode) 566 throws RemoteException, FileNotFoundException { 567 Parcel data = Parcel.obtain(); 568 Parcel reply = Parcel.obtain(); 569 570 data.writeInterfaceToken(IContentProvider.descriptor); 571 572 url.writeToParcel(data, 0); 573 data.writeString(mode); 574 575 mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0); 576 577 DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); 578 int has = reply.readInt(); 579 AssetFileDescriptor fd = has != 0 580 ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; 581 582 data.recycle(); 583 reply.recycle(); 584 585 return fd; 586 } 587 588 public Bundle call(String method, String request, Bundle args) 589 throws RemoteException { 590 Parcel data = Parcel.obtain(); 591 Parcel reply = Parcel.obtain(); 592 593 data.writeInterfaceToken(IContentProvider.descriptor); 594 595 data.writeString(method); 596 data.writeString(request); 597 data.writeBundle(args); 598 599 mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0); 600 601 DatabaseUtils.readExceptionFromParcel(reply); 602 Bundle bundle = reply.readBundle(); 603 604 data.recycle(); 605 reply.recycle(); 606 607 return bundle; 608 } 609 610 public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException 611 { 612 Parcel data = Parcel.obtain(); 613 Parcel reply = Parcel.obtain(); 614 615 data.writeInterfaceToken(IContentProvider.descriptor); 616 617 url.writeToParcel(data, 0); 618 data.writeString(mimeTypeFilter); 619 620 mRemote.transact(IContentProvider.GET_STREAM_TYPES_TRANSACTION, data, reply, 0); 621 622 DatabaseUtils.readExceptionFromParcel(reply); 623 String[] out = reply.createStringArray(); 624 625 data.recycle(); 626 reply.recycle(); 627 628 return out; 629 } 630 631 public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts) 632 throws RemoteException, FileNotFoundException { 633 Parcel data = Parcel.obtain(); 634 Parcel reply = Parcel.obtain(); 635 636 data.writeInterfaceToken(IContentProvider.descriptor); 637 638 url.writeToParcel(data, 0); 639 data.writeString(mimeType); 640 data.writeBundle(opts); 641 642 mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0); 643 644 DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); 645 int has = reply.readInt(); 646 AssetFileDescriptor fd = has != 0 647 ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; 648 649 data.recycle(); 650 reply.recycle(); 651 652 return fd; 653 } 654 655 private IBinder mRemote; 656} 657