BulkCursorToCursorAdaptor.java revision 6dc0ef005d31f1aaf277164e8bc79be9068e8bf2
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.database;
18
19import android.os.RemoteException;
20import android.os.Bundle;
21import android.util.Log;
22
23import java.util.Map;
24
25/**
26 * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local
27 * process.
28 *
29 * {@hide}
30 */
31public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
32    private static final String TAG = "BulkCursor";
33
34    private SelfContentObserver mObserverBridge;
35    private IBulkCursor mBulkCursor;
36    private int mCount;
37    private String[] mColumns;
38    private boolean mWantsAllOnMoveCalls;
39
40    public void set(IBulkCursor bulkCursor) {
41        mBulkCursor = bulkCursor;
42
43        try {
44            mCount = mBulkCursor.count();
45            mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls();
46
47            // Search for the rowID column index and set it for our parent
48            mColumns = mBulkCursor.getColumnNames();
49            int length = mColumns.length;
50            for (int i = 0; i < length; i++) {
51                if (mColumns[i].equals("_id")) {
52                    mRowIdColumnIndex = i;
53                    break;
54                }
55            }
56        } catch (RemoteException ex) {
57            Log.e(TAG, "Setup failed because the remote process is dead");
58        }
59    }
60
61    /**
62     * Gets a SelfDataChangeOberserver that can be sent to a remote
63     * process to receive change notifications over IPC.
64     *
65     * @return A SelfContentObserver hooked up to this Cursor
66     */
67    public synchronized IContentObserver getObserver() {
68        if (mObserverBridge == null) {
69            mObserverBridge = new SelfContentObserver(this);
70        }
71        return mObserverBridge.getContentObserver();
72    }
73
74    @Override
75    public int getCount() {
76        return mCount;
77    }
78
79    @Override
80    public boolean onMove(int oldPosition, int newPosition) {
81        try {
82            // Make sure we have the proper window
83            if (mWindow != null) {
84                if (newPosition < mWindow.getStartPosition() ||
85                        newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
86                    mWindow = mBulkCursor.getWindow(newPosition);
87                } else if (mWantsAllOnMoveCalls) {
88                    mBulkCursor.onMove(newPosition);
89                }
90            } else {
91                mWindow = mBulkCursor.getWindow(newPosition);
92            }
93        } catch (RemoteException ex) {
94            // We tried to get a window and failed
95            Log.e(TAG, "Unable to get window because the remote process is dead");
96            return false;
97        }
98
99        // Couldn't obtain a window, something is wrong
100        if (mWindow == null) {
101            return false;
102        }
103
104        return true;
105    }
106
107    @Override
108    public void deactivate() {
109        // This will call onInvalidated(), so make sure to do it before calling release,
110        // which is what actually makes the data set invalid.
111        super.deactivate();
112
113        try {
114            mBulkCursor.deactivate();
115        } catch (RemoteException ex) {
116            Log.w(TAG, "Remote process exception when deactivating");
117        }
118        mWindow = null;
119    }
120
121    @Override
122    public void close() {
123        super.close();
124        try {
125            mBulkCursor.close();
126        } catch (RemoteException ex) {
127            Log.w(TAG, "Remote process exception when closing");
128        }
129        mWindow = null;
130    }
131
132    @Override
133    public boolean requery() {
134        try {
135            int oldCount = mCount;
136            //TODO get the window from a pool somewhere to avoid creating the memory dealer
137            mCount = mBulkCursor.requery(getObserver(), new CursorWindow(
138                    false /* the window will be accessed across processes */));
139            if (mCount != -1) {
140                mPos = -1;
141                mWindow = null;
142
143                // super.requery() will call onChanged. Do it here instead of relying on the
144                // observer from the far side so that observers can see a correct value for mCount
145                // when responding to onChanged.
146                super.requery();
147                return true;
148            } else {
149                deactivate();
150                return false;
151            }
152        } catch (Exception ex) {
153            Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage());
154            deactivate();
155            return false;
156        }
157    }
158
159    /**
160     * @hide
161     * @deprecated
162     */
163    @Override
164    public boolean deleteRow() {
165        try {
166            boolean result = mBulkCursor.deleteRow(mPos);
167            if (result != false) {
168                // The window contains the old value, discard it
169                mWindow = null;
170
171                // Fix up the position
172                mCount = mBulkCursor.count();
173                if (mPos < mCount) {
174                    int oldPos = mPos;
175                    mPos = -1;
176                    moveToPosition(oldPos);
177                } else {
178                    mPos = mCount;
179                }
180
181                // Send the change notification
182                onChange(true);
183            }
184            return result;
185        } catch (RemoteException ex) {
186            Log.e(TAG, "Unable to delete row because the remote process is dead");
187            return false;
188        }
189    }
190
191    @Override
192    public String[] getColumnNames() {
193        return mColumns;
194    }
195
196    /**
197     * @hide
198     * @deprecated
199     */
200    @Override
201    public boolean commitUpdates(Map<? extends Long,
202            ? extends Map<String,Object>> additionalValues) {
203        if (!supportsUpdates()) {
204            Log.e(TAG, "commitUpdates not supported on this cursor, did you include the _id column?");
205            return false;
206        }
207
208        synchronized(mUpdatedRows) {
209            if (additionalValues != null) {
210                mUpdatedRows.putAll(additionalValues);
211            }
212
213            if (mUpdatedRows.size() <= 0) {
214                return false;
215            }
216
217            try {
218                boolean result = mBulkCursor.updateRows(mUpdatedRows);
219
220                if (result == true) {
221                    mUpdatedRows.clear();
222
223                    // Send the change notification
224                    onChange(true);
225                }
226                return result;
227            } catch (RemoteException ex) {
228                Log.e(TAG, "Unable to commit updates because the remote process is dead");
229                return false;
230            }
231        }
232    }
233
234    @Override
235    public Bundle getExtras() {
236        try {
237            return mBulkCursor.getExtras();
238        } catch (RemoteException e) {
239            // This should never happen because the system kills processes that are using remote
240            // cursors when the provider process is killed.
241            throw new RuntimeException(e);
242        }
243    }
244
245    @Override
246    public Bundle respond(Bundle extras) {
247        try {
248            return mBulkCursor.respond(extras);
249        } catch (RemoteException e) {
250            // the system kills processes that are using remote cursors when the provider process
251            // is killed, but this can still happen if this is being called from the system process,
252            // so, better to log and return an empty bundle.
253            Log.w(TAG, "respond() threw RemoteException, returning an empty bundle.", e);
254            return Bundle.EMPTY;
255        }
256    }
257}
258
259