1 /* 2 * Copyright (C) 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.aconfig.storage; 18 19 import java.io.Closeable; 20 import java.nio.MappedByteBuffer; 21 import java.nio.channels.FileChannel; 22 import java.nio.file.DirectoryStream; 23 import java.nio.file.Files; 24 import java.nio.file.NoSuchFileException; 25 import java.nio.file.Path; 26 import java.nio.file.Paths; 27 import java.nio.file.StandardOpenOption; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.HashSet; 31 import java.util.List; 32 import java.util.Set; 33 34 /** @hide */ 35 public class StorageFileProvider { 36 37 private static final String DEFAULT_MAP_PATH = "/metadata/aconfig/maps/"; 38 private static final String DEFAULT_BOOT_PATH = "/metadata/aconfig/boot/"; 39 private static final String PMAP_FILE_EXT = ".package.map"; 40 private static final String FMAP_FILE_EXT = ".flag.map"; 41 private static final String VAL_FILE_EXT = ".val"; 42 private static final StorageFileProvider DEFAULT_INSTANCE = 43 new StorageFileProvider(DEFAULT_MAP_PATH, DEFAULT_BOOT_PATH); 44 45 private final String mMapPath; 46 private final String mBootPath; 47 48 /** @hide */ getDefaultProvider()49 public static StorageFileProvider getDefaultProvider() { 50 return DEFAULT_INSTANCE; 51 } 52 53 /** @hide */ StorageFileProvider(String mapPath, String bootPath)54 public StorageFileProvider(String mapPath, String bootPath) { 55 mMapPath = mapPath; 56 mBootPath = bootPath; 57 } 58 59 /** @hide */ listContainers(String[] excludes)60 public List<String> listContainers(String[] excludes) { 61 List<String> result = new ArrayList<>(); 62 Set<String> set = new HashSet<>(Arrays.asList(excludes)); 63 64 try { 65 DirectoryStream<Path> stream = 66 Files.newDirectoryStream(Paths.get(mMapPath), "*" + PMAP_FILE_EXT); 67 for (Path entry : stream) { 68 String fileName = entry.getFileName().toString(); 69 String container = 70 fileName.substring(0, fileName.length() - PMAP_FILE_EXT.length()); 71 if (!set.contains(container)) { 72 result.add(container); 73 } 74 } 75 } catch (NoSuchFileException e) { 76 return result; 77 } catch (Exception e) { 78 throw new AconfigStorageException( 79 String.format("Fail to list map files in path %s", mMapPath), e); 80 } 81 82 return result; 83 } 84 85 /** @hide */ getPackageTable(String container)86 public PackageTable getPackageTable(String container) { 87 return PackageTable.fromBytes( 88 mapStorageFile( 89 Paths.get(mMapPath, container + PMAP_FILE_EXT), FileType.PACKAGE_MAP)); 90 } 91 92 /** @hide */ getFlagTable(String container)93 public FlagTable getFlagTable(String container) { 94 return FlagTable.fromBytes( 95 mapStorageFile(Paths.get(mMapPath, container + FMAP_FILE_EXT), FileType.FLAG_MAP)); 96 } 97 98 /** @hide */ getFlagValueList(String container)99 public FlagValueList getFlagValueList(String container) { 100 return FlagValueList.fromBytes( 101 mapStorageFile(Paths.get(mBootPath, container + VAL_FILE_EXT), FileType.FLAG_VAL)); 102 } 103 104 // Map a storage file given file path mapStorageFile(Path file, FileType type)105 private static MappedByteBuffer mapStorageFile(Path file, FileType type) { 106 FileChannel channel = null; 107 try { 108 channel = FileChannel.open(file, StandardOpenOption.READ); 109 return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); 110 } catch (Exception e) { 111 throw new AconfigStorageException( 112 AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, 113 String.format("Fail to mmap storage %s file %s", type.toString(), file), 114 e); 115 } finally { 116 quietlyDispose(channel); 117 } 118 } 119 quietlyDispose(Closeable closable)120 private static void quietlyDispose(Closeable closable) { 121 try { 122 if (closable != null) { 123 closable.close(); 124 } 125 } catch (Exception e) { 126 // no need to care, at least as of now 127 } 128 } 129 } 130