• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static java.nio.charset.StandardCharsets.UTF_8;
20 
21 import java.nio.ByteBuffer;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Objects;
25 
26 public class PackageTable {
27 
28     private static final int FINGERPRINT_BYTES = 8;
29     // int: mPackageId + int: mBooleanStartIndex + int: mNextOffset
30     private static final int NODE_SKIP_BYTES = 12;
31 
32     private Header mHeader;
33     private ByteBuffer mBuffer;
34 
fromBytes(ByteBuffer bytes)35     public static PackageTable fromBytes(ByteBuffer bytes) {
36         PackageTable packageTable = new PackageTable();
37         packageTable.mBuffer = bytes;
38         packageTable.mHeader = Header.fromBytes(new ByteBufferReader(bytes));
39 
40         return packageTable;
41     }
42 
get(String packageName)43     public Node get(String packageName) {
44         int numBuckets = (mHeader.mNodeOffset - mHeader.mBucketOffset) / 4;
45         int bucketIndex = TableUtils.getBucketIndex(packageName.getBytes(UTF_8), numBuckets);
46         int newPosition = mHeader.mBucketOffset + bucketIndex * 4;
47         if (newPosition >= mHeader.mNodeOffset) {
48             return null;
49         }
50         ByteBufferReader reader = new ByteBufferReader(mBuffer);
51         reader.position(newPosition);
52         int nodeIndex = reader.readInt();
53 
54         if (nodeIndex < mHeader.mNodeOffset || nodeIndex >= mHeader.mFileSize) {
55             return null;
56         }
57 
58         while (nodeIndex != -1) {
59             reader.position(nodeIndex);
60             Node node = Node.fromBytes(reader, mHeader.mVersion);
61             if (Objects.equals(packageName, node.mPackageName)) {
62                 return node;
63             }
64             nodeIndex = node.mNextOffset;
65         }
66 
67         return null;
68     }
69 
getPackageList()70     public List<String> getPackageList() {
71         List<String> list = new ArrayList<>(mHeader.mNumPackages);
72         ByteBufferReader reader = new ByteBufferReader(mBuffer);
73         reader.position(mHeader.mNodeOffset);
74         int fingerprintBytes = mHeader.mVersion == 1 ? 0 : FINGERPRINT_BYTES;
75         int skipBytes = fingerprintBytes + NODE_SKIP_BYTES;
76         for (int i = 0; i < mHeader.mNumPackages; i++) {
77             list.add(reader.readString());
78             reader.position(reader.position() + skipBytes);
79         }
80         return list;
81     }
82 
getHeader()83     public Header getHeader() {
84         return mHeader;
85     }
86 
87     public static class Header {
88 
89         private int mVersion;
90         private String mContainer;
91         private FileType mFileType;
92         private int mFileSize;
93         private int mNumPackages;
94         private int mBucketOffset;
95         private int mNodeOffset;
96 
fromBytes(ByteBufferReader reader)97         private static Header fromBytes(ByteBufferReader reader) {
98             Header header = new Header();
99             header.mVersion = reader.readInt();
100             header.mContainer = reader.readString();
101             header.mFileType = FileType.fromInt(reader.readByte());
102             header.mFileSize = reader.readInt();
103             header.mNumPackages = reader.readInt();
104             header.mBucketOffset = reader.readInt();
105             header.mNodeOffset = reader.readInt();
106 
107             if (header.mFileType != FileType.PACKAGE_MAP) {
108                 throw new AconfigStorageException("binary file is not a package map");
109             }
110 
111             return header;
112         }
113 
getVersion()114         public int getVersion() {
115             return mVersion;
116         }
117 
getContainer()118         public String getContainer() {
119             return mContainer;
120         }
121 
getFileType()122         public FileType getFileType() {
123             return mFileType;
124         }
125 
getFileSize()126         public int getFileSize() {
127             return mFileSize;
128         }
129 
getNumPackages()130         public int getNumPackages() {
131             return mNumPackages;
132         }
133 
getBucketOffset()134         public int getBucketOffset() {
135             return mBucketOffset;
136         }
137 
getNodeOffset()138         public int getNodeOffset() {
139             return mNodeOffset;
140         }
141     }
142 
143     public static class Node {
144 
145         private String mPackageName;
146         private int mPackageId;
147         private long mPackageFingerprint;
148         private int mBooleanStartIndex;
149         private int mNextOffset;
150         private boolean mHasPackageFingerprint;
151 
fromBytes(ByteBufferReader reader, int version)152         private static Node fromBytes(ByteBufferReader reader, int version) {
153             switch (version) {
154                 case 1:
155                     return fromBytesV1(reader);
156                 case 2:
157                     return fromBytesV2(reader);
158                 default:
159                     // Do we want to throw here?
160                     return new Node();
161             }
162         }
163 
fromBytesV1(ByteBufferReader reader)164         private static Node fromBytesV1(ByteBufferReader reader) {
165             Node node = new Node();
166             node.mPackageName = reader.readString();
167             node.mPackageId = reader.readInt();
168             node.mBooleanStartIndex = reader.readInt();
169             node.mNextOffset = reader.readInt();
170             node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
171             return node;
172         }
173 
fromBytesV2(ByteBufferReader reader)174         private static Node fromBytesV2(ByteBufferReader reader) {
175             Node node = new Node();
176             node.mPackageName = reader.readString();
177             node.mPackageId = reader.readInt();
178             node.mPackageFingerprint = reader.readLong();
179             node.mBooleanStartIndex = reader.readInt();
180             node.mNextOffset = reader.readInt();
181             node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
182             node.mHasPackageFingerprint = true;
183             return node;
184         }
185 
186         @Override
hashCode()187         public int hashCode() {
188             return Objects.hash(mPackageName, mPackageId, mBooleanStartIndex, mNextOffset);
189         }
190 
191         @Override
equals(Object obj)192         public boolean equals(Object obj) {
193             if (this == obj) {
194                 return true;
195             }
196 
197             if (obj == null || !(obj instanceof Node)) {
198                 return false;
199             }
200 
201             Node other = (Node) obj;
202             return Objects.equals(mPackageName, other.mPackageName)
203                     && mPackageId == other.mPackageId
204                     && mBooleanStartIndex == other.mBooleanStartIndex
205                     && mNextOffset == other.mNextOffset;
206         }
207 
getPackageName()208         public String getPackageName() {
209             return mPackageName;
210         }
211 
getPackageId()212         public int getPackageId() {
213             return mPackageId;
214         }
215 
getPackageFingerprint()216         public long getPackageFingerprint() {
217             return mPackageFingerprint;
218         }
219 
getBooleanStartIndex()220         public int getBooleanStartIndex() {
221             return mBooleanStartIndex;
222         }
223 
getNextOffset()224         public int getNextOffset() {
225             return mNextOffset;
226         }
227 
hasPackageFingerprint()228         public boolean hasPackageFingerprint() {
229             return mHasPackageFingerprint;
230         }
231     }
232 }
233