| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.database; |
| |
| import android.database.sqlite.SQLiteMisuseException; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.util.Config; |
| import android.util.Log; |
| |
| import java.util.Map; |
| |
| |
| /** |
| * Wraps a BulkCursor around an existing Cursor making it remotable. |
| * |
| * {@hide} |
| */ |
| public final class CursorToBulkCursorAdaptor extends BulkCursorNative |
| implements IBinder.DeathRecipient { |
| private static final String TAG = "Cursor"; |
| private final CrossProcessCursor mCursor; |
| private CursorWindow mWindow; |
| private final String mProviderName; |
| private final boolean mReadOnly; |
| private ContentObserverProxy mObserver; |
| |
| private static final class ContentObserverProxy extends ContentObserver |
| { |
| protected IContentObserver mRemote; |
| |
| public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) { |
| super(null); |
| mRemote = remoteObserver; |
| try { |
| remoteObserver.asBinder().linkToDeath(recipient, 0); |
| } catch (RemoteException e) { |
| // Do nothing, the far side is dead |
| } |
| } |
| |
| public boolean unlinkToDeath(DeathRecipient recipient) { |
| return mRemote.asBinder().unlinkToDeath(recipient, 0); |
| } |
| |
| @Override |
| public boolean deliverSelfNotifications() { |
| // The far side handles the self notifications. |
| return false; |
| } |
| |
| @Override |
| public void onChange(boolean selfChange) { |
| try { |
| mRemote.onChange(selfChange); |
| } catch (RemoteException ex) { |
| // Do nothing, the far side is dead |
| } |
| } |
| } |
| |
| public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName, |
| boolean allowWrite, CursorWindow window) { |
| try { |
| mCursor = (CrossProcessCursor) cursor; |
| if (mCursor instanceof AbstractWindowedCursor) { |
| AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor; |
| if (windowedCursor.hasWindow()) { |
| if (Log.isLoggable(TAG, Log.VERBOSE) || Config.LOGV) { |
| Log.v(TAG, "Cross process cursor has a local window before setWindow in " |
| + providerName, new RuntimeException()); |
| } |
| } |
| windowedCursor.setWindow(window); |
| } else { |
| mWindow = window; |
| mCursor.fillWindow(0, window); |
| } |
| } catch (ClassCastException e) { |
| // TODO Implement this case. |
| throw new UnsupportedOperationException( |
| "Only CrossProcessCursor cursors are supported across process for now", e); |
| } |
| mProviderName = providerName; |
| mReadOnly = !allowWrite; |
| |
| createAndRegisterObserverProxy(observer); |
| } |
| |
| public void binderDied() { |
| mCursor.close(); |
| if (mWindow != null) { |
| mWindow.close(); |
| } |
| } |
| |
| public CursorWindow getWindow(int startPos) { |
| mCursor.moveToPosition(startPos); |
| |
| if (mWindow != null) { |
| if (startPos < mWindow.getStartPosition() || |
| startPos >= (mWindow.getStartPosition() + mWindow.getNumRows())) { |
| mCursor.fillWindow(startPos, mWindow); |
| } |
| return mWindow; |
| } else { |
| return ((AbstractWindowedCursor)mCursor).getWindow(); |
| } |
| } |
| |
| public void onMove(int position) { |
| mCursor.onMove(mCursor.getPosition(), position); |
| } |
| |
| public int count() { |
| return mCursor.getCount(); |
| } |
| |
| public String[] getColumnNames() { |
| return mCursor.getColumnNames(); |
| } |
| |
| public void deactivate() { |
| maybeUnregisterObserverProxy(); |
| mCursor.deactivate(); |
| } |
| |
| public void close() { |
| maybeUnregisterObserverProxy(); |
| mCursor.close(); |
| } |
| |
| public int requery(IContentObserver observer, CursorWindow window) { |
| if (mWindow == null) { |
| ((AbstractWindowedCursor)mCursor).setWindow(window); |
| } |
| try { |
| if (!mCursor.requery()) { |
| return -1; |
| } |
| } catch (IllegalStateException e) { |
| IllegalStateException leakProgram = new IllegalStateException( |
| mProviderName + " Requery misuse db, mCursor isClosed:" + |
| mCursor.isClosed(), e); |
| throw leakProgram; |
| } |
| |
| if (mWindow != null) { |
| mCursor.fillWindow(0, window); |
| mWindow = window; |
| } |
| maybeUnregisterObserverProxy(); |
| createAndRegisterObserverProxy(observer); |
| return mCursor.getCount(); |
| } |
| |
| public boolean getWantsAllOnMoveCalls() { |
| return mCursor.getWantsAllOnMoveCalls(); |
| } |
| |
| /** |
| * Create a ContentObserver from the observer and register it as an observer on the |
| * underlying cursor. |
| * @param observer the IContentObserver that wants to monitor the cursor |
| * @throws IllegalStateException if an observer is already registered |
| */ |
| private void createAndRegisterObserverProxy(IContentObserver observer) { |
| if (mObserver != null) { |
| throw new IllegalStateException("an observer is already registered"); |
| } |
| mObserver = new ContentObserverProxy(observer, this); |
| mCursor.registerContentObserver(mObserver); |
| } |
| |
| /** Unregister the observer if it is already registered. */ |
| private void maybeUnregisterObserverProxy() { |
| if (mObserver != null) { |
| mCursor.unregisterContentObserver(mObserver); |
| mObserver.unlinkToDeath(this); |
| mObserver = null; |
| } |
| } |
| |
| public boolean updateRows(Map<? extends Long, ? extends Map<String, Object>> values) { |
| if (mReadOnly) { |
| Log.w("ContentProvider", "Permission Denial: modifying " |
| + mProviderName |
| + " from pid=" + Binder.getCallingPid() |
| + ", uid=" + Binder.getCallingUid()); |
| return false; |
| } |
| return mCursor.commitUpdates(values); |
| } |
| |
| public boolean deleteRow(int position) { |
| if (mReadOnly) { |
| Log.w("ContentProvider", "Permission Denial: modifying " |
| + mProviderName |
| + " from pid=" + Binder.getCallingPid() |
| + ", uid=" + Binder.getCallingUid()); |
| return false; |
| } |
| if (mCursor.moveToPosition(position) == false) { |
| return false; |
| } |
| return mCursor.deleteRow(); |
| } |
| |
| public Bundle getExtras() { |
| return mCursor.getExtras(); |
| } |
| |
| public Bundle respond(Bundle extras) { |
| return mCursor.respond(extras); |
| } |
| } |