1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.testing.shadows; 18 19 import android.app.backup.BackupDataOutput; 20 21 import org.robolectric.annotation.Implementation; 22 import org.robolectric.annotation.Implements; 23 24 import java.io.ByteArrayOutputStream; 25 import java.io.FileDescriptor; 26 import java.io.FileOutputStream; 27 import java.io.IOException; 28 import java.io.ObjectOutputStream; 29 30 /** 31 * Shadow for {@link BackupDataOutput}. Format written does NOT match implementation. To read data 32 * written with this shadow you should also declare shadow {@link ShadowBackupDataInput}. 33 */ 34 @Implements(BackupDataOutput.class) 35 public class ShadowBackupDataOutput { 36 private FileDescriptor mFileDescriptor; 37 private ObjectOutputStream mOutput; 38 private long mQuota; 39 private int mTransportFlags; 40 41 @Implementation __constructor__(FileDescriptor fd, long quota, int transportFlags)42 protected void __constructor__(FileDescriptor fd, long quota, int transportFlags) { 43 mFileDescriptor = fd; 44 mQuota = quota; 45 mTransportFlags = transportFlags; 46 } 47 48 @Implementation getQuota()49 protected long getQuota() { 50 return mQuota; 51 } 52 53 @Implementation getTransportFlags()54 protected int getTransportFlags() { 55 return mTransportFlags; 56 } 57 getOutputStream()58 public ObjectOutputStream getOutputStream() { 59 ensureOutput(); 60 return mOutput; 61 } 62 63 @Implementation writeEntityHeader(String key, int dataSize)64 protected int writeEntityHeader(String key, int dataSize) throws IOException { 65 ensureOutput(); 66 final int size; 67 try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) { 68 writeEntityHeader(new ObjectOutputStream(byteStream), key, dataSize); 69 size = byteStream.size(); 70 } 71 writeEntityHeader(mOutput, key, dataSize); 72 return size; 73 } 74 writeEntityHeader(ObjectOutputStream stream, String key, int dataSize)75 private void writeEntityHeader(ObjectOutputStream stream, String key, int dataSize) 76 throws IOException { 77 // Write the int first because readInt() throws EOFException, to know when stream ends 78 stream.writeInt(dataSize); 79 stream.writeUTF(key); 80 stream.flush(); 81 } 82 83 @Implementation writeEntityData(byte[] data, int size)84 protected int writeEntityData(byte[] data, int size) throws IOException { 85 ensureOutput(); 86 mOutput.write(data, 0, size); 87 mOutput.flush(); 88 return size; 89 } 90 91 /** 92 * Lazily initializing output to avoid writing the header data below for when there is no data 93 * (Java Object IO writes/reads some header data). 94 */ ensureOutput()95 private void ensureOutput() { 96 if (mOutput == null) { 97 try { 98 // This writes 4 bytes: Java Object IO writes/reads some header data 99 mOutput = new ObjectOutputStream(new FileOutputStream(mFileDescriptor)); 100 } catch (IOException e) { 101 throw new AssertionError(e); 102 } 103 } 104 } 105 } 106