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