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