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