1 /* 2 * Copyright 2024 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 android.util; 18 19 import java.io.FileOutputStream; 20 import java.io.IOException; 21 22 /** 23 * {@link FileOutputStream} for {@link AtomicFile}. 24 * Allows user-code to write into file output stream backed by {@link AtomicFile}. 25 * In order to "commit" the new content to the file, call {@link #markSuccess()} then 26 * {@link #close()}. Calling{@link #markSuccess()} alone won't update the file. 27 * This class does not confer any file locking semantics. Do not use this class when the file may be 28 * accessed or modified concurrently by multiple threads or processes. The caller is responsible for 29 * ensuring appropriate mutual exclusion invariants whenever it accesses the file. 30 * @hide 31 */ 32 @android.ravenwood.annotation.RavenwoodKeepWholeClass 33 public class AtomicFileOutputStream extends FileOutputStream implements AutoCloseable { 34 private static final String TAG = "AtomicFileOutputStream"; 35 private final AtomicFile mFile; 36 private final FileOutputStream mOutStream; 37 private boolean mWritingSuccessful; 38 private boolean mClosed; 39 40 /** 41 * See {@link AtomicFile#startWrite()}. 42 */ AtomicFileOutputStream(AtomicFile file)43 public AtomicFileOutputStream(AtomicFile file) throws IOException { 44 this(file, file.startWrite()); 45 } 46 AtomicFileOutputStream(AtomicFile file, FileOutputStream oStream)47 private AtomicFileOutputStream(AtomicFile file, FileOutputStream oStream) throws IOException { 48 super(oStream.getFD()); 49 mFile = file; 50 mOutStream = oStream; 51 } 52 53 /** 54 * Marks the writing as successful. 55 */ markSuccess()56 public void markSuccess() { 57 if (mWritingSuccessful) { 58 throw new IllegalStateException(TAG + " success is already marked"); 59 } 60 mWritingSuccessful = true; 61 } 62 63 /** 64 * Finishes writing to {@link #mFile}, see {@link AtomicFile#finishWrite(FileOutputStream)} 65 * and {@link AtomicFile#failWrite(FileOutputStream)}. Closes {@link #mOutStream} which 66 * is the owner of the file descriptor. 67 */ 68 @Override close()69 public void close() throws IOException { 70 super.close(); 71 synchronized (mOutStream) { 72 if (mClosed) { 73 // FileOutputStream#finalize() may call this #close() method. 74 // We don't want to throw exceptions in this case. 75 // CloseGuard#warnIfOpen() also called there, so no need to log warnings in 76 // AtomicFileOutputStream#finalize(). 77 return; 78 } 79 mClosed = true; 80 } 81 82 if (mWritingSuccessful) { 83 mFile.finishWrite(mOutStream); 84 } else { 85 mFile.failWrite(mOutStream); 86 } 87 } 88 89 /** 90 * Creates string representation of the object. 91 */ 92 @Override toString()93 public String toString() { 94 return TAG + "[" 95 + "mFile=" + mFile 96 + ", mWritingSuccessful=" + mWritingSuccessful 97 + ", mClosed=" + mClosed 98 + "]"; 99 } 100 } 101