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