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 17 package android.database; 18 19 import android.os.Bundle; 20 import android.os.RemoteException; 21 import android.util.Log; 22 23 /** 24 * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local process. 25 * 26 * {@hide} 27 */ 28 public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { 29 private static final String TAG = "BulkCursor"; 30 31 private SelfContentObserver mObserverBridge = new SelfContentObserver(this); 32 private IBulkCursor mBulkCursor; 33 private String[] mColumns; 34 private boolean mWantsAllOnMoveCalls; 35 private int mCount; 36 37 /** 38 * Initializes the adaptor. 39 * Must be called before first use. 40 */ 41 public void initialize(BulkCursorDescriptor d) { 42 mBulkCursor = d.cursor; 43 mColumns = d.columnNames; 44 mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns); 45 mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls; 46 mCount = d.count; 47 if (d.window != null) { 48 setWindow(d.window); 49 } 50 } 51 52 /** 53 * Gets a SelfDataChangeOberserver that can be sent to a remote 54 * process to receive change notifications over IPC. 55 * 56 * @return A SelfContentObserver hooked up to this Cursor 57 */ 58 public IContentObserver getObserver() { 59 return mObserverBridge.getContentObserver(); 60 } 61 62 private void throwIfCursorIsClosed() { 63 if (mBulkCursor == null) { 64 throw new StaleDataException("Attempted to access a cursor after it has been closed."); 65 } 66 } 67 68 @Override 69 public int getCount() { 70 throwIfCursorIsClosed(); 71 return mCount; 72 } 73 74 @Override 75 public boolean onMove(int oldPosition, int newPosition) { 76 throwIfCursorIsClosed(); 77 78 try { 79 // Make sure we have the proper window 80 if (mWindow == null 81 || newPosition < mWindow.getStartPosition() 82 || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) { 83 setWindow(mBulkCursor.getWindow(newPosition)); 84 } else if (mWantsAllOnMoveCalls) { 85 mBulkCursor.onMove(newPosition); 86 } 87 } catch (RemoteException ex) { 88 // We tried to get a window and failed 89 Log.e(TAG, "Unable to get window because the remote process is dead"); 90 return false; 91 } 92 93 // Couldn't obtain a window, something is wrong 94 if (mWindow == null) { 95 return false; 96 } 97 98 return true; 99 } 100 101 @Override 102 public void deactivate() { 103 // This will call onInvalidated(), so make sure to do it before calling release, 104 // which is what actually makes the data set invalid. 105 super.deactivate(); 106 107 if (mBulkCursor != null) { 108 try { 109 mBulkCursor.deactivate(); 110 } catch (RemoteException ex) { 111 Log.w(TAG, "Remote process exception when deactivating"); 112 } 113 } 114 } 115 116 @Override 117 public void close() { 118 super.close(); 119 120 if (mBulkCursor != null) { 121 try { 122 mBulkCursor.close(); 123 } catch (RemoteException ex) { 124 Log.w(TAG, "Remote process exception when closing"); 125 } finally { 126 mBulkCursor = null; 127 } 128 } 129 } 130 131 @Override 132 public boolean requery() { 133 throwIfCursorIsClosed(); 134 135 try { 136 mCount = mBulkCursor.requery(getObserver()); 137 if (mCount != -1) { 138 mPos = -1; 139 closeWindow(); 140 141 // super.requery() will call onChanged. Do it here instead of relying on the 142 // observer from the far side so that observers can see a correct value for mCount 143 // when responding to onChanged. 144 super.requery(); 145 return true; 146 } else { 147 deactivate(); 148 return false; 149 } 150 } catch (Exception ex) { 151 Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage()); 152 deactivate(); 153 return false; 154 } 155 } 156 157 @Override 158 public String[] getColumnNames() { 159 throwIfCursorIsClosed(); 160 161 return mColumns; 162 } 163 164 @Override 165 public Bundle getExtras() { 166 throwIfCursorIsClosed(); 167 168 try { 169 return mBulkCursor.getExtras(); 170 } catch (RemoteException e) { 171 // This should never happen because the system kills processes that are using remote 172 // cursors when the provider process is killed. 173 throw new RuntimeException(e); 174 } 175 } 176 177 @Override 178 public Bundle respond(Bundle extras) { 179 throwIfCursorIsClosed(); 180 181 try { 182 return mBulkCursor.respond(extras); 183 } catch (RemoteException e) { 184 // the system kills processes that are using remote cursors when the provider process 185 // is killed, but this can still happen if this is being called from the system process, 186 // so, better to log and return an empty bundle. 187 Log.w(TAG, "respond() threw RemoteException, returning an empty bundle.", e); 188 return Bundle.EMPTY; 189 } 190 } 191 } 192