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