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