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 android.health.connect.internal; 18 19 import android.annotation.NonNull; 20 import android.os.IBinder; 21 import android.os.Parcel; 22 import android.os.SharedMemory; 23 import android.system.ErrnoException; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 27 import java.nio.ByteBuffer; 28 29 /** @hide */ 30 public final class ParcelUtils { 31 @VisibleForTesting public static final int USING_SHARED_MEMORY = 0; 32 @VisibleForTesting public static final int USING_PARCEL = 1; 33 34 @VisibleForTesting 35 public static final int IPC_PARCEL_LIMIT = IBinder.getSuggestedMaxIpcSizeBytes() / 2; 36 37 public interface IPutToParcelRunnable { writeToParcel(Parcel dest)38 void writeToParcel(Parcel dest); 39 } 40 41 @NonNull getParcelForSharedMemoryIfRequired(Parcel in)42 public static Parcel getParcelForSharedMemoryIfRequired(Parcel in) { 43 int parcelType = in.readInt(); 44 if (parcelType == USING_SHARED_MEMORY) { 45 try (SharedMemory memory = SharedMemory.CREATOR.createFromParcel(in)) { 46 Parcel dataParcel = Parcel.obtain(); 47 ByteBuffer buffer = memory.mapReadOnly(); 48 byte[] payload = new byte[buffer.limit()]; 49 buffer.get(payload); 50 dataParcel.unmarshall(payload, 0, payload.length); 51 dataParcel.setDataPosition(0); 52 return dataParcel; 53 } catch (ErrnoException e) { 54 throw new RuntimeException(e); 55 } 56 } 57 return in; 58 } 59 getSharedMemoryForParcel(Parcel dataParcel, int dataParcelSize)60 public static SharedMemory getSharedMemoryForParcel(Parcel dataParcel, int dataParcelSize) { 61 try { 62 SharedMemory sharedMemory = 63 SharedMemory.create("RecordsParcelSharedMemory", dataParcelSize); 64 ByteBuffer buffer = sharedMemory.mapReadWrite(); 65 byte[] data = dataParcel.marshall(); 66 buffer.put(data, 0, dataParcelSize); 67 return sharedMemory; 68 } catch (ErrnoException e) { 69 throw new RuntimeException(e); 70 } 71 } 72 73 /** 74 * Determines which memory to use and puts the {@code parcel} in it, and details of it in {@code 75 * dest} 76 */ putToRequiredMemory( Parcel dest, int flags, IPutToParcelRunnable parcelRunnable)77 public static void putToRequiredMemory( 78 Parcel dest, int flags, IPutToParcelRunnable parcelRunnable) { 79 final Parcel dataParcel = Parcel.obtain(); 80 try { 81 parcelRunnable.writeToParcel(dataParcel); 82 final int dataParcelSize = dataParcel.dataSize(); 83 if (dataParcelSize > IPC_PARCEL_LIMIT) { 84 try (SharedMemory sharedMemory = 85 ParcelUtils.getSharedMemoryForParcel(dataParcel, dataParcelSize)) { 86 dest.writeInt(USING_SHARED_MEMORY); 87 sharedMemory.writeToParcel(dest, flags); 88 } 89 } else { 90 dest.writeInt(USING_PARCEL); 91 parcelRunnable.writeToParcel(dest); 92 } 93 } finally { 94 dataParcel.recycle(); 95 } 96 } 97 } 98