ContentProviderNative.java revision 9ffdfa0c238fce3b85741d7f6828fd484cd8f195
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        } catch (Exception e) {
262            DatabaseUtils.writeExceptionToParcel(reply, e);
263            return true;
264        }
265
266        return super.onTransact(code, data, reply, flags);
267    }
268
269    public IBinder asBinder()
270    {
271        return this;
272    }
273}
274
275
276final class ContentProviderProxy implements IContentProvider
277{
278    public ContentProviderProxy(IBinder remote)
279    {
280        mRemote = remote;
281    }
282
283    public IBinder asBinder()
284    {
285        return mRemote;
286    }
287
288    // Like bulkQuery() but sets up provided 'adaptor' if not null.
289    private IBulkCursor bulkQueryInternal(
290        Uri url, String[] projection,
291        String selection, String[] selectionArgs, String sortOrder,
292        IContentObserver observer, CursorWindow window,
293        BulkCursorToCursorAdaptor adaptor) throws RemoteException {
294        Parcel data = Parcel.obtain();
295        Parcel reply = Parcel.obtain();
296
297        data.writeInterfaceToken(IContentProvider.descriptor);
298
299        url.writeToParcel(data, 0);
300        int length = 0;
301        if (projection != null) {
302            length = projection.length;
303        }
304        data.writeInt(length);
305        for (int i = 0; i < length; i++) {
306            data.writeString(projection[i]);
307        }
308        data.writeString(selection);
309        if (selectionArgs != null) {
310            length = selectionArgs.length;
311        } else {
312            length = 0;
313        }
314        data.writeInt(length);
315        for (int i = 0; i < length; i++) {
316            data.writeString(selectionArgs[i]);
317        }
318        data.writeString(sortOrder);
319        data.writeStrongBinder(observer.asBinder());
320        window.writeToParcel(data, 0);
321
322        // Flag for whether or not we want the number of rows in the
323        // cursor and the position of the "_id" column index (or -1 if
324        // non-existent).  Only to be returned if binder != null.
325        final boolean wantsCursorMetadata = (adaptor != null);
326        data.writeInt(wantsCursorMetadata ? 1 : 0);
327
328        mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
329
330        DatabaseUtils.readExceptionFromParcel(reply);
331
332        IBulkCursor bulkCursor = null;
333        IBinder bulkCursorBinder = reply.readStrongBinder();
334        if (bulkCursorBinder != null) {
335            bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);
336
337            if (wantsCursorMetadata) {
338                int rowCount = reply.readInt();
339                int idColumnPosition = reply.readInt();
340                if (bulkCursor != null) {
341                    adaptor.set(bulkCursor, rowCount, idColumnPosition);
342                }
343            }
344        }
345
346        data.recycle();
347        reply.recycle();
348
349        return bulkCursor;
350    }
351
352    public IBulkCursor bulkQuery(Uri url, String[] projection,
353            String selection, String[] selectionArgs, String sortOrder, IContentObserver observer,
354            CursorWindow window) throws RemoteException {
355        return bulkQueryInternal(
356            url, projection, selection, selectionArgs, sortOrder,
357            observer, window,
358            null /* BulkCursorToCursorAdaptor */);
359    }
360
361    public Cursor query(Uri url, String[] projection, String selection,
362            String[] selectionArgs, String sortOrder) throws RemoteException {
363        //TODO make a pool of windows so we can reuse memory dealers
364        CursorWindow window = new CursorWindow(false /* window will be used remotely */);
365        BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
366        IBulkCursor bulkCursor = bulkQueryInternal(
367            url, projection, selection, selectionArgs, sortOrder,
368            adaptor.getObserver(), window,
369            adaptor);
370        if (bulkCursor == null) {
371            return null;
372        }
373        return adaptor;
374    }
375
376    public String getType(Uri url) throws RemoteException
377    {
378        Parcel data = Parcel.obtain();
379        Parcel reply = Parcel.obtain();
380
381        data.writeInterfaceToken(IContentProvider.descriptor);
382
383        url.writeToParcel(data, 0);
384
385        mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0);
386
387        DatabaseUtils.readExceptionFromParcel(reply);
388        String out = reply.readString();
389
390        data.recycle();
391        reply.recycle();
392
393        return out;
394    }
395
396    public Uri insert(Uri url, ContentValues values) throws RemoteException
397    {
398        Parcel data = Parcel.obtain();
399        Parcel reply = Parcel.obtain();
400
401        data.writeInterfaceToken(IContentProvider.descriptor);
402
403        url.writeToParcel(data, 0);
404        values.writeToParcel(data, 0);
405
406        mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0);
407
408        DatabaseUtils.readExceptionFromParcel(reply);
409        Uri out = Uri.CREATOR.createFromParcel(reply);
410
411        data.recycle();
412        reply.recycle();
413
414        return out;
415    }
416
417    public int bulkInsert(Uri url, ContentValues[] values) throws RemoteException {
418        Parcel data = Parcel.obtain();
419        Parcel reply = Parcel.obtain();
420
421        data.writeInterfaceToken(IContentProvider.descriptor);
422
423        url.writeToParcel(data, 0);
424        data.writeTypedArray(values, 0);
425
426        mRemote.transact(IContentProvider.BULK_INSERT_TRANSACTION, data, reply, 0);
427
428        DatabaseUtils.readExceptionFromParcel(reply);
429        int count = reply.readInt();
430
431        data.recycle();
432        reply.recycle();
433
434        return count;
435    }
436
437    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
438            throws RemoteException, OperationApplicationException {
439        Parcel data = Parcel.obtain();
440        Parcel reply = Parcel.obtain();
441
442        data.writeInterfaceToken(IContentProvider.descriptor);
443        data.writeInt(operations.size());
444        for (ContentProviderOperation operation : operations) {
445            operation.writeToParcel(data, 0);
446        }
447        mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0);
448
449        DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply);
450        final ContentProviderResult[] results =
451                reply.createTypedArray(ContentProviderResult.CREATOR);
452
453        data.recycle();
454        reply.recycle();
455
456        return results;
457    }
458
459    public int delete(Uri url, String selection, String[] selectionArgs)
460            throws RemoteException {
461        Parcel data = Parcel.obtain();
462        Parcel reply = Parcel.obtain();
463
464        data.writeInterfaceToken(IContentProvider.descriptor);
465
466        url.writeToParcel(data, 0);
467        data.writeString(selection);
468        data.writeStringArray(selectionArgs);
469
470        mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0);
471
472        DatabaseUtils.readExceptionFromParcel(reply);
473        int count = reply.readInt();
474
475        data.recycle();
476        reply.recycle();
477
478        return count;
479    }
480
481    public int update(Uri url, ContentValues values, String selection,
482            String[] selectionArgs) throws RemoteException {
483        Parcel data = Parcel.obtain();
484        Parcel reply = Parcel.obtain();
485
486        data.writeInterfaceToken(IContentProvider.descriptor);
487
488        url.writeToParcel(data, 0);
489        values.writeToParcel(data, 0);
490        data.writeString(selection);
491        data.writeStringArray(selectionArgs);
492
493        mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0);
494
495        DatabaseUtils.readExceptionFromParcel(reply);
496        int count = reply.readInt();
497
498        data.recycle();
499        reply.recycle();
500
501        return count;
502    }
503
504    public ParcelFileDescriptor openFile(Uri url, String mode)
505            throws RemoteException, FileNotFoundException {
506        Parcel data = Parcel.obtain();
507        Parcel reply = Parcel.obtain();
508
509        data.writeInterfaceToken(IContentProvider.descriptor);
510
511        url.writeToParcel(data, 0);
512        data.writeString(mode);
513
514        mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
515
516        DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
517        int has = reply.readInt();
518        ParcelFileDescriptor fd = has != 0 ? reply.readFileDescriptor() : null;
519
520        data.recycle();
521        reply.recycle();
522
523        return fd;
524    }
525
526    public AssetFileDescriptor openAssetFile(Uri url, String mode)
527            throws RemoteException, FileNotFoundException {
528        Parcel data = Parcel.obtain();
529        Parcel reply = Parcel.obtain();
530
531        data.writeInterfaceToken(IContentProvider.descriptor);
532
533        url.writeToParcel(data, 0);
534        data.writeString(mode);
535
536        mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0);
537
538        DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
539        int has = reply.readInt();
540        AssetFileDescriptor fd = has != 0
541                ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null;
542
543        data.recycle();
544        reply.recycle();
545
546        return fd;
547    }
548
549    public Bundle call(String method, String request, Bundle args)
550            throws RemoteException {
551        Parcel data = Parcel.obtain();
552        Parcel reply = Parcel.obtain();
553
554        data.writeInterfaceToken(IContentProvider.descriptor);
555
556        data.writeString(method);
557        data.writeString(request);
558        data.writeBundle(args);
559
560        mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0);
561
562        DatabaseUtils.readExceptionFromParcel(reply);
563        Bundle bundle = reply.readBundle();
564
565        data.recycle();
566        reply.recycle();
567
568        return bundle;
569    }
570
571    private IBinder mRemote;
572}
573