237 lines
7.9 KiB
Java
237 lines
7.9 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.content.ContentResolver;
|
||
|
import android.database.sqlite.SQLiteDatabase;
|
||
|
import android.database.sqlite.SQLiteQueryBuilder;
|
||
|
import android.net.Uri;
|
||
|
import android.os.CancellationSignal;
|
||
|
import android.util.ArraySet;
|
||
|
|
||
|
import com.android.internal.util.ArrayUtils;
|
||
|
|
||
|
import java.util.Arrays;
|
||
|
import java.util.Objects;
|
||
|
|
||
|
/**
|
||
|
* Cursor that supports deprecation of {@code _data} like columns which represent raw filepaths,
|
||
|
* typically by replacing values with fake paths that the OS then offers to redirect to
|
||
|
* {@link ContentResolver#openFileDescriptor(Uri, String)}, which developers
|
||
|
* should be using directly.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public class TranslatingCursor extends CrossProcessCursorWrapper {
|
||
|
public static class Config {
|
||
|
public final Uri baseUri;
|
||
|
public final String auxiliaryColumn;
|
||
|
public final String[] translateColumns;
|
||
|
|
||
|
public Config(Uri baseUri, String auxiliaryColumn, String... translateColumns) {
|
||
|
this.baseUri = baseUri;
|
||
|
this.auxiliaryColumn = auxiliaryColumn;
|
||
|
this.translateColumns = translateColumns;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public interface Translator {
|
||
|
String translate(String data, int auxiliaryColumnIndex,
|
||
|
String matchingColumn, Cursor cursor);
|
||
|
}
|
||
|
|
||
|
private final @NonNull Config mConfig;
|
||
|
private final @NonNull Translator mTranslator;
|
||
|
private final boolean mDropLast;
|
||
|
|
||
|
private final int mAuxiliaryColumnIndex;
|
||
|
private final ArraySet<Integer> mTranslateColumnIndices;
|
||
|
|
||
|
public TranslatingCursor(@NonNull Cursor cursor, @NonNull Config config,
|
||
|
@NonNull Translator translator, boolean dropLast) {
|
||
|
super(cursor);
|
||
|
|
||
|
mConfig = Objects.requireNonNull(config);
|
||
|
mTranslator = Objects.requireNonNull(translator);
|
||
|
mDropLast = dropLast;
|
||
|
|
||
|
mAuxiliaryColumnIndex = cursor.getColumnIndexOrThrow(config.auxiliaryColumn);
|
||
|
mTranslateColumnIndices = new ArraySet<>();
|
||
|
for (int i = 0; i < cursor.getColumnCount(); ++i) {
|
||
|
String columnName = cursor.getColumnName(i);
|
||
|
if (ArrayUtils.contains(config.translateColumns, columnName)) {
|
||
|
mTranslateColumnIndices.add(i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int getColumnCount() {
|
||
|
if (mDropLast) {
|
||
|
return super.getColumnCount() - 1;
|
||
|
} else {
|
||
|
return super.getColumnCount();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String[] getColumnNames() {
|
||
|
if (mDropLast) {
|
||
|
return Arrays.copyOfRange(super.getColumnNames(), 0, super.getColumnCount() - 1);
|
||
|
} else {
|
||
|
return super.getColumnNames();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static Cursor query(@NonNull Config config, @NonNull Translator translator,
|
||
|
SQLiteQueryBuilder qb, SQLiteDatabase db, String[] projectionIn, String selection,
|
||
|
String[] selectionArgs, String groupBy, String having, String sortOrder, String limit,
|
||
|
CancellationSignal signal) {
|
||
|
final boolean requestedAuxiliaryColumn = ArrayUtils.isEmpty(projectionIn)
|
||
|
|| ArrayUtils.contains(projectionIn, config.auxiliaryColumn);
|
||
|
final boolean requestedTranslateColumns = ArrayUtils.isEmpty(projectionIn)
|
||
|
|| ArrayUtils.containsAny(projectionIn, config.translateColumns);
|
||
|
|
||
|
// If caller didn't request any columns that need to be translated,
|
||
|
// we have nothing to redirect
|
||
|
if (!requestedTranslateColumns) {
|
||
|
return qb.query(db, projectionIn, selection, selectionArgs,
|
||
|
groupBy, having, sortOrder, limit, signal);
|
||
|
}
|
||
|
|
||
|
// If caller didn't request auxiliary column, we need to splice it in
|
||
|
if (!requestedAuxiliaryColumn) {
|
||
|
projectionIn = ArrayUtils.appendElement(String.class, projectionIn,
|
||
|
config.auxiliaryColumn);
|
||
|
}
|
||
|
|
||
|
final Cursor c = qb.query(db, projectionIn, selection, selectionArgs,
|
||
|
groupBy, having, sortOrder);
|
||
|
return new TranslatingCursor(c, config, translator, !requestedAuxiliaryColumn);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void fillWindow(int position, CursorWindow window) {
|
||
|
// Fill window directly to ensure data is rewritten
|
||
|
DatabaseUtils.cursorFillWindow(this, position, window);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public CursorWindow getWindow() {
|
||
|
// Returning underlying window risks leaking data
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public Cursor getWrappedCursor() {
|
||
|
throw new UnsupportedOperationException(
|
||
|
"Returning underlying cursor risks leaking data");
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public double getDouble(int columnIndex) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
throw new IllegalArgumentException();
|
||
|
} else {
|
||
|
return super.getDouble(columnIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public float getFloat(int columnIndex) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
throw new IllegalArgumentException();
|
||
|
} else {
|
||
|
return super.getFloat(columnIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int getInt(int columnIndex) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
throw new IllegalArgumentException();
|
||
|
} else {
|
||
|
return super.getInt(columnIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public long getLong(int columnIndex) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
throw new IllegalArgumentException();
|
||
|
} else {
|
||
|
return super.getLong(columnIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public short getShort(int columnIndex) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
throw new IllegalArgumentException();
|
||
|
} else {
|
||
|
return super.getShort(columnIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String getString(int columnIndex) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
return mTranslator.translate(super.getString(columnIndex),
|
||
|
mAuxiliaryColumnIndex, getColumnName(columnIndex), this);
|
||
|
} else {
|
||
|
return super.getString(columnIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
throw new IllegalArgumentException();
|
||
|
} else {
|
||
|
super.copyStringToBuffer(columnIndex, buffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public byte[] getBlob(int columnIndex) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
throw new IllegalArgumentException();
|
||
|
} else {
|
||
|
return super.getBlob(columnIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int getType(int columnIndex) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
return Cursor.FIELD_TYPE_STRING;
|
||
|
} else {
|
||
|
return super.getType(columnIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean isNull(int columnIndex) {
|
||
|
if (ArrayUtils.contains(mTranslateColumnIndices, columnIndex)) {
|
||
|
return getString(columnIndex) == null;
|
||
|
} else {
|
||
|
return super.isNull(columnIndex);
|
||
|
}
|
||
|
}
|
||
|
}
|