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