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