| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.database; |
| |
| import android.os.Bundle; |
| import android.os.RemoteException; |
| import android.util.Log; |
| |
| /** |
| * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local process. |
| * |
| * {@hide} |
| */ |
| public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { |
| private static final String TAG = "BulkCursor"; |
| |
| private SelfContentObserver mObserverBridge = new SelfContentObserver(this); |
| private IBulkCursor mBulkCursor; |
| private String[] mColumns; |
| private boolean mWantsAllOnMoveCalls; |
| private int mCount; |
| |
| /** |
| * Initializes the adaptor. |
| * Must be called before first use. |
| */ |
| public void initialize(BulkCursorDescriptor d) { |
| mBulkCursor = d.cursor; |
| mColumns = d.columnNames; |
| mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls; |
| mCount = d.count; |
| if (d.window != null) { |
| setWindow(d.window); |
| } |
| } |
| |
| /** |
| * Gets a SelfDataChangeOberserver that can be sent to a remote |
| * process to receive change notifications over IPC. |
| * |
| * @return A SelfContentObserver hooked up to this Cursor |
| */ |
| public IContentObserver getObserver() { |
| return mObserverBridge.getContentObserver(); |
| } |
| |
| private void throwIfCursorIsClosed() { |
| if (mBulkCursor == null) { |
| throw new StaleDataException("Attempted to access a cursor after it has been closed."); |
| } |
| } |
| |
| @Override |
| public int getCount() { |
| throwIfCursorIsClosed(); |
| return mCount; |
| } |
| |
| @Override |
| public boolean onMove(int oldPosition, int newPosition) { |
| throwIfCursorIsClosed(); |
| |
| try { |
| // Make sure we have the proper window |
| if (mWindow == null |
| || newPosition < mWindow.getStartPosition() |
| || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) { |
| setWindow(mBulkCursor.getWindow(newPosition)); |
| } else if (mWantsAllOnMoveCalls) { |
| mBulkCursor.onMove(newPosition); |
| } |
| } catch (RemoteException ex) { |
| // We tried to get a window and failed |
| Log.e(TAG, "Unable to get window because the remote process is dead"); |
| return false; |
| } |
| |
| // Couldn't obtain a window, something is wrong |
| if (mWindow == null) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public void deactivate() { |
| // This will call onInvalidated(), so make sure to do it before calling release, |
| // which is what actually makes the data set invalid. |
| super.deactivate(); |
| |
| if (mBulkCursor != null) { |
| try { |
| mBulkCursor.deactivate(); |
| } catch (RemoteException ex) { |
| Log.w(TAG, "Remote process exception when deactivating"); |
| } |
| } |
| } |
| |
| @Override |
| public void close() { |
| super.close(); |
| |
| if (mBulkCursor != null) { |
| try { |
| mBulkCursor.close(); |
| } catch (RemoteException ex) { |
| Log.w(TAG, "Remote process exception when closing"); |
| } finally { |
| mBulkCursor = null; |
| } |
| } |
| } |
| |
| @Override |
| public boolean requery() { |
| throwIfCursorIsClosed(); |
| |
| try { |
| mCount = mBulkCursor.requery(getObserver()); |
| if (mCount != -1) { |
| mPos = -1; |
| closeWindow(); |
| |
| // super.requery() will call onChanged. Do it here instead of relying on the |
| // observer from the far side so that observers can see a correct value for mCount |
| // when responding to onChanged. |
| super.requery(); |
| return true; |
| } else { |
| deactivate(); |
| return false; |
| } |
| } catch (Exception ex) { |
| Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage()); |
| deactivate(); |
| return false; |
| } |
| } |
| |
| @Override |
| public String[] getColumnNames() { |
| throwIfCursorIsClosed(); |
| |
| return mColumns; |
| } |
| |
| @Override |
| public Bundle getExtras() { |
| throwIfCursorIsClosed(); |
| |
| try { |
| return mBulkCursor.getExtras(); |
| } catch (RemoteException e) { |
| // This should never happen because the system kills processes that are using remote |
| // cursors when the provider process is killed. |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Override |
| public Bundle respond(Bundle extras) { |
| throwIfCursorIsClosed(); |
| |
| try { |
| return mBulkCursor.respond(extras); |
| } catch (RemoteException e) { |
| // the system kills processes that are using remote cursors when the provider process |
| // is killed, but this can still happen if this is being called from the system process, |
| // so, better to log and return an empty bundle. |
| Log.w(TAG, "respond() threw RemoteException, returning an empty bundle.", e); |
| return Bundle.EMPTY; |
| } |
| } |
| } |