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