1 /* 2 * Copyright (C) 2022 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.net; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.util.AtomicFile; 22 import android.util.SystemConfigFileCommitEventLogger; 23 24 import java.io.DataInputStream; 25 import java.io.DataOutputStream; 26 import java.io.File; 27 import java.io.FileInputStream; 28 import java.io.FileNotFoundException; 29 import java.io.FileOutputStream; 30 import java.io.IOException; 31 32 /** 33 * A simple integer backed by an on-disk {@link AtomicFile}. Not thread-safe. 34 */ 35 public class PersistentInt { 36 private final String mPath; 37 private final AtomicFile mFile; 38 39 /** 40 * Constructs a new {@code PersistentInt}. The counter is set to 0 if the file does not exist. 41 * Before returning, the constructor checks that the file is readable and writable. This 42 * indicates that in the future {@link #get} and {@link #set} are likely to succeed, 43 * though other events (data corruption, other code deleting the file, etc.) may cause these 44 * calls to fail in the future. 45 * 46 * @param path the path of the file to use. 47 * @param logger the logger 48 * @throws IOException the counter could not be read or written 49 */ PersistentInt(@onNull String path, @Nullable SystemConfigFileCommitEventLogger logger)50 public PersistentInt(@NonNull String path, @Nullable SystemConfigFileCommitEventLogger logger) 51 throws IOException { 52 mPath = path; 53 mFile = new AtomicFile(new File(path), logger); 54 checkReadWrite(); 55 } 56 checkReadWrite()57 private void checkReadWrite() throws IOException { 58 int value; 59 try { 60 value = get(); 61 } catch (FileNotFoundException e) { 62 // Counter does not exist. Attempt to initialize to 0. 63 // Note that we cannot tell here if the file does not exist or if opening it failed, 64 // because in Java both of those throw FileNotFoundException. 65 value = 0; 66 } 67 set(value); 68 get(); 69 // No exceptions? Good. 70 } 71 72 /** 73 * Gets the current value. 74 * 75 * @return the current value of the counter. 76 * @throws IOException if reading the value failed. 77 */ get()78 public int get() throws IOException { 79 try (FileInputStream fin = mFile.openRead(); 80 DataInputStream din = new DataInputStream(fin)) { 81 return din.readInt(); 82 } 83 } 84 85 /** 86 * Sets the current value. 87 * @param value the value to set 88 * @throws IOException if writing the value failed. 89 */ set(int value)90 public void set(int value) throws IOException { 91 FileOutputStream fout = null; 92 try { 93 fout = mFile.startWrite(); 94 DataOutputStream dout = new DataOutputStream(fout); 95 dout.writeInt(value); 96 mFile.finishWrite(fout); 97 } catch (IOException e) { 98 if (fout != null) { 99 mFile.failWrite(fout); 100 } 101 throw e; 102 } 103 } 104 getPath()105 public String getPath() { 106 return mPath; 107 } 108 } 109