182 lines
5.4 KiB
Java
182 lines
5.4 KiB
Java
/*
|
|
* Copyright (C) 2018 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.annotation.NonNull;
|
|
import android.util.SparseArray;
|
|
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Cursor that offers to redact values of requested columns.
|
|
*
|
|
* @hide
|
|
*/
|
|
public class RedactingCursor extends CrossProcessCursorWrapper {
|
|
private final SparseArray<Object> mRedactions;
|
|
|
|
private RedactingCursor(@NonNull Cursor cursor, SparseArray<Object> redactions) {
|
|
super(cursor);
|
|
mRedactions = redactions;
|
|
}
|
|
|
|
/**
|
|
* Create a wrapped instance of the given {@link Cursor} which redacts the
|
|
* requested columns so they always return specific values when accessed.
|
|
* <p>
|
|
* If a redacted column appears multiple times in the underlying cursor, all
|
|
* instances will be redacted. If none of the redacted columns appear in the
|
|
* given cursor, the given cursor will be returned untouched to improve
|
|
* performance.
|
|
*/
|
|
public static Cursor create(@NonNull Cursor cursor, @NonNull Map<String, Object> redactions) {
|
|
final SparseArray<Object> internalRedactions = new SparseArray<>();
|
|
|
|
final String[] columns = cursor.getColumnNames();
|
|
for (int i = 0; i < columns.length; i++) {
|
|
if (redactions.containsKey(columns[i])) {
|
|
internalRedactions.put(i, redactions.get(columns[i]));
|
|
}
|
|
}
|
|
|
|
if (internalRedactions.size() == 0) {
|
|
return cursor;
|
|
} else {
|
|
return new RedactingCursor(cursor, internalRedactions);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void fillWindow(int position, CursorWindow window) {
|
|
// Fill window directly to ensure data is redacted
|
|
DatabaseUtils.cursorFillWindow(this, position, window);
|
|
}
|
|
|
|
@Override
|
|
public CursorWindow getWindow() {
|
|
// Returning underlying window risks leaking redacted data
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Cursor getWrappedCursor() {
|
|
throw new UnsupportedOperationException(
|
|
"Returning underlying cursor risks leaking redacted data");
|
|
}
|
|
|
|
@Override
|
|
public double getDouble(int columnIndex) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
return (double) mRedactions.valueAt(i);
|
|
} else {
|
|
return super.getDouble(columnIndex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public float getFloat(int columnIndex) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
return (float) mRedactions.valueAt(i);
|
|
} else {
|
|
return super.getFloat(columnIndex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getInt(int columnIndex) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
return (int) mRedactions.valueAt(i);
|
|
} else {
|
|
return super.getInt(columnIndex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public long getLong(int columnIndex) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
return (long) mRedactions.valueAt(i);
|
|
} else {
|
|
return super.getLong(columnIndex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public short getShort(int columnIndex) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
return (short) mRedactions.valueAt(i);
|
|
} else {
|
|
return super.getShort(columnIndex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String getString(int columnIndex) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
return (String) mRedactions.valueAt(i);
|
|
} else {
|
|
return super.getString(columnIndex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
buffer.data = ((String) mRedactions.valueAt(i)).toCharArray();
|
|
buffer.sizeCopied = buffer.data.length;
|
|
} else {
|
|
super.copyStringToBuffer(columnIndex, buffer);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public byte[] getBlob(int columnIndex) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
return (byte[]) mRedactions.valueAt(i);
|
|
} else {
|
|
return super.getBlob(columnIndex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getType(int columnIndex) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
return DatabaseUtils.getTypeOfObject(mRedactions.valueAt(i));
|
|
} else {
|
|
return super.getType(columnIndex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isNull(int columnIndex) {
|
|
final int i = mRedactions.indexOfKey(columnIndex);
|
|
if (i >= 0) {
|
|
return mRedactions.valueAt(i) == null;
|
|
} else {
|
|
return super.isNull(columnIndex);
|
|
}
|
|
}
|
|
}
|