1 /* 2 * Copyright (C) 2019 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 android.os; 17 18 import static android.system.OsConstants.MAP_SHARED; 19 import static android.system.OsConstants.PROT_READ; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SuppressLint; 24 import android.system.ErrnoException; 25 import android.system.Os; 26 import android.util.Log; 27 28 import com.android.internal.util.Preconditions; 29 30 import java.io.FileDescriptor; 31 import java.nio.ByteBuffer; 32 import java.nio.DirectByteBuffer; 33 import java.util.ArrayList; 34 import java.util.List; 35 36 /** 37 * Provides utilities for dealing with HidlMemory. 38 * 39 * @hide 40 */ 41 public final class HidlMemoryUtil { 42 static private final String TAG = "HidlMemoryUtil"; 43 HidlMemoryUtil()44 private HidlMemoryUtil() { 45 } 46 47 /** 48 * Copies a byte-array into a new Ashmem region and return it as HidlMemory. 49 * The returned instance owns the underlying file descriptors, and the client should generally 50 * call close on it when no longer in use (or otherwise, when the object gets destroyed it will 51 * be closed). 52 * 53 * @param input The input byte array. 54 * @return A HidlMemory instance, containing a copy of the input. 55 */ 56 public static @NonNull byteArrayToHidlMemory(@onNull byte[] input)57 HidlMemory byteArrayToHidlMemory(@NonNull byte[] input) { 58 return byteArrayToHidlMemory(input, null); 59 } 60 61 /** 62 * Copies a byte-array into a new Ashmem region and return it as HidlMemory. 63 * The returned instance owns the underlying file descriptors, and the client should generally 64 * call close on it when no longer in use (or otherwise, when the object gets destroyed it will 65 * be closed). 66 * 67 * @param input The input byte array. 68 * @param name An optional name for the ashmem region. 69 * @return A HidlMemory instance, containing a copy of the input. 70 */ 71 public static @NonNull byteArrayToHidlMemory(@onNull byte[] input, @Nullable String name)72 HidlMemory byteArrayToHidlMemory(@NonNull byte[] input, @Nullable String name) { 73 Preconditions.checkNotNull(input); 74 75 if (input.length == 0) { 76 return new HidlMemory("ashmem", 0, null); 77 } 78 79 try (SharedMemory shmem = SharedMemory.create(name != null ? name : "", input.length)) { 80 ByteBuffer buffer = shmem.mapReadWrite(); 81 buffer.put(input); 82 shmem.unmap(buffer); 83 return sharedMemoryToHidlMemory(shmem); 84 } catch (ErrnoException e) { 85 throw new RuntimeException(e); 86 } 87 } 88 89 /** 90 * Copies a byte list into a new Ashmem region and return it as HidlMemory. 91 * The returned instance owns the underlying file descriptors, and the client should generally 92 * call close on it when no longer in use (or otherwise, when the object gets destroyed it will 93 * be closed). 94 * 95 * @param input The input byte list. 96 * @return A HidlMemory instance, containing a copy of the input. 97 */ 98 public static @NonNull byteListToHidlMemory(@onNull List<Byte> input)99 HidlMemory byteListToHidlMemory(@NonNull List<Byte> input) { 100 return byteListToHidlMemory(input, null); 101 } 102 103 /** 104 * Copies a byte list into a new Ashmem region and return it as HidlMemory. 105 * The returned instance owns the underlying file descriptors, and the client should generally 106 * call close on it when no longer in use (or otherwise, when the object gets destroyed it will 107 * be closed). 108 * 109 * @param input The input byte list. 110 * @param name An optional name for the ashmem region. 111 * @return A HidlMemory instance, containing a copy of the input. 112 */ 113 public static @NonNull byteListToHidlMemory(@onNull List<Byte> input, @Nullable String name)114 HidlMemory byteListToHidlMemory(@NonNull List<Byte> input, @Nullable String name) { 115 Preconditions.checkNotNull(input); 116 117 if (input.isEmpty()) { 118 return new HidlMemory("ashmem", 0, null); 119 } 120 121 try (SharedMemory shmem = SharedMemory.create(name != null ? name : "", input.size())) { 122 ByteBuffer buffer = shmem.mapReadWrite(); 123 for (Byte b : input) { 124 buffer.put(b); 125 } 126 shmem.unmap(buffer); 127 return sharedMemoryToHidlMemory(shmem); 128 } catch (ErrnoException e) { 129 throw new RuntimeException(e); 130 } 131 } 132 133 /** 134 * Copies all data from a HidlMemory instance into a byte array. 135 * 136 * @param mem The HidlMemory instance. Must be of name "ashmem" and of size that doesn't exceed 137 * {@link Integer#MAX_VALUE}. 138 * @return A byte array, containing a copy of the input. 139 */ 140 public static @NonNull hidlMemoryToByteArray(@onNull HidlMemory mem)141 byte[] hidlMemoryToByteArray(@NonNull HidlMemory mem) { 142 Preconditions.checkNotNull(mem); 143 Preconditions.checkArgumentInRange(mem.getSize(), 0L, (long) Integer.MAX_VALUE, 144 "Memory size"); 145 Preconditions.checkArgument(mem.getSize() == 0 || mem.getName().equals("ashmem"), 146 "Unsupported memory type: %s", mem.getName()); 147 148 if (mem.getSize() == 0) { 149 return new byte[0]; 150 } 151 152 ByteBuffer buffer = getBuffer(mem); 153 byte[] result = new byte[buffer.remaining()]; 154 buffer.get(result); 155 return result; 156 } 157 158 /** 159 * Copies all data from a HidlMemory instance into a byte list. 160 * 161 * @param mem The HidlMemory instance. Must be of name "ashmem" and of size that doesn't exceed 162 * {@link Integer#MAX_VALUE}. 163 * @return A byte list, containing a copy of the input. 164 */ 165 @SuppressLint("ConcreteCollection") 166 public static @NonNull hidlMemoryToByteList(@onNull HidlMemory mem)167 ArrayList<Byte> hidlMemoryToByteList(@NonNull HidlMemory mem) { 168 Preconditions.checkNotNull(mem); 169 Preconditions.checkArgumentInRange(mem.getSize(), 0L, (long) Integer.MAX_VALUE, 170 "Memory size"); 171 Preconditions.checkArgument(mem.getSize() == 0 || mem.getName().equals("ashmem"), 172 "Unsupported memory type: %s", mem.getName()); 173 174 if (mem.getSize() == 0) { 175 return new ArrayList<>(); 176 } 177 178 ByteBuffer buffer = getBuffer(mem); 179 180 ArrayList<Byte> result = new ArrayList<>(buffer.remaining()); 181 while (buffer.hasRemaining()) { 182 result.add(buffer.get()); 183 } 184 return result; 185 } 186 187 /** 188 * Converts a SharedMemory to a HidlMemory without copying. 189 * 190 * @param shmem The shared memory object. Null means "empty" and will still result in a non-null 191 * return value. 192 * @return The HidlMemory instance. 193 */ sharedMemoryToHidlMemory(@ullable SharedMemory shmem)194 @NonNull public static HidlMemory sharedMemoryToHidlMemory(@Nullable SharedMemory shmem) { 195 if (shmem == null) { 196 return new HidlMemory("ashmem", 0, null); 197 } 198 return fileDescriptorToHidlMemory(shmem.getFileDescriptor(), shmem.getSize()); 199 } 200 201 /** 202 * Converts a FileDescriptor to a HidlMemory without copying. 203 * 204 * @param fd The FileDescriptor object. Null is allowed if size is 0 and will still result in 205 * a non-null return value. 206 * @param size The size of the memory buffer. 207 * @return The HidlMemory instance. 208 */ fileDescriptorToHidlMemory(@ullable FileDescriptor fd, int size)209 @NonNull public static HidlMemory fileDescriptorToHidlMemory(@Nullable FileDescriptor fd, 210 int size) { 211 Preconditions.checkArgument(fd != null || size == 0); 212 if (fd == null) { 213 return new HidlMemory("ashmem", 0, null); 214 } 215 try { 216 NativeHandle handle = new NativeHandle(Os.dup(fd), true); 217 return new HidlMemory("ashmem", size, handle); 218 } catch (ErrnoException e) { 219 throw new RuntimeException(e); 220 } 221 } 222 getBuffer(@onNull HidlMemory mem)223 private static ByteBuffer getBuffer(@NonNull HidlMemory mem) { 224 try { 225 final int size = (int) mem.getSize(); 226 227 if (size == 0) { 228 return ByteBuffer.wrap(new byte[0]); 229 } 230 231 NativeHandle handle = mem.getHandle(); 232 233 final long address = Os.mmap(0, size, PROT_READ, MAP_SHARED, handle.getFileDescriptor(), 234 0); 235 return new DirectByteBuffer(size, address, handle.getFileDescriptor(), () -> { 236 try { 237 Os.munmap(address, size); 238 } catch (ErrnoException e) { 239 Log.wtf(TAG, e); 240 } 241 }, true); 242 } catch (ErrnoException e) { 243 throw new RuntimeException(e); 244 } 245 } 246 } 247