BulkCursorToCursorAdaptor.java revision 7cd51efcbd2d083bf577696591ef1769034f7e2f
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.Bundle;
20import android.os.RemoteException;
21import android.util.Log;
22
23/**
24 * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local
25 * process.
26 *
27 * {@hide}
28 */
29public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
30    private static final String TAG = "BulkCursor";
31
32    private SelfContentObserver mObserverBridge;
33    private IBulkCursor mBulkCursor;
34    private int mCount;
35    private String[] mColumns;
36    private boolean mWantsAllOnMoveCalls;
37
38    public void set(IBulkCursor bulkCursor) {
39        mBulkCursor = bulkCursor;
40
41        try {
42            mCount = mBulkCursor.count();
43            mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls();
44
45            // Search for the rowID column index and set it for our parent
46            mColumns = mBulkCursor.getColumnNames();
47            mRowIdColumnIndex = findRowIdColumnIndex(mColumns);
48        } catch (RemoteException ex) {
49            Log.e(TAG, "Setup failed because the remote process is dead");
50        }
51    }
52
53    /**
54     * Version of set() that does fewer Binder calls if the caller
55     * already knows BulkCursorToCursorAdaptor's properties.
56     */
57    public void set(IBulkCursor bulkCursor, int count, int idIndex) {
58        mBulkCursor = bulkCursor;
59        mColumns = null;  // lazily retrieved
60        mCount = count;
61        mRowIdColumnIndex = idIndex;
62    }
63
64    /**
65     * Returns column index of "_id" column, or -1 if not found.
66     */
67    public static int findRowIdColumnIndex(String[] columnNames) {
68        int length = columnNames.length;
69        for (int i = 0; i < length; i++) {
70            if (columnNames[i].equals("_id")) {
71                return i;
72            }
73        }
74        return -1;
75    }
76
77    /**
78     * Gets a SelfDataChangeOberserver that can be sent to a remote
79     * process to receive change notifications over IPC.
80     *
81     * @return A SelfContentObserver hooked up to this Cursor
82     */
83    public synchronized IContentObserver getObserver() {
84        if (mObserverBridge == null) {
85            mObserverBridge = new SelfContentObserver(this);
86        }
87        return mObserverBridge.getContentObserver();
88    }
89
90    @Override
91    public int getCount() {
92        return mCount;
93    }
94
95    @Override
96    public boolean onMove(int oldPosition, int newPosition) {
97        try {
98            // Make sure we have the proper window
99            if (mWindow != null) {
100                if (newPosition < mWindow.getStartPosition() ||
101                        newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
102                    mWindow = mBulkCursor.getWindow(newPosition);
103                } else if (mWantsAllOnMoveCalls) {
104                    mBulkCursor.onMove(newPosition);
105                }
106            } else {
107                mWindow = mBulkCursor.getWindow(newPosition);
108            }
109        } catch (RemoteException ex) {
110            // We tried to get a window and failed
111            Log.e(TAG, "Unable to get window because the remote process is dead");
112            return false;
113        }
114
115        // Couldn't obtain a window, something is wrong
116        if (mWindow == null) {
117            return false;
118        }
119
120        return true;
121    }
122
123    @Override
124    public void deactivate() {
125        // This will call onInvalidated(), so make sure to do it before calling release,
126        // which is what actually makes the data set invalid.
127        super.deactivate();
128
129        try {
130            mBulkCursor.deactivate();
131        } catch (RemoteException ex) {
132            Log.w(TAG, "Remote process exception when deactivating");
133        }
134        mWindow = null;
135    }
136
137    @Override
138    public void close() {
139        super.close();
140        try {
141            mBulkCursor.close();
142        } catch (RemoteException ex) {
143            Log.w(TAG, "Remote process exception when closing");
144        }
145        mWindow = null;
146    }
147
148    @Override
149    public boolean requery() {
150        try {
151            int oldCount = mCount;
152            //TODO get the window from a pool somewhere to avoid creating the memory dealer
153            mCount = mBulkCursor.requery(getObserver(), new CursorWindow(
154                    false /* the window will be accessed across processes */));
155            if (mCount != -1) {
156                mPos = -1;
157                mWindow = null;
158
159                // super.requery() will call onChanged. Do it here instead of relying on the
160                // observer from the far side so that observers can see a correct value for mCount
161                // when responding to onChanged.
162                super.requery();
163                return true;
164            } else {
165                deactivate();
166                return false;
167            }
168        } catch (Exception ex) {
169            Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage());
170            deactivate();
171            return false;
172        }
173    }
174
175    @Override
176    public String[] getColumnNames() {
177        if (mColumns == null) {
178            try {
179                mColumns = mBulkCursor.getColumnNames();
180            } catch (RemoteException ex) {
181                Log.e(TAG, "Unable to fetch column names because the remote process is dead");
182                return null;
183            }
184        }
185        return mColumns;
186    }
187
188    @Override
189    public Bundle getExtras() {
190        try {
191            return mBulkCursor.getExtras();
192        } catch (RemoteException e) {
193            // This should never happen because the system kills processes that are using remote
194            // cursors when the provider process is killed.
195            throw new RuntimeException(e);
196        }
197    }
198
199    @Override
200    public Bundle respond(Bundle extras) {
201        try {
202            return mBulkCursor.respond(extras);
203        } catch (RemoteException e) {
204            // the system kills processes that are using remote cursors when the provider process
205            // is killed, but this can still happen if this is being called from the system process,
206            // so, better to log and return an empty bundle.
207            Log.w(TAG, "respond() threw RemoteException, returning an empty bundle.", e);
208            return Bundle.EMPTY;
209        }
210    }
211}
212