1 /*
2  * Copyright (C) 2023 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.ondevicepersonalization.services.util;
18 
19 import android.content.ContentValues;
20 
21 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
22 import com.android.ondevicepersonalization.services.fbs.EventFields;
23 import com.android.ondevicepersonalization.services.fbs.KeyValue;
24 import com.android.ondevicepersonalization.services.fbs.KeyValueList;
25 import com.android.ondevicepersonalization.services.fbs.Owner;
26 import com.android.ondevicepersonalization.services.fbs.QueryData;
27 import com.android.ondevicepersonalization.services.fbs.QueryFields;
28 
29 import com.google.common.primitives.Ints;
30 import com.google.flatbuffers.FlatBufferBuilder;
31 
32 import java.nio.ByteBuffer;
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /**
37  * Util class to support creation of OnDevicePersonalization flatbuffers
38  */
39 public class OnDevicePersonalizationFlatbufferUtils {
40     public static final byte DATA_TYPE_BYTE = 1;
41     public static final byte DATA_TYPE_SHORT = 2;
42     public static final byte DATA_TYPE_INT = 3;
43     public static final byte DATA_TYPE_LONG = 4;
44     public static final byte DATA_TYPE_FLOAT = 5;
45     public static final byte DATA_TYPE_DOUBLE = 6;
46     public static final byte DATA_TYPE_STRING = 7;
47     public static final byte DATA_TYPE_BLOB = 8;
48     public static final byte DATA_TYPE_BOOL = 9;
49     private static final String TAG = "OnDevicePersonalizationFlatbufferUtils";
50     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
51 
OnDevicePersonalizationFlatbufferUtils()52     private OnDevicePersonalizationFlatbufferUtils() {
53     }
54 
55     /**
56      * Creates a byte array representing the QueryData as a flatbuffer
57      */
createQueryData( String servicePackageName, String certDigest, List<ContentValues> rows)58     public static byte[] createQueryData(
59             String servicePackageName, String certDigest, List<ContentValues> rows) {
60         try {
61             sLogger.d(TAG + ": createQueryData started.");
62             FlatBufferBuilder builder = new FlatBufferBuilder();
63             int ownerOffset = createOwner(builder, servicePackageName, certDigest);
64             int rowsOffset = 0;
65             if (rows != null && !rows.isEmpty()) {
66                 int[] rowOffsets = new int[rows.size()];
67                 for (int i = 0; i < rows.size(); i++) {
68                     ContentValues row = rows.get(i);
69                     rowOffsets[i] = createKeyValueList(builder, row);
70                 }
71                 rowsOffset = QueryFields.createRowsVector(builder, rowOffsets);
72             }
73             QueryFields.startQueryFields(builder);
74             QueryFields.addOwner(builder, ownerOffset);
75             QueryFields.addRows(builder, rowsOffset);
76             int[] queryFieldsOffset = new int[1];
77             queryFieldsOffset[0] = QueryFields.endQueryFields(builder);
78             int queryFieldsListOffset = QueryData.createQueryFieldsVector(
79                     builder, queryFieldsOffset);
80             QueryData.startQueryData(builder);
81             QueryData.addQueryFields(builder, queryFieldsListOffset);
82             int queryDataOffset = QueryData.endQueryData(builder);
83             builder.finish(queryDataOffset);
84             return builder.sizedByteArray();
85         } catch (Exception e) {
86             sLogger.e(e, TAG + ": createQueryData failed.");
87             return new byte[0];
88         }
89     }
90 
91     /**
92      * Creates a byte array representing the EventData as a flatbuffer
93      */
createEventData(ContentValues data)94     public static byte[] createEventData(ContentValues data) {
95         try {
96             sLogger.d(TAG + ": createEventData started.");
97             FlatBufferBuilder builder = new FlatBufferBuilder();
98             int dataOffset = createKeyValueList(builder, data);
99             EventFields.startEventFields(builder);
100             EventFields.addData(builder, dataOffset);
101             int eventFieldsOffset = EventFields.endEventFields(builder);
102             builder.finish(eventFieldsOffset);
103             return builder.sizedByteArray();
104         } catch (Exception e) {
105             sLogger.e(e, TAG + ": createEventData failed.");
106             return new byte[0];
107         }
108     }
109 
110     /**
111      * Retrieves all KeyValueLists in a QueryField flatbuffer as a List of ContentValues objects.
112      */
getContentValuesFromQueryData(byte[] queryData)113     public static List<ContentValues> getContentValuesFromQueryData(byte[] queryData) {
114         List<ContentValues> contentValuesList = new ArrayList<>();
115         QueryFields queryFields = QueryData.getRootAsQueryData(
116                 ByteBuffer.wrap(queryData)).queryFields(0);
117         for (int i = 0; i < queryFields.rowsLength(); i++) {
118             contentValuesList.add(getContentValuesFromKeyValueList(queryFields.rows(i)));
119         }
120         return contentValuesList;
121     }
122 
123 
124     /**
125      * Retrieves the KeyValueList in a QueryField flatbuffer at the specified index as a
126      * ContentValues object.
127      */
getContentValuesRowFromQueryData(byte[] queryData, int rowIndex)128     public static ContentValues getContentValuesRowFromQueryData(byte[] queryData, int rowIndex) {
129         QueryFields queryFields = QueryData.getRootAsQueryData(
130                 ByteBuffer.wrap(queryData)).queryFields(0);
131         return getContentValuesFromKeyValueList(queryFields.rows(rowIndex));
132     }
133 
134     /**
135      * Retrieves the length of the rows in a QueryField flatbuffer.
136      */
getContentValuesLengthFromQueryData(byte[] queryData)137     public static int getContentValuesLengthFromQueryData(byte[] queryData) {
138         QueryFields queryFields = QueryData.getRootAsQueryData(
139                 ByteBuffer.wrap(queryData)).queryFields(0);
140         return queryFields.rowsLength();
141     }
142 
143     /**
144      * Retrieves the KeyValueList in an EventData flatbuffer as a ContentValues object.
145      */
getContentValuesFromEventData(byte[] eventData)146     public static ContentValues getContentValuesFromEventData(byte[] eventData) {
147         EventFields eventFields = EventFields.getRootAsEventFields(ByteBuffer.wrap(eventData));
148         return getContentValuesFromKeyValueList(eventFields.data());
149     }
150 
getContentValuesFromKeyValueList(KeyValueList list)151     private static ContentValues getContentValuesFromKeyValueList(KeyValueList list) {
152         ContentValues data = new ContentValues();
153         for (int i = 0; i < list.entriesLength(); i++) {
154             KeyValue kv = list.entries(i);
155             switch (kv.type()) {
156                 case DATA_TYPE_BYTE:
157                     data.put(kv.key(), kv.byteValue());
158                     break;
159                 case DATA_TYPE_SHORT:
160                     data.put(kv.key(), kv.shortValue());
161                     break;
162                 case DATA_TYPE_INT:
163                     data.put(kv.key(), kv.intValue());
164                     break;
165                 case DATA_TYPE_LONG:
166                     data.put(kv.key(), kv.longValue());
167                     break;
168                 case DATA_TYPE_FLOAT:
169                     data.put(kv.key(), kv.floatValue());
170                     break;
171                 case DATA_TYPE_DOUBLE:
172                     data.put(kv.key(), kv.doubleValue());
173                     break;
174                 case DATA_TYPE_STRING:
175                     data.put(kv.key(), kv.stringValue());
176                     break;
177                 case DATA_TYPE_BLOB:
178                     ByteBuffer buf = kv.blobValueAsByteBuffer();
179                     byte[] arr = new byte[buf.remaining()];
180                     buf.get(arr);
181                     data.put(kv.key(), arr);
182                     break;
183                 case DATA_TYPE_BOOL:
184                     data.put(kv.key(), kv.boolValue());
185                     break;
186             }
187         }
188         return data;
189     }
190 
createKeyValueList( FlatBufferBuilder builder, ContentValues data)191     private static int createKeyValueList(
192             FlatBufferBuilder builder, ContentValues data) {
193         int entriesOffset = 0;
194         if (data != null) {
195             ArrayList<Integer> entryOffsets = new ArrayList<>();
196             for (var entry : data.valueSet()) {
197                 if (entry.getKey() != null && entry.getValue() != null) {
198                     entryOffsets.add(
199                             createKeyValueEntry(builder, entry.getKey(), entry.getValue()));
200                 }
201             }
202             entriesOffset = KeyValueList.createEntriesVector(builder, Ints.toArray(entryOffsets));
203         }
204         KeyValueList.startKeyValueList(builder);
205         KeyValueList.addEntries(builder, entriesOffset);
206         return KeyValueList.endKeyValueList(builder);
207     }
208 
createKeyValueEntry(FlatBufferBuilder builder, String key, Object value)209     private static int createKeyValueEntry(FlatBufferBuilder builder, String key, Object value) {
210         int valueOffset = 0;
211         if (value instanceof String) {
212             valueOffset = builder.createString((String) value);
213         } else if (value instanceof byte[]) {
214             valueOffset = builder.createByteVector((byte[]) value);
215         }
216         int keyOffset = builder.createString(key);
217         KeyValue.startKeyValue(builder);
218         KeyValue.addKey(builder, keyOffset);
219         if (value instanceof Byte) {
220             KeyValue.addType(builder, DATA_TYPE_BYTE);
221             KeyValue.addByteValue(builder, ((Byte) value).byteValue());
222         } else if (value instanceof Short) {
223             KeyValue.addType(builder, DATA_TYPE_SHORT);
224             KeyValue.addShortValue(builder, ((Short) value).shortValue());
225         } else if (value instanceof Integer) {
226             KeyValue.addType(builder, DATA_TYPE_INT);
227             KeyValue.addIntValue(builder, ((Integer) value).intValue());
228         } else if (value instanceof Long) {
229             KeyValue.addType(builder, DATA_TYPE_LONG);
230             KeyValue.addLongValue(builder, ((Long) value).longValue());
231         } else if (value instanceof Float) {
232             KeyValue.addType(builder, DATA_TYPE_FLOAT);
233             KeyValue.addFloatValue(builder, ((Float) value).floatValue());
234         } else if (value instanceof Double) {
235             KeyValue.addType(builder, DATA_TYPE_DOUBLE);
236             KeyValue.addDoubleValue(builder, ((Double) value).doubleValue());
237         } else if (value instanceof String) {
238             KeyValue.addType(builder, DATA_TYPE_STRING);
239             KeyValue.addStringValue(builder, valueOffset);
240         } else if (value instanceof byte[]) {
241             KeyValue.addType(builder, DATA_TYPE_BLOB);
242             KeyValue.addBlobValue(builder, valueOffset);
243         } else if (value instanceof Boolean) {
244             KeyValue.addType(builder, DATA_TYPE_BOOL);
245             KeyValue.addBoolValue(builder, ((Boolean) value).booleanValue());
246         }
247         return KeyValue.endKeyValue(builder);
248     }
249 
createOwner( FlatBufferBuilder builder, String packageName, String certDigest)250     private static int createOwner(
251             FlatBufferBuilder builder,
252             String packageName,
253             String certDigest) {
254         int packageNameOffset = 0;
255         if (packageName != null) {
256             packageNameOffset = builder.createString(packageName);
257         }
258         int certDigestOffset = 0;
259         if (certDigest != null) {
260             certDigestOffset = builder.createString(certDigest);
261         }
262         Owner.startOwner(builder);
263         Owner.addPackageName(builder, packageNameOffset);
264         Owner.addCertDigest(builder, certDigestOffset);
265         return Owner.endOwner(builder);
266     }
267 }
268 
269