blob: 19ad946b91a3dab79b62386f5726bc996a64b0a4 [file] [log] [blame]
/*
* 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.deactivate();
}
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);
}
}