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 package com.android.platform.test.ravenwood.nativesubstitution; 17 18 import java.nio.ByteBuffer; 19 import java.nio.charset.StandardCharsets; 20 import java.util.Arrays; 21 import java.util.Map; 22 import java.util.concurrent.ConcurrentHashMap; 23 import java.util.concurrent.atomic.AtomicLong; 24 25 /** 26 * Tentative, partial implementation of the Parcel native methods, using Java's 27 * {@code byte[]}. 28 * (We don't use a {@link ByteBuffer} because there's enough semantics differences between Parcel 29 * and {@link ByteBuffer}, and it didn't work out. 30 * e.g. Parcel seems to allow moving the data position to be beyond its size? Which 31 * {@link ByteBuffer} wouldn't allow...) 32 */ 33 public class Parcel_host { Parcel_host()34 private Parcel_host() { 35 } 36 37 private static final AtomicLong sNextId = new AtomicLong(1); 38 39 private static final Map<Long, Parcel_host> sInstances = new ConcurrentHashMap<>(); 40 41 private boolean mDeleted = false; 42 43 private byte[] mBuffer; 44 private int mSize; 45 private int mPos; 46 47 private boolean mSensitive; 48 private boolean mAllowFds; 49 50 // TODO Use the actual value from Parcel.java. 51 private static final int OK = 0; 52 validate()53 private void validate() { 54 if (mDeleted) { 55 // TODO: Put more info 56 throw new RuntimeException("Parcel already destroyed"); 57 } 58 } 59 getInstance(long id)60 private static Parcel_host getInstance(long id) { 61 Parcel_host p = sInstances.get(id); 62 if (p == null) { 63 // TODO: Put more info 64 throw new RuntimeException("Parcel doesn't exist with id=" + id); 65 } 66 p.validate(); 67 return p; 68 } 69 nativeCreate()70 public static long nativeCreate() { 71 final long id = sNextId.getAndIncrement(); 72 final Parcel_host p = new Parcel_host(); 73 sInstances.put(id, p); 74 p.init(); 75 return id; 76 } 77 init()78 private void init() { 79 mBuffer = new byte[0]; 80 mSize = 0; 81 mPos = 0; 82 mSensitive = false; 83 mAllowFds = false; 84 } 85 updateSize()86 private void updateSize() { 87 if (mSize < mPos) { 88 mSize = mPos; 89 } 90 } 91 nativeDestroy(long nativePtr)92 public static void nativeDestroy(long nativePtr) { 93 getInstance(nativePtr).mDeleted = true; 94 sInstances.remove(nativePtr); 95 } 96 nativeFreeBuffer(long nativePtr)97 public static void nativeFreeBuffer(long nativePtr) { 98 getInstance(nativePtr).freeBuffer(); 99 } 100 freeBuffer()101 public void freeBuffer() { 102 init(); 103 } 104 getCapacity()105 private int getCapacity() { 106 return mBuffer.length; 107 } 108 ensureMoreCapacity(int size)109 private void ensureMoreCapacity(int size) { 110 ensureCapacity(mPos + size); 111 } 112 ensureCapacity(int targetSize)113 private void ensureCapacity(int targetSize) { 114 if (targetSize <= getCapacity()) { 115 return; 116 } 117 var newSize = getCapacity() * 2; 118 if (newSize < targetSize) { 119 newSize = targetSize; 120 } 121 forceSetCapacity(newSize); 122 } 123 forceSetCapacity(int newSize)124 private void forceSetCapacity(int newSize) { 125 var newBuf = new byte[newSize]; 126 127 // Copy 128 System.arraycopy(mBuffer, 0, newBuf, 0, Math.min(newSize, getCapacity())); 129 130 this.mBuffer = newBuf; 131 } 132 ensureDataAvailable(int requestSize)133 private void ensureDataAvailable(int requestSize) { 134 if (mSize - mPos < requestSize) { 135 throw new RuntimeException(String.format( 136 "Pacel data underflow. size=%d, pos=%d, request=%d", mSize, mPos, requestSize)); 137 } 138 } 139 nativeMarkSensitive(long nativePtr)140 public static void nativeMarkSensitive(long nativePtr) { 141 getInstance(nativePtr).mSensitive = true; 142 } nativeDataSize(long nativePtr)143 public static int nativeDataSize(long nativePtr) { 144 return getInstance(nativePtr).mSize; 145 } nativeDataAvail(long nativePtr)146 public static int nativeDataAvail(long nativePtr) { 147 var p = getInstance(nativePtr); 148 return p.mSize - p.mPos; 149 } nativeDataPosition(long nativePtr)150 public static int nativeDataPosition(long nativePtr) { 151 return getInstance(nativePtr).mPos; 152 } nativeDataCapacity(long nativePtr)153 public static int nativeDataCapacity(long nativePtr) { 154 return getInstance(nativePtr).mBuffer.length; 155 } nativeSetDataSize(long nativePtr, int size)156 public static void nativeSetDataSize(long nativePtr, int size) { 157 var p = getInstance(nativePtr); 158 p.ensureCapacity(size); 159 getInstance(nativePtr).mSize = size; 160 } nativeSetDataPosition(long nativePtr, int pos)161 public static void nativeSetDataPosition(long nativePtr, int pos) { 162 var p = getInstance(nativePtr); 163 // TODO: Should this change the size or the capacity?? 164 p.mPos = pos; 165 } nativeSetDataCapacity(long nativePtr, int size)166 public static void nativeSetDataCapacity(long nativePtr, int size) { 167 if (size < 0) { 168 throw new IllegalArgumentException("size < 0: size=" + size); 169 } 170 var p = getInstance(nativePtr); 171 if (p.getCapacity() < size) { 172 p.forceSetCapacity(size); 173 } 174 } 175 nativePushAllowFds(long nativePtr, boolean allowFds)176 public static boolean nativePushAllowFds(long nativePtr, boolean allowFds) { 177 var p = getInstance(nativePtr); 178 var prev = p.mAllowFds; 179 p.mAllowFds = allowFds; 180 return prev; 181 } nativeRestoreAllowFds(long nativePtr, boolean lastValue)182 public static void nativeRestoreAllowFds(long nativePtr, boolean lastValue) { 183 getInstance(nativePtr).mAllowFds = lastValue; 184 } 185 nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len)186 public static void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len) { 187 nativeWriteBlob(nativePtr, b, offset, len); 188 } 189 nativeWriteBlob(long nativePtr, byte[] b, int offset, int len)190 public static void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len) { 191 var p = getInstance(nativePtr); 192 193 if (b == null) { 194 nativeWriteInt(nativePtr, -1); 195 } else { 196 final var alignedSize = align4(len); 197 198 nativeWriteInt(nativePtr, len); 199 200 p.ensureMoreCapacity(alignedSize); 201 202 System.arraycopy(b, offset, p.mBuffer, p.mPos, len); 203 p.mPos += alignedSize; 204 p.updateSize(); 205 } 206 } 207 nativeWriteInt(long nativePtr, int value)208 public static int nativeWriteInt(long nativePtr, int value) { 209 var p = getInstance(nativePtr); 210 p.ensureMoreCapacity(Integer.BYTES); 211 212 p.mBuffer[p.mPos++] = (byte) ((value >> 24) & 0xff); 213 p.mBuffer[p.mPos++] = (byte) ((value >> 16) & 0xff); 214 p.mBuffer[p.mPos++] = (byte) ((value >> 8) & 0xff); 215 p.mBuffer[p.mPos++] = (byte) ((value >> 0) & 0xff); 216 217 p.updateSize(); 218 219 return OK; 220 } 221 nativeWriteLong(long nativePtr, long value)222 public static int nativeWriteLong(long nativePtr, long value) { 223 nativeWriteInt(nativePtr, (int) (value >>> 32)); 224 nativeWriteInt(nativePtr, (int) (value)); 225 return OK; 226 } nativeWriteFloat(long nativePtr, float val)227 public static int nativeWriteFloat(long nativePtr, float val) { 228 return nativeWriteInt(nativePtr, Float.floatToIntBits(val)); 229 } nativeWriteDouble(long nativePtr, double val)230 public static int nativeWriteDouble(long nativePtr, double val) { 231 return nativeWriteLong(nativePtr, Double.doubleToLongBits(val)); 232 } 233 align4(int val)234 private static int align4(int val) { 235 return ((val + 3) / 4) * 4; 236 } 237 nativeWriteString8(long nativePtr, String val)238 public static void nativeWriteString8(long nativePtr, String val) { 239 if (val == null) { 240 nativeWriteBlob(nativePtr, null, 0, 0); 241 } else { 242 var bytes = val.getBytes(StandardCharsets.UTF_8); 243 nativeWriteBlob(nativePtr, bytes, 0, bytes.length); 244 } 245 } nativeWriteString16(long nativePtr, String val)246 public static void nativeWriteString16(long nativePtr, String val) { 247 // Just reuse String8 248 nativeWriteString8(nativePtr, val); 249 } 250 nativeCreateByteArray(long nativePtr)251 public static byte[] nativeCreateByteArray(long nativePtr) { 252 return nativeReadBlob(nativePtr); 253 } 254 nativeReadByteArray(long nativePtr, byte[] dest, int destLen)255 public static boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen) { 256 if (dest == null) { 257 return false; 258 } 259 var data = nativeReadBlob(nativePtr); 260 if (data == null) { 261 System.err.println("Percel has NULL, which is unexpected."); // TODO: Is this correct? 262 return false; 263 } 264 // TODO: Make sure the check logic is correct. 265 if (data.length != destLen) { 266 System.err.println("Byte array size mismatch: expected=" 267 + data.length + " given=" + destLen); 268 return false; 269 } 270 System.arraycopy(data, 0, dest, 0, data.length); 271 return true; 272 } 273 nativeReadBlob(long nativePtr)274 public static byte[] nativeReadBlob(long nativePtr) { 275 var p = getInstance(nativePtr); 276 if (p.mSize - p.mPos < 4) { 277 // Match native impl that returns "null" when not enough data 278 return null; 279 } 280 final var size = nativeReadInt(nativePtr); 281 if (size == -1) { 282 return null; 283 } 284 try { 285 p.ensureDataAvailable(align4(size)); 286 } catch (Exception e) { 287 System.err.println(e.toString()); 288 return null; 289 } 290 291 var bytes = new byte[size]; 292 System.arraycopy(p.mBuffer, p.mPos, bytes, 0, size); 293 294 p.mPos += align4(size); 295 296 return bytes; 297 } nativeReadInt(long nativePtr)298 public static int nativeReadInt(long nativePtr) { 299 var p = getInstance(nativePtr); 300 301 if (p.mSize - p.mPos < 4) { 302 // Match native impl that returns "0" when not enough data 303 return 0; 304 } 305 306 var ret = (((p.mBuffer[p.mPos++] & 0xff) << 24) 307 | ((p.mBuffer[p.mPos++] & 0xff) << 16) 308 | ((p.mBuffer[p.mPos++] & 0xff) << 8) 309 | ((p.mBuffer[p.mPos++] & 0xff) << 0)); 310 311 return ret; 312 } nativeReadLong(long nativePtr)313 public static long nativeReadLong(long nativePtr) { 314 return (((long) nativeReadInt(nativePtr)) << 32) 315 | (((long) nativeReadInt(nativePtr)) & 0xffff_ffffL); 316 } 317 nativeReadFloat(long nativePtr)318 public static float nativeReadFloat(long nativePtr) { 319 return Float.intBitsToFloat(nativeReadInt(nativePtr)); 320 } 321 nativeReadDouble(long nativePtr)322 public static double nativeReadDouble(long nativePtr) { 323 return Double.longBitsToDouble(nativeReadLong(nativePtr)); 324 } 325 nativeReadString8(long nativePtr)326 public static String nativeReadString8(long nativePtr) { 327 final var bytes = nativeReadBlob(nativePtr); 328 if (bytes == null) { 329 return null; 330 } 331 return new String(bytes, StandardCharsets.UTF_8); 332 } nativeReadString16(long nativePtr)333 public static String nativeReadString16(long nativePtr) { 334 return nativeReadString8(nativePtr); 335 } 336 nativeMarshall(long nativePtr)337 public static byte[] nativeMarshall(long nativePtr) { 338 var p = getInstance(nativePtr); 339 return Arrays.copyOf(p.mBuffer, p.mSize); 340 } nativeUnmarshall( long nativePtr, byte[] data, int offset, int length)341 public static void nativeUnmarshall( 342 long nativePtr, byte[] data, int offset, int length) { 343 var p = getInstance(nativePtr); 344 p.ensureMoreCapacity(length); 345 System.arraycopy(data, offset, p.mBuffer, p.mPos, length); 346 p.mPos += length; 347 p.updateSize(); 348 } nativeCompareData(long thisNativePtr, long otherNativePtr)349 public static int nativeCompareData(long thisNativePtr, long otherNativePtr) { 350 var a = getInstance(thisNativePtr); 351 var b = getInstance(otherNativePtr); 352 if ((a.mSize == b.mSize) && Arrays.equals(a.mBuffer, b.mBuffer)) { 353 return 0; 354 } else { 355 return -1; 356 } 357 } nativeCompareDataInRange( long ptrA, int offsetA, long ptrB, int offsetB, int length)358 public static boolean nativeCompareDataInRange( 359 long ptrA, int offsetA, long ptrB, int offsetB, int length) { 360 var a = getInstance(ptrA); 361 var b = getInstance(ptrB); 362 if (offsetA < 0 || offsetA + length > a.mSize) { 363 throw new IllegalArgumentException(); 364 } 365 if (offsetB < 0 || offsetB + length > b.mSize) { 366 throw new IllegalArgumentException(); 367 } 368 return Arrays.equals(Arrays.copyOfRange(a.mBuffer, offsetA, offsetA + length), 369 Arrays.copyOfRange(b.mBuffer, offsetB, offsetB + length)); 370 } nativeAppendFrom( long thisNativePtr, long otherNativePtr, int srcOffset, int length)371 public static void nativeAppendFrom( 372 long thisNativePtr, long otherNativePtr, int srcOffset, int length) { 373 var dst = getInstance(thisNativePtr); 374 var src = getInstance(otherNativePtr); 375 376 dst.ensureMoreCapacity(length); 377 378 System.arraycopy(src.mBuffer, srcOffset, dst.mBuffer, dst.mPos, length); 379 dst.mPos += length; // TODO: 4 byte align? 380 dst.updateSize(); 381 382 // TODO: Update the other's position? 383 } 384 nativeHasFileDescriptors(long nativePtr)385 public static boolean nativeHasFileDescriptors(long nativePtr) { 386 // Assume false for now, because we don't support writing FDs yet. 387 return false; 388 } 389 nativeHasFileDescriptorsInRange( long nativePtr, int offset, int length)390 public static boolean nativeHasFileDescriptorsInRange( 391 long nativePtr, int offset, int length) { 392 // Assume false for now, because we don't support writing FDs yet. 393 return false; 394 } 395 nativeHasBinders(long nativePtr)396 public static boolean nativeHasBinders(long nativePtr) { 397 // Assume false for now, because we don't support adding binders. 398 return false; 399 } 400 nativeHasBindersInRange( long nativePtr, int offset, int length)401 public static boolean nativeHasBindersInRange( 402 long nativePtr, int offset, int length) { 403 // Assume false for now, because we don't support writing FDs yet. 404 return false; 405 } 406 } 407