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