CursorToBulkCursorAdaptor.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.IBinder;
21import android.os.RemoteException;
22import android.util.Config;
23import android.util.Log;
24
25
26/**
27 * Wraps a BulkCursor around an existing Cursor making it remotable.
28 *
29 * {@hide}
30 */
31public final class CursorToBulkCursorAdaptor extends BulkCursorNative
32        implements IBinder.DeathRecipient {
33    private static final String TAG = "Cursor";
34    private final CrossProcessCursor mCursor;
35    private CursorWindow mWindow;
36    private final String mProviderName;
37    private ContentObserverProxy mObserver;
38
39    private static final class ContentObserverProxy extends ContentObserver
40            {
41        protected IContentObserver mRemote;
42
43        public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) {
44            super(null);
45            mRemote = remoteObserver;
46            try {
47                remoteObserver.asBinder().linkToDeath(recipient, 0);
48            } catch (RemoteException e) {
49                // Do nothing, the far side is dead
50            }
51        }
52
53        public boolean unlinkToDeath(DeathRecipient recipient) {
54            return mRemote.asBinder().unlinkToDeath(recipient, 0);
55        }
56
57        @Override
58        public boolean deliverSelfNotifications() {
59            // The far side handles the self notifications.
60            return false;
61        }
62
63        @Override
64        public void onChange(boolean selfChange) {
65            try {
66                mRemote.onChange(selfChange);
67            } catch (RemoteException ex) {
68                // Do nothing, the far side is dead
69            }
70        }
71    }
72
73    public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName,
74            boolean allowWrite, CursorWindow window) {
75        try {
76            mCursor = (CrossProcessCursor) cursor;
77            if (mCursor instanceof AbstractWindowedCursor) {
78                AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
79                if (windowedCursor.hasWindow()) {
80                    if (Log.isLoggable(TAG, Log.VERBOSE) || Config.LOGV) {
81                        Log.v(TAG, "Cross process cursor has a local window before setWindow in "
82                                + providerName, new RuntimeException());
83                    }
84                }
85                windowedCursor.setWindow(window);
86            } else {
87                mWindow = window;
88                mCursor.fillWindow(0, window);
89            }
90        } catch (ClassCastException e) {
91            // TODO Implement this case.
92            throw new UnsupportedOperationException(
93                    "Only CrossProcessCursor cursors are supported across process for now", e);
94        }
95        mProviderName = providerName;
96
97        createAndRegisterObserverProxy(observer);
98    }
99
100    public void binderDied() {
101        mCursor.close();
102        if (mWindow != null) {
103            mWindow.close();
104        }
105    }
106
107    public CursorWindow getWindow(int startPos) {
108        mCursor.moveToPosition(startPos);
109
110        if (mWindow != null) {
111            if (startPos < mWindow.getStartPosition() ||
112                    startPos >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
113                mCursor.fillWindow(startPos, mWindow);
114            }
115            return mWindow;
116        } else {
117            return ((AbstractWindowedCursor)mCursor).getWindow();
118        }
119    }
120
121    public void onMove(int position) {
122        mCursor.onMove(mCursor.getPosition(), position);
123    }
124
125    public int count() {
126        return mCursor.getCount();
127    }
128
129    public String[] getColumnNames() {
130        return mCursor.getColumnNames();
131    }
132
133    public void deactivate() {
134        maybeUnregisterObserverProxy();
135        mCursor.deactivate();
136    }
137
138    public void close() {
139        maybeUnregisterObserverProxy();
140        mCursor.close();
141    }
142
143    public int requery(IContentObserver observer, CursorWindow window) {
144        if (mWindow == null) {
145            ((AbstractWindowedCursor)mCursor).setWindow(window);
146        }
147        try {
148            if (!mCursor.requery()) {
149                return -1;
150            }
151        } catch (IllegalStateException e) {
152            IllegalStateException leakProgram = new IllegalStateException(
153                    mProviderName + " Requery misuse db, mCursor isClosed:" +
154                    mCursor.isClosed(), e);
155            throw leakProgram;
156        }
157
158        if (mWindow != null) {
159            mCursor.fillWindow(0, window);
160            mWindow = window;
161        }
162        maybeUnregisterObserverProxy();
163        createAndRegisterObserverProxy(observer);
164        return mCursor.getCount();
165    }
166
167    public boolean getWantsAllOnMoveCalls() {
168        return mCursor.getWantsAllOnMoveCalls();
169    }
170
171    /**
172     * Create a ContentObserver from the observer and register it as an observer on the
173     * underlying cursor.
174     * @param observer the IContentObserver that wants to monitor the cursor
175     * @throws IllegalStateException if an observer is already registered
176     */
177    private void createAndRegisterObserverProxy(IContentObserver observer) {
178        if (mObserver != null) {
179            throw new IllegalStateException("an observer is already registered");
180        }
181        mObserver = new ContentObserverProxy(observer, this);
182        mCursor.registerContentObserver(mObserver);
183    }
184
185    /** Unregister the observer if it is already registered. */
186    private void maybeUnregisterObserverProxy() {
187        if (mObserver != null) {
188            mCursor.unregisterContentObserver(mObserver);
189            mObserver.unlinkToDeath(this);
190            mObserver = null;
191        }
192    }
193
194    public Bundle getExtras() {
195        return mCursor.getExtras();
196    }
197
198    public Bundle respond(Bundle extras) {
199        return mCursor.respond(extras);
200    }
201}
202