CursorToBulkCursorAdaptor.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.IBinder; 21import android.os.RemoteException; 22import android.util.Config; 23import android.util.Log; 24 25 26/** 27 * Wraps a BulkCursor around an existing Cursor making it remotable. 28 * 29 * {@hide} 30 */ 31public final class CursorToBulkCursorAdaptor extends BulkCursorNative 32 implements IBinder.DeathRecipient { 33 private static final String TAG = "Cursor"; 34 private final CrossProcessCursor mCursor; 35 private CursorWindow mWindow; 36 private final String mProviderName; 37 private ContentObserverProxy mObserver; 38 39 private static final class ContentObserverProxy extends ContentObserver 40 { 41 protected IContentObserver mRemote; 42 43 public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) { 44 super(null); 45 mRemote = remoteObserver; 46 try { 47 remoteObserver.asBinder().linkToDeath(recipient, 0); 48 } catch (RemoteException e) { 49 // Do nothing, the far side is dead 50 } 51 } 52 53 public boolean unlinkToDeath(DeathRecipient recipient) { 54 return mRemote.asBinder().unlinkToDeath(recipient, 0); 55 } 56 57 @Override 58 public boolean deliverSelfNotifications() { 59 // The far side handles the self notifications. 60 return false; 61 } 62 63 @Override 64 public void onChange(boolean selfChange) { 65 try { 66 mRemote.onChange(selfChange); 67 } catch (RemoteException ex) { 68 // Do nothing, the far side is dead 69 } 70 } 71 } 72 73 public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName, 74 boolean allowWrite, CursorWindow window) { 75 try { 76 mCursor = (CrossProcessCursor) cursor; 77 if (mCursor instanceof AbstractWindowedCursor) { 78 AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor; 79 if (windowedCursor.hasWindow()) { 80 if (Log.isLoggable(TAG, Log.VERBOSE) || Config.LOGV) { 81 Log.v(TAG, "Cross process cursor has a local window before setWindow in " 82 + providerName, new RuntimeException()); 83 } 84 } 85 windowedCursor.setWindow(window); 86 } else { 87 mWindow = window; 88 mCursor.fillWindow(0, window); 89 } 90 } catch (ClassCastException e) { 91 // TODO Implement this case. 92 throw new UnsupportedOperationException( 93 "Only CrossProcessCursor cursors are supported across process for now", e); 94 } 95 mProviderName = providerName; 96 97 createAndRegisterObserverProxy(observer); 98 } 99 100 public void binderDied() { 101 mCursor.close(); 102 if (mWindow != null) { 103 mWindow.close(); 104 } 105 } 106 107 public CursorWindow getWindow(int startPos) { 108 mCursor.moveToPosition(startPos); 109 110 if (mWindow != null) { 111 if (startPos < mWindow.getStartPosition() || 112 startPos >= (mWindow.getStartPosition() + mWindow.getNumRows())) { 113 mCursor.fillWindow(startPos, mWindow); 114 } 115 return mWindow; 116 } else { 117 return ((AbstractWindowedCursor)mCursor).getWindow(); 118 } 119 } 120 121 public void onMove(int position) { 122 mCursor.onMove(mCursor.getPosition(), position); 123 } 124 125 public int count() { 126 return mCursor.getCount(); 127 } 128 129 public String[] getColumnNames() { 130 return mCursor.getColumnNames(); 131 } 132 133 public void deactivate() { 134 maybeUnregisterObserverProxy(); 135 mCursor.deactivate(); 136 } 137 138 public void close() { 139 maybeUnregisterObserverProxy(); 140 mCursor.close(); 141 } 142 143 public int requery(IContentObserver observer, CursorWindow window) { 144 if (mWindow == null) { 145 ((AbstractWindowedCursor)mCursor).setWindow(window); 146 } 147 try { 148 if (!mCursor.requery()) { 149 return -1; 150 } 151 } catch (IllegalStateException e) { 152 IllegalStateException leakProgram = new IllegalStateException( 153 mProviderName + " Requery misuse db, mCursor isClosed:" + 154 mCursor.isClosed(), e); 155 throw leakProgram; 156 } 157 158 if (mWindow != null) { 159 mCursor.fillWindow(0, window); 160 mWindow = window; 161 } 162 maybeUnregisterObserverProxy(); 163 createAndRegisterObserverProxy(observer); 164 return mCursor.getCount(); 165 } 166 167 public boolean getWantsAllOnMoveCalls() { 168 return mCursor.getWantsAllOnMoveCalls(); 169 } 170 171 /** 172 * Create a ContentObserver from the observer and register it as an observer on the 173 * underlying cursor. 174 * @param observer the IContentObserver that wants to monitor the cursor 175 * @throws IllegalStateException if an observer is already registered 176 */ 177 private void createAndRegisterObserverProxy(IContentObserver observer) { 178 if (mObserver != null) { 179 throw new IllegalStateException("an observer is already registered"); 180 } 181 mObserver = new ContentObserverProxy(observer, this); 182 mCursor.registerContentObserver(mObserver); 183 } 184 185 /** Unregister the observer if it is already registered. */ 186 private void maybeUnregisterObserverProxy() { 187 if (mObserver != null) { 188 mCursor.unregisterContentObserver(mObserver); 189 mObserver.unlinkToDeath(this); 190 mObserver = null; 191 } 192 } 193 194 public Bundle getExtras() { 195 return mCursor.getExtras(); 196 } 197 198 public Bundle respond(Bundle extras) { 199 return mCursor.respond(extras); 200 } 201} 202