| /* |
| * 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; |
| private IBulkCursor mBulkCursor; |
| private int mCount; |
| private String[] mColumns; |
| private boolean mWantsAllOnMoveCalls; |
| |
| public void set(IBulkCursor bulkCursor) { |
| mBulkCursor = bulkCursor; |
| |
| try { |
| mCount = mBulkCursor.count(); |
| mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls(); |
| |
| // Search for the rowID column index and set it for our parent |
| mColumns = mBulkCursor.getColumnNames(); |
| mRowIdColumnIndex = findRowIdColumnIndex(mColumns); |
| } catch (RemoteException ex) { |
| Log.e(TAG, "Setup failed because the remote process is dead"); |
| } |
| } |
| |
| /** |
| * Version of set() that does fewer Binder calls if the caller |
| * already knows BulkCursorToCursorAdaptor's properties. |
| */ |
| public void set(IBulkCursor bulkCursor, int count, int idIndex) { |
| mBulkCursor = bulkCursor; |
| mColumns = null; // lazily retrieved |
| mCount = count; |
| mRowIdColumnIndex = idIndex; |
| } |
| |
| /** |
| * Returns column index of "_id" column, or -1 if not found. |
| */ |
| public static int findRowIdColumnIndex(String[] columnNames) { |
| int length = columnNames.length; |
| for (int i = 0; i < length; i++) { |
| if (columnNames[i].equals("_id")) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * 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 synchronized IContentObserver getObserver() { |
| if (mObserverBridge == null) { |
| mObserverBridge = new SelfContentObserver(this); |
| } |
| return mObserverBridge.getContentObserver(); |
| } |
| |
| @Override |
| public int getCount() { |
| return mCount; |
| } |
| |
| @Override |
| public boolean onMove(int oldPosition, int newPosition) { |
| try { |
| // Make sure we have the proper window |
| if (mWindow != null) { |
| if (newPosition < mWindow.getStartPosition() || |
| newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) { |
| mWindow = mBulkCursor.getWindow(newPosition); |
| } else if (mWantsAllOnMoveCalls) { |
| mBulkCursor.onMove(newPosition); |
| } |
| } else { |
| mWindow = mBulkCursor.getWindow(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(); |
| |
| try { |
| mBulkCursor.deactivate(); |
| } catch (RemoteException ex) { |
| Log.w(TAG, "Remote process exception when deactivating"); |
| } |
| mWindow = null; |
| } |
| |
| @Override |
| public void close() { |
| super.close(); |
| try { |
| mBulkCursor.close(); |
| } catch (RemoteException ex) { |
| Log.w(TAG, "Remote process exception when closing"); |
| } |
| mWindow = null; |
| } |
| |
| @Override |
| public boolean requery() { |
| try { |
| int oldCount = mCount; |
| //TODO get the window from a pool somewhere to avoid creating the memory dealer |
| mCount = mBulkCursor.requery(getObserver(), new CursorWindow( |
| false /* the window will be accessed across processes */)); |
| 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() { |
| if (mColumns == null) { |
| try { |
| mColumns = mBulkCursor.getColumnNames(); |
| } catch (RemoteException ex) { |
| Log.e(TAG, "Unable to fetch column names because the remote process is dead"); |
| return null; |
| } |
| } |
| return mColumns; |
| } |
| |
| @Override |
| public Bundle getExtras() { |
| 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) { |
| 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; |
| } |
| } |
| } |