ContentProviderNative.java revision 23fdaf6fb62a9b5154b2508916a21c678462c5d0
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 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 case GET_STREAM_TYPES_TRANSACTION: 262 { 263 data.enforceInterface(IContentProvider.descriptor); 264 Uri url = Uri.CREATOR.createFromParcel(data); 265 String mimeTypeFilter = data.readString(); 266 String[] types = getStreamTypes(url, mimeTypeFilter); 267 reply.writeNoException(); 268 reply.writeStringArray(types); 269 270 return true; 271 } 272 273 case OPEN_TYPED_ASSET_FILE_TRANSACTION: 274 { 275 data.enforceInterface(IContentProvider.descriptor); 276 Uri url = Uri.CREATOR.createFromParcel(data); 277 String mimeType = data.readString(); 278 Bundle opts = data.readBundle(); 279 280 AssetFileDescriptor fd; 281 fd = openTypedAssetFile(url, mimeType, opts); 282 reply.writeNoException(); 283 if (fd != null) { 284 reply.writeInt(1); 285 fd.writeToParcel(reply, 286 Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 287 } else { 288 reply.writeInt(0); 289 } 290 return true; 291 } 292 } 293 } catch (Exception e) { 294 DatabaseUtils.writeExceptionToParcel(reply, e); 295 return true; 296 } 297 298 return super.onTransact(code, data, reply, flags); 299 } 300 301 public IBinder asBinder() 302 { 303 return this; 304 } 305} 306 307 308final class ContentProviderProxy implements IContentProvider 309{ 310 public ContentProviderProxy(IBinder remote) 311 { 312 mRemote = remote; 313 } 314 315 public IBinder asBinder() 316 { 317 return mRemote; 318 } 319 320 // Like bulkQuery() but sets up provided 'adaptor' if not null. 321 private IBulkCursor bulkQueryInternal( 322 Uri url, String[] projection, 323 String selection, String[] selectionArgs, String sortOrder, 324 IContentObserver observer, CursorWindow window, 325 BulkCursorToCursorAdaptor adaptor) throws RemoteException { 326 Parcel data = Parcel.obtain(); 327 Parcel reply = Parcel.obtain(); 328 329 data.writeInterfaceToken(IContentProvider.descriptor); 330 331 url.writeToParcel(data, 0); 332 int length = 0; 333 if (projection != null) { 334 length = projection.length; 335 } 336 data.writeInt(length); 337 for (int i = 0; i < length; i++) { 338 data.writeString(projection[i]); 339 } 340 data.writeString(selection); 341 if (selectionArgs != null) { 342 length = selectionArgs.length; 343 } else { 344 length = 0; 345 } 346 data.writeInt(length); 347 for (int i = 0; i < length; i++) { 348 data.writeString(selectionArgs[i]); 349 } 350 data.writeString(sortOrder); 351 data.writeStrongBinder(observer.asBinder()); 352 window.writeToParcel(data, 0); 353 354 // Flag for whether or not we want the number of rows in the 355 // cursor and the position of the "_id" column index (or -1 if 356 // non-existent). Only to be returned if binder != null. 357 final boolean wantsCursorMetadata = (adaptor != null); 358 data.writeInt(wantsCursorMetadata ? 1 : 0); 359 360 mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); 361 362 DatabaseUtils.readExceptionFromParcel(reply); 363 364 IBulkCursor bulkCursor = null; 365 IBinder bulkCursorBinder = reply.readStrongBinder(); 366 if (bulkCursorBinder != null) { 367 bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); 368 369 if (wantsCursorMetadata) { 370 int rowCount = reply.readInt(); 371 int idColumnPosition = reply.readInt(); 372 if (bulkCursor != null) { 373 adaptor.set(bulkCursor, rowCount, idColumnPosition); 374 } 375 } 376 } 377 378 data.recycle(); 379 reply.recycle(); 380 381 return bulkCursor; 382 } 383 384 public IBulkCursor bulkQuery(Uri url, String[] projection, 385 String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, 386 CursorWindow window) throws RemoteException { 387 return bulkQueryInternal( 388 url, projection, selection, selectionArgs, sortOrder, 389 observer, window, 390 null /* BulkCursorToCursorAdaptor */); 391 } 392 393 public Cursor query(Uri url, String[] projection, String selection, 394 String[] selectionArgs, String sortOrder) throws RemoteException { 395 //TODO make a pool of windows so we can reuse memory dealers 396 CursorWindow window = new CursorWindow(false /* window will be used remotely */); 397 BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); 398 IBulkCursor bulkCursor = bulkQueryInternal( 399 url, projection, selection, selectionArgs, sortOrder, 400 adaptor.getObserver(), window, 401 adaptor); 402 if (bulkCursor == null) { 403 return null; 404 } 405 return adaptor; 406 } 407 408 public String getType(Uri url) throws RemoteException 409 { 410 Parcel data = Parcel.obtain(); 411 Parcel reply = Parcel.obtain(); 412 413 data.writeInterfaceToken(IContentProvider.descriptor); 414 415 url.writeToParcel(data, 0); 416 417 mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0); 418 419 DatabaseUtils.readExceptionFromParcel(reply); 420 String out = reply.readString(); 421 422 data.recycle(); 423 reply.recycle(); 424 425 return out; 426 } 427 428 public Uri insert(Uri url, ContentValues values) throws RemoteException 429 { 430 Parcel data = Parcel.obtain(); 431 Parcel reply = Parcel.obtain(); 432 433 data.writeInterfaceToken(IContentProvider.descriptor); 434 435 url.writeToParcel(data, 0); 436 values.writeToParcel(data, 0); 437 438 mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0); 439 440 DatabaseUtils.readExceptionFromParcel(reply); 441 Uri out = Uri.CREATOR.createFromParcel(reply); 442 443 data.recycle(); 444 reply.recycle(); 445 446 return out; 447 } 448 449 public int bulkInsert(Uri url, ContentValues[] values) throws RemoteException { 450 Parcel data = Parcel.obtain(); 451 Parcel reply = Parcel.obtain(); 452 453 data.writeInterfaceToken(IContentProvider.descriptor); 454 455 url.writeToParcel(data, 0); 456 data.writeTypedArray(values, 0); 457 458 mRemote.transact(IContentProvider.BULK_INSERT_TRANSACTION, data, reply, 0); 459 460 DatabaseUtils.readExceptionFromParcel(reply); 461 int count = reply.readInt(); 462 463 data.recycle(); 464 reply.recycle(); 465 466 return count; 467 } 468 469 public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) 470 throws RemoteException, OperationApplicationException { 471 Parcel data = Parcel.obtain(); 472 Parcel reply = Parcel.obtain(); 473 474 data.writeInterfaceToken(IContentProvider.descriptor); 475 data.writeInt(operations.size()); 476 for (ContentProviderOperation operation : operations) { 477 operation.writeToParcel(data, 0); 478 } 479 mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0); 480 481 DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply); 482 final ContentProviderResult[] results = 483 reply.createTypedArray(ContentProviderResult.CREATOR); 484 485 data.recycle(); 486 reply.recycle(); 487 488 return results; 489 } 490 491 public int delete(Uri url, String selection, String[] selectionArgs) 492 throws RemoteException { 493 Parcel data = Parcel.obtain(); 494 Parcel reply = Parcel.obtain(); 495 496 data.writeInterfaceToken(IContentProvider.descriptor); 497 498 url.writeToParcel(data, 0); 499 data.writeString(selection); 500 data.writeStringArray(selectionArgs); 501 502 mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0); 503 504 DatabaseUtils.readExceptionFromParcel(reply); 505 int count = reply.readInt(); 506 507 data.recycle(); 508 reply.recycle(); 509 510 return count; 511 } 512 513 public int update(Uri url, ContentValues values, String selection, 514 String[] selectionArgs) throws RemoteException { 515 Parcel data = Parcel.obtain(); 516 Parcel reply = Parcel.obtain(); 517 518 data.writeInterfaceToken(IContentProvider.descriptor); 519 520 url.writeToParcel(data, 0); 521 values.writeToParcel(data, 0); 522 data.writeString(selection); 523 data.writeStringArray(selectionArgs); 524 525 mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0); 526 527 DatabaseUtils.readExceptionFromParcel(reply); 528 int count = reply.readInt(); 529 530 data.recycle(); 531 reply.recycle(); 532 533 return count; 534 } 535 536 public ParcelFileDescriptor openFile(Uri url, String mode) 537 throws RemoteException, FileNotFoundException { 538 Parcel data = Parcel.obtain(); 539 Parcel reply = Parcel.obtain(); 540 541 data.writeInterfaceToken(IContentProvider.descriptor); 542 543 url.writeToParcel(data, 0); 544 data.writeString(mode); 545 546 mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0); 547 548 DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); 549 int has = reply.readInt(); 550 ParcelFileDescriptor fd = has != 0 ? reply.readFileDescriptor() : null; 551 552 data.recycle(); 553 reply.recycle(); 554 555 return fd; 556 } 557 558 public AssetFileDescriptor openAssetFile(Uri url, String mode) 559 throws RemoteException, FileNotFoundException { 560 Parcel data = Parcel.obtain(); 561 Parcel reply = Parcel.obtain(); 562 563 data.writeInterfaceToken(IContentProvider.descriptor); 564 565 url.writeToParcel(data, 0); 566 data.writeString(mode); 567 568 mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0); 569 570 DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); 571 int has = reply.readInt(); 572 AssetFileDescriptor fd = has != 0 573 ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; 574 575 data.recycle(); 576 reply.recycle(); 577 578 return fd; 579 } 580 581 public Bundle call(String method, String request, Bundle args) 582 throws RemoteException { 583 Parcel data = Parcel.obtain(); 584 Parcel reply = Parcel.obtain(); 585 586 data.writeInterfaceToken(IContentProvider.descriptor); 587 588 data.writeString(method); 589 data.writeString(request); 590 data.writeBundle(args); 591 592 mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0); 593 594 DatabaseUtils.readExceptionFromParcel(reply); 595 Bundle bundle = reply.readBundle(); 596 597 data.recycle(); 598 reply.recycle(); 599 600 return bundle; 601 } 602 603 public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException 604 { 605 Parcel data = Parcel.obtain(); 606 Parcel reply = Parcel.obtain(); 607 608 data.writeInterfaceToken(IContentProvider.descriptor); 609 610 url.writeToParcel(data, 0); 611 data.writeString(mimeTypeFilter); 612 613 mRemote.transact(IContentProvider.GET_STREAM_TYPES_TRANSACTION, data, reply, 0); 614 615 DatabaseUtils.readExceptionFromParcel(reply); 616 String[] out = reply.createStringArray(); 617 618 data.recycle(); 619 reply.recycle(); 620 621 return out; 622 } 623 624 public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts) 625 throws RemoteException, FileNotFoundException { 626 Parcel data = Parcel.obtain(); 627 Parcel reply = Parcel.obtain(); 628 629 data.writeInterfaceToken(IContentProvider.descriptor); 630 631 url.writeToParcel(data, 0); 632 data.writeString(mimeType); 633 data.writeBundle(opts); 634 635 mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0); 636 637 DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); 638 int has = reply.readInt(); 639 AssetFileDescriptor fd = has != 0 640 ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; 641 642 data.recycle(); 643 reply.recycle(); 644 645 return fd; 646 } 647 648 private IBinder mRemote; 649} 650