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