blob: e7d2a68c4221385e7543e4e8940f422b0da2da75 [file] [log] [blame]
/*
* 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 com.android.server.testing.shadows;
import android.app.backup.BackupDataInput;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.io.EOFException;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* Shadow for {@link BackupDataInput}. Format read does NOT match implementation. To write data to
* be read by this shadow, you should also declare shadow {@link ShadowBackupDataOutput}.
*/
@Implements(BackupDataInput.class)
public class ShadowBackupDataInput {
private static boolean sReadNextHeaderThrow = false;
public static void throwInNextHeaderRead() {
sReadNextHeaderThrow = true;
}
@Resetter
public static void reset() {
sReadNextHeaderThrow = false;
}
private FileDescriptor mFileDescriptor;
private ObjectInputStream mInput;
private int mSize;
private String mKey;
private boolean mHeaderReady;
@Implementation
protected void __constructor__(FileDescriptor fd) {
mFileDescriptor = fd;
}
@Implementation
protected boolean readNextHeader() throws IOException {
if (sReadNextHeaderThrow) {
sReadNextHeaderThrow = false;
throw new IOException("Fake exception");
}
mHeaderReady = false;
try {
ensureInput();
mSize = mInput.readInt();
} catch (EOFException e) {
return false;
}
mKey = mInput.readUTF();
mHeaderReady = true;
return true;
}
@Implementation
protected String getKey() {
checkHeaderReady();
return mKey;
}
@Implementation
protected int getDataSize() {
checkHeaderReady();
return mSize;
}
@Implementation
protected int readEntityData(byte[] data, int offset, int size) throws IOException {
checkHeaderReady();
int result = mInput.read(data, offset, size);
if (result < 0) {
throw new IOException("result=0x" + Integer.toHexString(result));
}
return result;
}
@Implementation
protected void skipEntityData() throws IOException {
checkHeaderReady();
mInput.read(new byte[mSize], 0, mSize);
}
private void checkHeaderReady() {
if (!mHeaderReady) {
throw new IllegalStateException("Entity header not read");
}
}
/**
* Lazily initializing input to avoid throwing exception when stream is completely empty in
* constructor (Java Object IO writes/reads some header data).
*
* @throws EOFException When the input is empty.
*/
private void ensureInput() throws EOFException {
if (mInput == null) {
try {
mInput = new ObjectInputStream(new FileInputStream(mFileDescriptor));
} catch (EOFException e) {
throw e;
} catch (IOException e) {
throw new AssertionError(e);
}
}
}
}