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