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                        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
366final 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