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.BulkCursorDescriptor;
21import android.database.BulkCursorToCursorAdaptor;
22import android.database.Cursor;
23import android.database.CursorToBulkCursorAdaptor;
24import android.database.DatabaseUtils;
25import android.database.IContentObserver;
26import android.net.Uri;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.IBinder;
30import android.os.ICancellationSignal;
31import android.os.Parcel;
32import android.os.ParcelFileDescriptor;
33import android.os.Parcelable;
34import android.os.RemoteException;
35
36import java.io.FileNotFoundException;
37import java.util.ArrayList;
38
39/**
40 * {@hide}
41 */
42abstract 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
374final 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