1/*
2 * Copyright (C) 2009 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.database.Cursor;
20import android.net.Uri;
21import android.os.Bundle;
22import android.os.CancellationSignal;
23import android.os.DeadObjectException;
24import android.os.ICancellationSignal;
25import android.os.RemoteException;
26import android.os.ParcelFileDescriptor;
27import android.content.res.AssetFileDescriptor;
28
29import java.io.FileNotFoundException;
30import java.util.ArrayList;
31
32/**
33 * The public interface object used to interact with a {@link ContentProvider}. This is obtained by
34 * calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
35 * using {@link #release} in order to indicate to the system that the {@link ContentProvider} is
36 * no longer needed and can be killed to free up resources.
37 *
38 * <p>Note that you should generally create a new ContentProviderClient instance
39 * for each thread that will be performing operations.  Unlike
40 * {@link ContentResolver}, the methods here such as {@link #query} and
41 * {@link #openFile} are not thread safe -- you must not call
42 * {@link #release()} on the ContentProviderClient those calls are made from
43 * until you are finished with the data they have returned.
44 */
45public class ContentProviderClient {
46    private final IContentProvider mContentProvider;
47    private final ContentResolver mContentResolver;
48    private final boolean mStable;
49    private boolean mReleased;
50
51    /**
52     * @hide
53     */
54    ContentProviderClient(ContentResolver contentResolver,
55            IContentProvider contentProvider, boolean stable) {
56        mContentProvider = contentProvider;
57        mContentResolver = contentResolver;
58        mStable = stable;
59    }
60
61    /** See {@link ContentProvider#query ContentProvider.query} */
62    public Cursor query(Uri url, String[] projection, String selection,
63            String[] selectionArgs, String sortOrder) throws RemoteException {
64        try {
65            return query(url, projection, selection,  selectionArgs, sortOrder, null);
66        } catch (DeadObjectException e) {
67            if (!mStable) {
68                mContentResolver.unstableProviderDied(mContentProvider);
69            }
70            throw e;
71        }
72    }
73
74    /** See {@link ContentProvider#query ContentProvider.query} */
75    public Cursor query(Uri url, String[] projection, String selection,
76            String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal)
77                    throws RemoteException {
78        ICancellationSignal remoteCancellationSignal = null;
79        if (cancellationSignal != null) {
80            remoteCancellationSignal = mContentProvider.createCancellationSignal();
81            cancellationSignal.setRemote(remoteCancellationSignal);
82        }
83        try {
84            return mContentProvider.query(url, projection, selection,  selectionArgs, sortOrder,
85                    remoteCancellationSignal);
86        } catch (DeadObjectException e) {
87            if (!mStable) {
88                mContentResolver.unstableProviderDied(mContentProvider);
89            }
90            throw e;
91        }
92    }
93
94    /** See {@link ContentProvider#getType ContentProvider.getType} */
95    public String getType(Uri url) throws RemoteException {
96        try {
97            return mContentProvider.getType(url);
98        } catch (DeadObjectException e) {
99            if (!mStable) {
100                mContentResolver.unstableProviderDied(mContentProvider);
101            }
102            throw e;
103        }
104    }
105
106    /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
107    public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
108        try {
109            return mContentProvider.getStreamTypes(url, mimeTypeFilter);
110        } catch (DeadObjectException e) {
111            if (!mStable) {
112                mContentResolver.unstableProviderDied(mContentProvider);
113            }
114            throw e;
115        }
116    }
117
118    /** See {@link ContentProvider#insert ContentProvider.insert} */
119    public Uri insert(Uri url, ContentValues initialValues)
120            throws RemoteException {
121        try {
122            return mContentProvider.insert(url, initialValues);
123        } catch (DeadObjectException e) {
124            if (!mStable) {
125                mContentResolver.unstableProviderDied(mContentProvider);
126            }
127            throw e;
128        }
129    }
130
131    /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
132    public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
133        try {
134            return mContentProvider.bulkInsert(url, initialValues);
135        } catch (DeadObjectException e) {
136            if (!mStable) {
137                mContentResolver.unstableProviderDied(mContentProvider);
138            }
139            throw e;
140        }
141    }
142
143    /** See {@link ContentProvider#delete ContentProvider.delete} */
144    public int delete(Uri url, String selection, String[] selectionArgs)
145            throws RemoteException {
146        try {
147            return mContentProvider.delete(url, selection, selectionArgs);
148        } catch (DeadObjectException e) {
149            if (!mStable) {
150                mContentResolver.unstableProviderDied(mContentProvider);
151            }
152            throw e;
153        }
154    }
155
156    /** See {@link ContentProvider#update ContentProvider.update} */
157    public int update(Uri url, ContentValues values, String selection,
158            String[] selectionArgs) throws RemoteException {
159        try {
160            return mContentProvider.update(url, values, selection, selectionArgs);
161        } catch (DeadObjectException e) {
162            if (!mStable) {
163                mContentResolver.unstableProviderDied(mContentProvider);
164            }
165            throw e;
166        }
167    }
168
169    /**
170     * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
171     * this <em>does not</em>
172     * take care of non-content: URIs such as file:.  It is strongly recommended
173     * you use the {@link ContentResolver#openFileDescriptor
174     * ContentResolver.openFileDescriptor} API instead.
175     */
176    public ParcelFileDescriptor openFile(Uri url, String mode)
177            throws RemoteException, FileNotFoundException {
178        try {
179            return mContentProvider.openFile(url, mode);
180        } catch (DeadObjectException e) {
181            if (!mStable) {
182                mContentResolver.unstableProviderDied(mContentProvider);
183            }
184            throw e;
185        }
186    }
187
188    /**
189     * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
190     * Note that this <em>does not</em>
191     * take care of non-content: URIs such as file:.  It is strongly recommended
192     * you use the {@link ContentResolver#openAssetFileDescriptor
193     * ContentResolver.openAssetFileDescriptor} API instead.
194     */
195    public AssetFileDescriptor openAssetFile(Uri url, String mode)
196            throws RemoteException, FileNotFoundException {
197        try {
198            return mContentProvider.openAssetFile(url, mode);
199        } catch (DeadObjectException e) {
200            if (!mStable) {
201                mContentResolver.unstableProviderDied(mContentProvider);
202            }
203            throw e;
204        }
205    }
206
207    /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
208    public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
209            String mimeType, Bundle opts)
210            throws RemoteException, FileNotFoundException {
211        try {
212            return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
213        } catch (DeadObjectException e) {
214            if (!mStable) {
215                mContentResolver.unstableProviderDied(mContentProvider);
216            }
217            throw e;
218        }
219    }
220
221    /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
222    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
223            throws RemoteException, OperationApplicationException {
224        try {
225            return mContentProvider.applyBatch(operations);
226        } catch (DeadObjectException e) {
227            if (!mStable) {
228                mContentResolver.unstableProviderDied(mContentProvider);
229            }
230            throw e;
231        }
232    }
233
234    /** See {@link ContentProvider#call(String, String, Bundle)} */
235    public Bundle call(String method, String arg, Bundle extras)
236            throws RemoteException {
237        try {
238            return mContentProvider.call(method, arg, extras);
239        } catch (DeadObjectException e) {
240            if (!mStable) {
241                mContentResolver.unstableProviderDied(mContentProvider);
242            }
243            throw e;
244        }
245    }
246
247    /**
248     * Call this to indicate to the system that the associated {@link ContentProvider} is no
249     * longer needed by this {@link ContentProviderClient}.
250     * @return true if this was release, false if it was already released
251     */
252    public boolean release() {
253        synchronized (this) {
254            if (mReleased) {
255                throw new IllegalStateException("Already released");
256            }
257            mReleased = true;
258            if (mStable) {
259                return mContentResolver.releaseProvider(mContentProvider);
260            } else {
261                return mContentResolver.releaseUnstableProvider(mContentProvider);
262            }
263        }
264    }
265
266    /**
267     * Get a reference to the {@link ContentProvider} that is associated with this
268     * client. If the {@link ContentProvider} is running in a different process then
269     * null will be returned. This can be used if you know you are running in the same
270     * process as a provider, and want to get direct access to its implementation details.
271     *
272     * @return If the associated {@link ContentProvider} is local, returns it.
273     * Otherwise returns null.
274     */
275    public ContentProvider getLocalContentProvider() {
276        return ContentProvider.coerceToLocalContentProvider(mContentProvider);
277    }
278}
279