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