Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 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 | |
| 17 | package android.os.incremental; |
| 18 | |
| 19 | import android.annotation.NonNull; |
| 20 | import android.annotation.Nullable; |
| 21 | import android.os.RemoteException; |
| 22 | |
| 23 | import java.io.File; |
| 24 | import java.io.IOException; |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 25 | import java.nio.ByteBuffer; |
| 26 | import java.util.UUID; |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 27 | |
| 28 | /** |
Songchun Fan | f5c894f | 2019-11-29 15:43:58 -0800 | [diff] [blame] | 29 | * Provides operations on an Incremental File System directory, using IncrementalServiceNative. |
| 30 | * Example usage: |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 31 | * |
| 32 | * <blockquote><pre> |
Songchun Fan | f5c894f | 2019-11-29 15:43:58 -0800 | [diff] [blame] | 33 | * IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_SERVICE); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 34 | * IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir"); |
| 35 | * storage.makeDirectory("subdir"); |
| 36 | * </pre></blockquote> |
| 37 | * |
| 38 | * @hide |
| 39 | */ |
| 40 | public final class IncrementalStorage { |
| 41 | private static final String TAG = "IncrementalStorage"; |
| 42 | private final int mId; |
Songchun Fan | f5c894f | 2019-11-29 15:43:58 -0800 | [diff] [blame] | 43 | private final IIncrementalManagerNative mService; |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 44 | |
| 45 | |
Songchun Fan | f5c894f | 2019-11-29 15:43:58 -0800 | [diff] [blame] | 46 | public IncrementalStorage(@NonNull IIncrementalManagerNative is, int id) { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 47 | mService = is; |
| 48 | mId = id; |
| 49 | } |
| 50 | |
| 51 | public int getId() { |
| 52 | return mId; |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * Temporarily bind-mounts the current storage directory to a target directory. The bind-mount |
| 57 | * will NOT be preserved between device reboots. |
| 58 | * |
| 59 | * @param targetPath Absolute path to the target directory. |
| 60 | */ |
| 61 | public void bind(@NonNull String targetPath) throws IOException { |
| 62 | bind("", targetPath); |
| 63 | } |
| 64 | |
| 65 | /** |
| 66 | * Temporarily bind-mounts a subdir under the current storage directory to a target directory. |
| 67 | * The bind-mount will NOT be preserved between device reboots. |
| 68 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 69 | * @param sourcePath Source path as a relative path under current storage |
| 70 | * directory. |
| 71 | * @param targetPath Absolute path to the target directory. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 72 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 73 | public void bind(@NonNull String sourcePath, @NonNull String targetPath) |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 74 | throws IOException { |
| 75 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 76 | int res = mService.makeBindMount(mId, sourcePath, targetPath, |
Songchun Fan | f5c894f | 2019-11-29 15:43:58 -0800 | [diff] [blame] | 77 | IIncrementalManagerNative.BIND_TEMPORARY); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 78 | if (res < 0) { |
| 79 | throw new IOException("bind() failed with errno " + -res); |
| 80 | } |
| 81 | } catch (RemoteException e) { |
| 82 | e.rethrowFromSystemServer(); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | |
| 87 | /** |
| 88 | * Permanently bind-mounts the current storage directory to a target directory. The bind-mount |
| 89 | * WILL be preserved between device reboots. |
| 90 | * |
| 91 | * @param targetPath Absolute path to the target directory. |
| 92 | */ |
| 93 | public void bindPermanent(@NonNull String targetPath) throws IOException { |
| 94 | bindPermanent("", targetPath); |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * Permanently bind-mounts a subdir under the current storage directory to a target directory. |
| 99 | * The bind-mount WILL be preserved between device reboots. |
| 100 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 101 | * @param sourcePath Relative path under the current storage directory. |
| 102 | * @param targetPath Absolute path to the target directory. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 103 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 104 | public void bindPermanent(@NonNull String sourcePath, @NonNull String targetPath) |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 105 | throws IOException { |
| 106 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 107 | int res = mService.makeBindMount(mId, sourcePath, targetPath, |
Songchun Fan | f5c894f | 2019-11-29 15:43:58 -0800 | [diff] [blame] | 108 | IIncrementalManagerNative.BIND_PERMANENT); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 109 | if (res < 0) { |
| 110 | throw new IOException("bind() permanent failed with errno " + -res); |
| 111 | } |
| 112 | } catch (RemoteException e) { |
| 113 | e.rethrowFromSystemServer(); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /** |
| 118 | * Unbinds a bind mount. |
| 119 | * |
| 120 | * @param targetPath Absolute path to the target directory. |
| 121 | */ |
| 122 | public void unBind(@NonNull String targetPath) throws IOException { |
| 123 | try { |
| 124 | int res = mService.deleteBindMount(mId, targetPath); |
| 125 | if (res < 0) { |
| 126 | throw new IOException("unbind() failed with errno " + -res); |
| 127 | } |
| 128 | } catch (RemoteException e) { |
| 129 | e.rethrowFromSystemServer(); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | /** |
| 134 | * Creates a sub-directory under the current storage directory. |
| 135 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 136 | * @param path Relative path of the sub-directory, e.g., "subdir" |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 137 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 138 | public void makeDirectory(@NonNull String path) throws IOException { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 139 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 140 | int res = mService.makeDirectory(mId, path); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 141 | if (res < 0) { |
| 142 | throw new IOException("makeDirectory() failed with errno " + -res); |
| 143 | } |
| 144 | } catch (RemoteException e) { |
| 145 | e.rethrowFromSystemServer(); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Creates a sub-directory under the current storage directory. If its parent dirs do not exist, |
| 151 | * create the parent dirs as well. |
| 152 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 153 | * @param path Full path. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 154 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 155 | public void makeDirectories(@NonNull String path) throws IOException { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 156 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 157 | int res = mService.makeDirectories(mId, path); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 158 | if (res < 0) { |
| 159 | throw new IOException("makeDirectory() failed with errno " + -res); |
| 160 | } |
| 161 | } catch (RemoteException e) { |
| 162 | e.rethrowFromSystemServer(); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Creates a file under the current storage directory. |
| 168 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 169 | * @param path Relative path of the new file. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 170 | * @param size Size of the new file in bytes. |
| 171 | * @param metadata Metadata bytes. |
| 172 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 173 | public void makeFile(@NonNull String path, long size, @Nullable UUID id, |
| 174 | @Nullable byte[] metadata, int hashAlgorithm, @Nullable byte[] rootHash, |
| 175 | @Nullable byte[] additionalData, @Nullable byte[] signature) throws IOException { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 176 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 177 | final IncrementalNewFileParams params = new IncrementalNewFileParams(); |
| 178 | params.size = size; |
| 179 | params.metadata = metadata; |
| 180 | params.fileId = idToBytes(id); |
| 181 | if (hashAlgorithm != 0 || signature != null) { |
| 182 | params.signature = new IncrementalSignature(); |
| 183 | params.signature.hashAlgorithm = hashAlgorithm; |
| 184 | params.signature.rootHash = rootHash; |
| 185 | params.signature.additionalData = additionalData; |
| 186 | params.signature.signature = signature; |
| 187 | } |
| 188 | int res = mService.makeFile(mId, path, params); |
| 189 | if (res != 0) { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 190 | throw new IOException("makeFile() failed with errno " + -res); |
| 191 | } |
| 192 | } catch (RemoteException e) { |
| 193 | e.rethrowFromSystemServer(); |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | /** |
| 198 | * Creates a file in Incremental storage. The content of the file is mapped from a range inside |
| 199 | * a source file in the same storage. |
| 200 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 201 | * @param destPath Target full path. |
| 202 | * @param sourcePath Source full path. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 203 | * @param rangeStart Starting offset (in bytes) in the source file. |
| 204 | * @param rangeEnd Ending offset (in bytes) in the source file. |
| 205 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 206 | public void makeFileFromRange(@NonNull String destPath, |
| 207 | @NonNull String sourcePath, long rangeStart, long rangeEnd) throws IOException { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 208 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 209 | int res = mService.makeFileFromRange(mId, destPath, sourcePath, |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 210 | rangeStart, rangeEnd); |
| 211 | if (res < 0) { |
| 212 | throw new IOException("makeFileFromRange() failed, errno " + -res); |
| 213 | } |
| 214 | } catch (RemoteException e) { |
| 215 | e.rethrowFromSystemServer(); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Creates a hard-link between two paths, which can be under different storages but in the same |
| 221 | * Incremental File System. |
| 222 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 223 | * @param sourcePath The absolute path of the source. |
| 224 | * @param destStorage The target storage of the link target. |
| 225 | * @param destPath The absolute path of the target. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 226 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 227 | public void makeLink(@NonNull String sourcePath, IncrementalStorage destStorage, |
| 228 | @NonNull String destPath) throws IOException { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 229 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 230 | int res = mService.makeLink(mId, sourcePath, destStorage.getId(), |
| 231 | destPath); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 232 | if (res < 0) { |
| 233 | throw new IOException("makeLink() failed with errno " + -res); |
| 234 | } |
| 235 | } catch (RemoteException e) { |
| 236 | e.rethrowFromSystemServer(); |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | /** |
| 241 | * Deletes a hard-link under the current storage directory. |
| 242 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 243 | * @param path The absolute path of the target. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 244 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 245 | public void unlink(@NonNull String path) throws IOException { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 246 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 247 | int res = mService.unlink(mId, path); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 248 | if (res < 0) { |
| 249 | throw new IOException("unlink() failed with errno " + -res); |
| 250 | } |
| 251 | } catch (RemoteException e) { |
| 252 | e.rethrowFromSystemServer(); |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | /** |
| 257 | * Rename an old file name to a new file name under the current storage directory. |
| 258 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 259 | * @param sourcepath Old file path as a full path to the storage directory. |
| 260 | * @param destpath New file path as a full path to the storage directory. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 261 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 262 | public void moveFile(@NonNull String sourcepath, |
| 263 | @NonNull String destpath) throws IOException { |
| 264 | //TODO(zyy): implement using rename(2) when confirmed that IncFS supports it. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 265 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 266 | int res = mService.makeLink(mId, sourcepath, mId, destpath); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 267 | if (res < 0) { |
| 268 | throw new IOException("moveFile() failed at makeLink(), errno " + -res); |
| 269 | } |
| 270 | } catch (RemoteException e) { |
| 271 | e.rethrowFromSystemServer(); |
| 272 | } |
| 273 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 274 | mService.unlink(mId, sourcepath); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 275 | } catch (RemoteException ignored) { |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | /** |
| 280 | * Move a directory, which is bind-mounted to a given storage, to a new location. The bind mount |
| 281 | * will be persistent between reboots. |
| 282 | * |
| 283 | * @param sourcePath The old path of the directory as an absolute path. |
| 284 | * @param destPath The new path of the directory as an absolute path, expected to already |
| 285 | * exist. |
| 286 | */ |
| 287 | public void moveDir(@NonNull String sourcePath, @NonNull String destPath) throws IOException { |
| 288 | if (!new File(destPath).exists()) { |
| 289 | throw new IOException("moveDir() requires that destination dir already exists."); |
| 290 | } |
| 291 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 292 | int res = mService.makeBindMount(mId, sourcePath, destPath, |
Songchun Fan | f5c894f | 2019-11-29 15:43:58 -0800 | [diff] [blame] | 293 | IIncrementalManagerNative.BIND_PERMANENT); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 294 | if (res < 0) { |
| 295 | throw new IOException("moveDir() failed at making bind mount, errno " + -res); |
| 296 | } |
| 297 | } catch (RemoteException e) { |
| 298 | e.rethrowFromSystemServer(); |
| 299 | } |
| 300 | try { |
| 301 | mService.deleteBindMount(mId, sourcePath); |
| 302 | } catch (RemoteException ignored) { |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | /** |
| 307 | * Checks whether a file under the current storage directory is fully loaded. |
| 308 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 309 | * @param path The relative path of the file. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 310 | * @return True if the file is fully loaded. |
| 311 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 312 | public boolean isFileFullyLoaded(@NonNull String path) { |
| 313 | return isFileRangeLoaded(path, 0, -1); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 314 | } |
| 315 | |
| 316 | /** |
| 317 | * Checks whether a range in a file if loaded. |
| 318 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 319 | * @param path The relative path of the file. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 320 | * @param start The starting offset of the range. |
| 321 | * @param end The ending offset of the range. |
| 322 | * @return True if the file is fully loaded. |
| 323 | */ |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 324 | public boolean isFileRangeLoaded(@NonNull String path, long start, long end) { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 325 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 326 | return mService.isFileRangeLoaded(mId, path, start, end); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 327 | } catch (RemoteException e) { |
| 328 | e.rethrowFromSystemServer(); |
| 329 | return false; |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | /** |
| 334 | * Returns the metadata object of an IncFs File. |
| 335 | * |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 336 | * @param path The relative path of the file. |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 337 | * @return Byte array that contains metadata bytes. |
| 338 | */ |
| 339 | @Nullable |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 340 | public byte[] getFileMetadata(@NonNull String path) { |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 341 | try { |
Yurii Zubrytskyi | 4a25dfb | 2020-01-10 11:53:24 -0800 | [diff] [blame] | 342 | return mService.getMetadataByPath(mId, path); |
| 343 | } catch (RemoteException e) { |
| 344 | e.rethrowFromSystemServer(); |
| 345 | return null; |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | private static final int UUID_BYTE_SIZE = 16; |
| 350 | |
| 351 | /** |
| 352 | * Converts UUID to a byte array usable for Incremental API calls |
| 353 | * |
| 354 | * @param id The id to convert |
| 355 | * @return Byte array that contains the same ID. |
| 356 | */ |
| 357 | public static byte[] idToBytes(UUID id) { |
| 358 | if (id == null) { |
| 359 | return null; |
| 360 | } |
| 361 | final ByteBuffer buf = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]); |
| 362 | buf.putLong(id.getMostSignificantBits()); |
| 363 | buf.putLong(id.getLeastSignificantBits()); |
| 364 | return buf.array(); |
| 365 | } |
| 366 | |
| 367 | /** |
| 368 | * Converts UUID from a byte array usable for Incremental API calls |
| 369 | * |
| 370 | * @param bytes The id in byte array format, 16 bytes long |
| 371 | * @return UUID constructed from the byte array. |
| 372 | */ |
| 373 | public static UUID bytesToId(byte[] bytes) { |
| 374 | if (bytes.length != UUID_BYTE_SIZE) { |
| 375 | throw new IllegalArgumentException("Expected array of size " + UUID_BYTE_SIZE |
| 376 | + ", got " + bytes.length); |
| 377 | } |
| 378 | final ByteBuffer buf = ByteBuffer.wrap(bytes); |
| 379 | long msb = buf.getLong(); |
| 380 | long lsb = buf.getLong(); |
| 381 | return new UUID(msb, lsb); |
| 382 | } |
| 383 | |
| 384 | /** |
| 385 | * Returns the metadata object of an IncFs File. |
| 386 | * |
| 387 | * @param id The file id. |
| 388 | * @return Byte array that contains metadata bytes. |
| 389 | */ |
| 390 | @Nullable |
| 391 | public byte[] getFileMetadata(@NonNull UUID id) { |
| 392 | try { |
| 393 | final byte[] rawId = idToBytes(id); |
| 394 | return mService.getMetadataById(mId, rawId); |
Songchun Fan | d1b41d4 | 2019-11-12 17:09:53 -0800 | [diff] [blame] | 395 | } catch (RemoteException e) { |
| 396 | e.rethrowFromSystemServer(); |
| 397 | return null; |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | /** |
| 402 | * Informs the data loader service associated with the current storage to start data loader |
| 403 | * |
| 404 | * @return True if data loader is successfully started. |
| 405 | */ |
| 406 | public boolean startLoading() { |
| 407 | try { |
| 408 | return mService.startLoading(mId); |
| 409 | } catch (RemoteException e) { |
| 410 | e.rethrowFromSystemServer(); |
| 411 | return false; |
| 412 | } |
| 413 | } |
| 414 | } |