BitmapFactory.java revision 25ba1c86945a441428194d9ebcabbf31be75a45a
1/* 2 * Copyright (C) 2007 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 17package android.graphics; 18 19import android.content.res.AssetManager; 20import android.content.res.Resources; 21import android.os.Trace; 22import android.util.DisplayMetrics; 23import android.util.Log; 24import android.util.TypedValue; 25 26import java.io.BufferedInputStream; 27import java.io.FileDescriptor; 28import java.io.FileInputStream; 29import java.io.IOException; 30import java.io.InputStream; 31 32/** 33 * Creates Bitmap objects from various sources, including files, streams, 34 * and byte-arrays. 35 */ 36public class BitmapFactory { 37 private static final int DECODE_BUFFER_SIZE = 16 * 1024; 38 39 public static class Options { 40 /** 41 * Create a default Options object, which if left unchanged will give 42 * the same result from the decoder as if null were passed. 43 */ 44 public Options() { 45 inDither = false; 46 inScaled = true; 47 inPremultiplied = true; 48 } 49 50 /** 51 * If set, decode methods that take the Options object will attempt to 52 * reuse this bitmap when loading content. If the decode operation 53 * cannot use this bitmap, the decode method will return 54 * <code>null</code> and will throw an IllegalArgumentException. The 55 * current implementation necessitates that the reused bitmap be 56 * mutable, and the resulting reused bitmap will continue to remain 57 * mutable even when decoding a resource which would normally result in 58 * an immutable bitmap.</p> 59 * 60 * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, any 61 * mutable bitmap can be reused to decode any other bitmaps as long as 62 * the resulting {@link Bitmap#getByteCount() byte count} of the decoded 63 * bitmap is less than or equal to the {@link 64 * Bitmap#getAllocationByteCount() allocated byte count} of the reused 65 * bitmap. This can be because the intrinsic size is smaller, or its 66 * size post scaling (for density / sample size) is smaller.</p> 67 * 68 * <p>Prior to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE} 69 * additional constraints apply: The image being decoded (whether as a 70 * resource or as a stream) must be in jpeg or png format. Only equal 71 * sized bitmaps are supported, with {@link #inSampleSize} set to 1. 72 * Additionally, the {@link android.graphics.Bitmap.Config 73 * configuration} of the reused bitmap will override the setting of 74 * {@link #inPreferredConfig}, if set.</p> 75 * 76 * <p>You should still always use the returned Bitmap of the decode 77 * method and not assume that reusing the bitmap worked, due to the 78 * constraints outlined above and failure situations that can occur. 79 * Checking whether the return value matches the value of the inBitmap 80 * set in the Options structure will indicate if the bitmap was reused, 81 * but in all cases you should use the Bitmap returned by the decoding 82 * function to ensure that you are using the bitmap that was used as the 83 * decode destination.</p> 84 * 85 * @see Bitmap#reconfigure(int,int, android.graphics.Bitmap.Config) 86 */ 87 public Bitmap inBitmap; 88 89 /** 90 * If set, decode methods will always return a mutable Bitmap instead of 91 * an immutable one. This can be used for instance to programmatically apply 92 * effects to a Bitmap loaded through BitmapFactory. 93 */ 94 @SuppressWarnings({"UnusedDeclaration"}) // used in native code 95 public boolean inMutable; 96 97 /** 98 * If set to true, the decoder will return null (no bitmap), but 99 * the out... fields will still be set, allowing the caller to query 100 * the bitmap without having to allocate the memory for its pixels. 101 */ 102 public boolean inJustDecodeBounds; 103 104 /** 105 * If set to a value > 1, requests the decoder to subsample the original 106 * image, returning a smaller image to save memory. The sample size is 107 * the number of pixels in either dimension that correspond to a single 108 * pixel in the decoded bitmap. For example, inSampleSize == 4 returns 109 * an image that is 1/4 the width/height of the original, and 1/16 the 110 * number of pixels. Any value <= 1 is treated the same as 1. Note: the 111 * decoder uses a final value based on powers of 2, any other value will 112 * be rounded down to the nearest power of 2. 113 */ 114 public int inSampleSize; 115 116 /** 117 * If this is non-null, the decoder will try to decode into this 118 * internal configuration. If it is null, or the request cannot be met, 119 * the decoder will try to pick the best matching config based on the 120 * system's screen depth, and characteristics of the original image such 121 * as if it has per-pixel alpha (requiring a config that also does). 122 * 123 * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by 124 * default. 125 */ 126 public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888; 127 128 /** 129 * If true (which is the default), the resulting bitmap will have its 130 * color channels pre-multipled by the alpha channel. 131 * 132 * <p>This should NOT be set to false for images to be directly drawn by 133 * the view system or through a {@link Canvas}. The view system and 134 * {@link Canvas} assume all drawn images are pre-multiplied to simplify 135 * draw-time blending, and will throw a RuntimeException when 136 * un-premultiplied are drawn.</p> 137 * 138 * <p>This is likely only useful if you want to manipulate raw encoded 139 * image data, e.g. with RenderScript or custom OpenGL.</p> 140 * 141 * <p>This does not affect bitmaps without an alpha channel.</p> 142 * 143 * @see Bitmap#hasAlpha() 144 * @see Bitmap#isPremultiplied() 145 */ 146 public boolean inPremultiplied; 147 148 /** 149 * If dither is true, the decoder will attempt to dither the decoded 150 * image. 151 */ 152 public boolean inDither; 153 154 /** 155 * The pixel density to use for the bitmap. This will always result 156 * in the returned bitmap having a density set for it (see 157 * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)}). In addition, 158 * if {@link #inScaled} is set (which it is by default} and this 159 * density does not match {@link #inTargetDensity}, then the bitmap 160 * will be scaled to the target density before being returned. 161 * 162 * <p>If this is 0, 163 * {@link BitmapFactory#decodeResource(Resources, int)}, 164 * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}, 165 * and {@link BitmapFactory#decodeResourceStream} 166 * will fill in the density associated with the resource. The other 167 * functions will leave it as-is and no density will be applied. 168 * 169 * @see #inTargetDensity 170 * @see #inScreenDensity 171 * @see #inScaled 172 * @see Bitmap#setDensity(int) 173 * @see android.util.DisplayMetrics#densityDpi 174 */ 175 public int inDensity; 176 177 /** 178 * The pixel density of the destination this bitmap will be drawn to. 179 * This is used in conjunction with {@link #inDensity} and 180 * {@link #inScaled} to determine if and how to scale the bitmap before 181 * returning it. 182 * 183 * <p>If this is 0, 184 * {@link BitmapFactory#decodeResource(Resources, int)}, 185 * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}, 186 * and {@link BitmapFactory#decodeResourceStream} 187 * will fill in the density associated the Resources object's 188 * DisplayMetrics. The other 189 * functions will leave it as-is and no scaling for density will be 190 * performed. 191 * 192 * @see #inDensity 193 * @see #inScreenDensity 194 * @see #inScaled 195 * @see android.util.DisplayMetrics#densityDpi 196 */ 197 public int inTargetDensity; 198 199 /** 200 * The pixel density of the actual screen that is being used. This is 201 * purely for applications running in density compatibility code, where 202 * {@link #inTargetDensity} is actually the density the application 203 * sees rather than the real screen density. 204 * 205 * <p>By setting this, you 206 * allow the loading code to avoid scaling a bitmap that is currently 207 * in the screen density up/down to the compatibility density. Instead, 208 * if {@link #inDensity} is the same as {@link #inScreenDensity}, the 209 * bitmap will be left as-is. Anything using the resulting bitmap 210 * must also used {@link Bitmap#getScaledWidth(int) 211 * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight 212 * Bitmap.getScaledHeight} to account for any different between the 213 * bitmap's density and the target's density. 214 * 215 * <p>This is never set automatically for the caller by 216 * {@link BitmapFactory} itself. It must be explicitly set, since the 217 * caller must deal with the resulting bitmap in a density-aware way. 218 * 219 * @see #inDensity 220 * @see #inTargetDensity 221 * @see #inScaled 222 * @see android.util.DisplayMetrics#densityDpi 223 */ 224 public int inScreenDensity; 225 226 /** 227 * When this flag is set, if {@link #inDensity} and 228 * {@link #inTargetDensity} are not 0, the 229 * bitmap will be scaled to match {@link #inTargetDensity} when loaded, 230 * rather than relying on the graphics system scaling it each time it 231 * is drawn to a Canvas. 232 * 233 * <p>This flag is turned on by default and should be turned off if you need 234 * a non-scaled version of the bitmap. Nine-patch bitmaps ignore this 235 * flag and are always scaled. 236 */ 237 public boolean inScaled; 238 239 /** 240 * If this is set to true, then the resulting bitmap will allocate its 241 * pixels such that they can be purged if the system needs to reclaim 242 * memory. In that instance, when the pixels need to be accessed again 243 * (e.g. the bitmap is drawn, getPixels() is called), they will be 244 * automatically re-decoded. 245 * 246 * For the re-decode to happen, the bitmap must have access to the 247 * encoded data, either by sharing a reference to the input 248 * or by making a copy of it. This distinction is controlled by 249 * inInputShareable. If this is true, then the bitmap may keep a shallow 250 * reference to the input. If this is false, then the bitmap will 251 * explicitly make a copy of the input data, and keep that. Even if 252 * sharing is allowed, the implementation may still decide to make a 253 * deep copy of the input data. 254 */ 255 public boolean inPurgeable; 256 257 /** 258 * This field works in conjuction with inPurgeable. If inPurgeable is 259 * false, then this field is ignored. If inPurgeable is true, then this 260 * field determines whether the bitmap can share a reference to the 261 * input data (inputstream, array, etc.) or if it must make a deep copy. 262 */ 263 public boolean inInputShareable; 264 265 /** 266 * If inPreferQualityOverSpeed is set to true, the decoder will try to 267 * decode the reconstructed image to a higher quality even at the 268 * expense of the decoding speed. Currently the field only affects JPEG 269 * decode, in the case of which a more accurate, but slightly slower, 270 * IDCT method will be used instead. 271 */ 272 public boolean inPreferQualityOverSpeed; 273 274 /** 275 * The resulting width of the bitmap, set independent of the state of 276 * inJustDecodeBounds. However, if there is an error trying to decode, 277 * outWidth will be set to -1. 278 */ 279 280 public int outWidth; 281 282 /** 283 * The resulting height of the bitmap, set independent of the state of 284 * inJustDecodeBounds. However, if there is an error trying to decode, 285 * outHeight will be set to -1. 286 */ 287 public int outHeight; 288 289 /** 290 * If known, this string is set to the mimetype of the decoded image. 291 * If not know, or there is an error, it is set to null. 292 */ 293 public String outMimeType; 294 295 /** 296 * Temp storage to use for decoding. Suggest 16K or so. 297 */ 298 public byte[] inTempStorage; 299 300 private native void requestCancel(); 301 302 /** 303 * Flag to indicate that cancel has been called on this object. This 304 * is useful if there's an intermediary that wants to first decode the 305 * bounds and then decode the image. In that case the intermediary 306 * can check, inbetween the bounds decode and the image decode, to see 307 * if the operation is canceled. 308 */ 309 public boolean mCancel; 310 311 /** 312 * This can be called from another thread while this options object is 313 * inside a decode... call. Calling this will notify the decoder that 314 * it should cancel its operation. This is not guaranteed to cancel 315 * the decode, but if it does, the decoder... operation will return 316 * null, or if inJustDecodeBounds is true, will set outWidth/outHeight 317 * to -1 318 */ 319 public void requestCancelDecode() { 320 mCancel = true; 321 requestCancel(); 322 } 323 } 324 325 /** 326 * Decode a file path into a bitmap. If the specified file name is null, 327 * or cannot be decoded into a bitmap, the function returns null. 328 * 329 * @param pathName complete path name for the file to be decoded. 330 * @param opts null-ok; Options that control downsampling and whether the 331 * image should be completely decoded, or just is size returned. 332 * @return The decoded bitmap, or null if the image data could not be 333 * decoded, or, if opts is non-null, if opts requested only the 334 * size be returned (in opts.outWidth and opts.outHeight) 335 */ 336 public static Bitmap decodeFile(String pathName, Options opts) { 337 Bitmap bm = null; 338 InputStream stream = null; 339 try { 340 stream = new FileInputStream(pathName); 341 bm = decodeStream(stream, null, opts); 342 } catch (Exception e) { 343 /* do nothing. 344 If the exception happened on open, bm will be null. 345 */ 346 Log.e("BitmapFactory", "Unable to decode stream: " + e); 347 } finally { 348 if (stream != null) { 349 try { 350 stream.close(); 351 } catch (IOException e) { 352 // do nothing here 353 } 354 } 355 } 356 return bm; 357 } 358 359 /** 360 * Decode a file path into a bitmap. If the specified file name is null, 361 * or cannot be decoded into a bitmap, the function returns null. 362 * 363 * @param pathName complete path name for the file to be decoded. 364 * @return the resulting decoded bitmap, or null if it could not be decoded. 365 */ 366 public static Bitmap decodeFile(String pathName) { 367 return decodeFile(pathName, null); 368 } 369 370 /** 371 * Decode a new Bitmap from an InputStream. This InputStream was obtained from 372 * resources, which we pass to be able to scale the bitmap accordingly. 373 */ 374 public static Bitmap decodeResourceStream(Resources res, TypedValue value, 375 InputStream is, Rect pad, Options opts) { 376 377 if (opts == null) { 378 opts = new Options(); 379 } 380 381 if (opts.inDensity == 0 && value != null) { 382 final int density = value.density; 383 if (density == TypedValue.DENSITY_DEFAULT) { 384 opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; 385 } else if (density != TypedValue.DENSITY_NONE) { 386 opts.inDensity = density; 387 } 388 } 389 390 if (opts.inTargetDensity == 0 && res != null) { 391 opts.inTargetDensity = res.getDisplayMetrics().densityDpi; 392 } 393 394 return decodeStream(is, pad, opts); 395 } 396 397 /** 398 * Synonym for opening the given resource and calling 399 * {@link #decodeResourceStream}. 400 * 401 * @param res The resources object containing the image data 402 * @param id The resource id of the image data 403 * @param opts null-ok; Options that control downsampling and whether the 404 * image should be completely decoded, or just is size returned. 405 * @return The decoded bitmap, or null if the image data could not be 406 * decoded, or, if opts is non-null, if opts requested only the 407 * size be returned (in opts.outWidth and opts.outHeight) 408 */ 409 public static Bitmap decodeResource(Resources res, int id, Options opts) { 410 Bitmap bm = null; 411 InputStream is = null; 412 413 try { 414 final TypedValue value = new TypedValue(); 415 is = res.openRawResource(id, value); 416 417 bm = decodeResourceStream(res, value, is, null, opts); 418 } catch (Exception e) { 419 /* do nothing. 420 If the exception happened on open, bm will be null. 421 If it happened on close, bm is still valid. 422 */ 423 } finally { 424 try { 425 if (is != null) is.close(); 426 } catch (IOException e) { 427 // Ignore 428 } 429 } 430 431 if (bm == null && opts != null && opts.inBitmap != null) { 432 throw new IllegalArgumentException("Problem decoding into existing bitmap"); 433 } 434 435 return bm; 436 } 437 438 /** 439 * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)} 440 * will null Options. 441 * 442 * @param res The resources object containing the image data 443 * @param id The resource id of the image data 444 * @return The decoded bitmap, or null if the image could not be decode. 445 */ 446 public static Bitmap decodeResource(Resources res, int id) { 447 return decodeResource(res, id, null); 448 } 449 450 /** 451 * Decode an immutable bitmap from the specified byte array. 452 * 453 * @param data byte array of compressed image data 454 * @param offset offset into imageData for where the decoder should begin 455 * parsing. 456 * @param length the number of bytes, beginning at offset, to parse 457 * @param opts null-ok; Options that control downsampling and whether the 458 * image should be completely decoded, or just is size returned. 459 * @return The decoded bitmap, or null if the image data could not be 460 * decoded, or, if opts is non-null, if opts requested only the 461 * size be returned (in opts.outWidth and opts.outHeight) 462 */ 463 public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) { 464 if ((offset | length) < 0 || data.length < offset + length) { 465 throw new ArrayIndexOutOfBoundsException(); 466 } 467 468 Bitmap bm; 469 470 Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); 471 try { 472 bm = nativeDecodeByteArray(data, offset, length, opts); 473 474 if (bm == null && opts != null && opts.inBitmap != null) { 475 throw new IllegalArgumentException("Problem decoding into existing bitmap"); 476 } 477 setDensityFromOptions(bm, opts); 478 } finally { 479 Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); 480 } 481 482 return bm; 483 } 484 485 /** 486 * Decode an immutable bitmap from the specified byte array. 487 * 488 * @param data byte array of compressed image data 489 * @param offset offset into imageData for where the decoder should begin 490 * parsing. 491 * @param length the number of bytes, beginning at offset, to parse 492 * @return The decoded bitmap, or null if the image could not be decode. 493 */ 494 public static Bitmap decodeByteArray(byte[] data, int offset, int length) { 495 return decodeByteArray(data, offset, length, null); 496 } 497 498 /** 499 * Set the newly decoded bitmap's density based on the Options. 500 */ 501 private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) { 502 if (outputBitmap == null || opts == null) return; 503 504 final int density = opts.inDensity; 505 if (density != 0) { 506 outputBitmap.setDensity(density); 507 final int targetDensity = opts.inTargetDensity; 508 if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) { 509 return; 510 } 511 512 byte[] np = outputBitmap.getNinePatchChunk(); 513 final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); 514 if (opts.inScaled || isNinePatch) { 515 outputBitmap.setDensity(targetDensity); 516 } 517 } else if (opts.inBitmap != null) { 518 // bitmap was reused, ensure density is reset 519 outputBitmap.setDensity(Bitmap.getDefaultDensity()); 520 } 521 } 522 523 /** 524 * Decode an input stream into a bitmap. If the input stream is null, or 525 * cannot be used to decode a bitmap, the function returns null. 526 * The stream's position will be where ever it was after the encoded data 527 * was read. 528 * 529 * @param is The input stream that holds the raw data to be decoded into a 530 * bitmap. 531 * @param outPadding If not null, return the padding rect for the bitmap if 532 * it exists, otherwise set padding to [-1,-1,-1,-1]. If 533 * no bitmap is returned (null) then padding is 534 * unchanged. 535 * @param opts null-ok; Options that control downsampling and whether the 536 * image should be completely decoded, or just is size returned. 537 * @return The decoded bitmap, or null if the image data could not be 538 * decoded, or, if opts is non-null, if opts requested only the 539 * size be returned (in opts.outWidth and opts.outHeight) 540 */ 541 public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) { 542 // we don't throw in this case, thus allowing the caller to only check 543 // the cache, and not force the image to be decoded. 544 if (is == null) { 545 return null; 546 } 547 548 Bitmap bm; 549 550 Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); 551 try { 552 // we need mark/reset to work properly 553 if (!is.markSupported()) { 554 is = new BufferedInputStream(is, DECODE_BUFFER_SIZE); 555 } 556 557 // so we can call reset() if a given codec gives up after reading up to 558 // this many bytes. FIXME: need to find out from the codecs what this 559 // value should be. 560 is.mark(1024); 561 562 if (is instanceof AssetManager.AssetInputStream) { 563 final int asset = ((AssetManager.AssetInputStream) is).getAssetInt(); 564 bm = nativeDecodeAsset(asset, outPadding, opts); 565 } else { 566 // pass some temp storage down to the native code. 1024 is made up, 567 // but should be large enough to avoid too many small calls back 568 // into is.read(...) This number is not related to the value passed 569 // to mark(...) above. 570 byte [] tempStorage = null; 571 if (opts != null) tempStorage = opts.inTempStorage; 572 if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; 573 bm = nativeDecodeStream(is, tempStorage, outPadding, opts); 574 } 575 576 if (bm == null && opts != null && opts.inBitmap != null) { 577 throw new IllegalArgumentException("Problem decoding into existing bitmap"); 578 } 579 580 setDensityFromOptions(bm, opts); 581 } finally { 582 Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); 583 } 584 585 return bm; 586 } 587 588 /** 589 * Decode an input stream into a bitmap. If the input stream is null, or 590 * cannot be used to decode a bitmap, the function returns null. 591 * The stream's position will be where ever it was after the encoded data 592 * was read. 593 * 594 * @param is The input stream that holds the raw data to be decoded into a 595 * bitmap. 596 * @return The decoded bitmap, or null if the image data could not be decoded. 597 */ 598 public static Bitmap decodeStream(InputStream is) { 599 return decodeStream(is, null, null); 600 } 601 602 /** 603 * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded 604 * return null. The position within the descriptor will not be changed when 605 * this returns, so the descriptor can be used again as-is. 606 * 607 * @param fd The file descriptor containing the bitmap data to decode 608 * @param outPadding If not null, return the padding rect for the bitmap if 609 * it exists, otherwise set padding to [-1,-1,-1,-1]. If 610 * no bitmap is returned (null) then padding is 611 * unchanged. 612 * @param opts null-ok; Options that control downsampling and whether the 613 * image should be completely decoded, or just is size returned. 614 * @return the decoded bitmap, or null 615 */ 616 public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) { 617 if (nativeIsSeekable(fd)) { 618 Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts); 619 if (bm == null && opts != null && opts.inBitmap != null) { 620 throw new IllegalArgumentException("Problem decoding into existing bitmap"); 621 } 622 return bm; 623 } else { 624 FileInputStream fis = new FileInputStream(fd); 625 try { 626 return decodeStream(fis, outPadding, opts); 627 } finally { 628 try { 629 fis.close(); 630 } catch (Throwable t) {/* ignore */} 631 } 632 } 633 } 634 635 /** 636 * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded 637 * return null. The position within the descriptor will not be changed when 638 * this returns, so the descriptor can be used again as is. 639 * 640 * @param fd The file descriptor containing the bitmap data to decode 641 * @return the decoded bitmap, or null 642 */ 643 public static Bitmap decodeFileDescriptor(FileDescriptor fd) { 644 return decodeFileDescriptor(fd, null, null); 645 } 646 647 private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage, 648 Rect padding, Options opts); 649 private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, 650 Rect padding, Options opts); 651 private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts); 652 private static native Bitmap nativeDecodeByteArray(byte[] data, int offset, 653 int length, Options opts); 654 private static native boolean nativeIsSeekable(FileDescriptor fd); 655} 656