Repository: deepjavalibrary/djl
Pull request #1483 (2022-01-27 07:44:14)

[android] Update to 0.15.0
Change-Id: I77f27b7271b96f2094cc9d24e4e15014d94541eb

## Description ##

Brief description of what this PR is about

- If this change is a backward incompatible change, why must this change be made?
- Interesting edge cases to note here


codecov-commenter: # [Codecov](https://codecov.io/gh/deepjavalibrary/djl/pull/1483?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) Report
> Merging [#1483](https://codecov.io/gh/deepjavalibrary/djl/pull/1483?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) (c7215ad) into [master](https://codecov.io/gh/deepjavalibrary/djl/commit/bb5073f7ffc33f1883621fc1783dbb01e70b418f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) (bb5073f) will **decrease** coverage by `1.36%`.
> The diff coverage is `49.69%`.

[![Impacted file tree graph](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/graphs/tree.svg?width=650&height=150&src=pr&token=mDKVNwFXRV&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary)](https://codecov.io/gh/deepjavalibrary/djl/pull/1483?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary)

```diff
@@             Coverage Diff              @@
##             master    #1483      +/-   ##
============================================
- Coverage     72.08%   70.72%   -1.37%     
- Complexity     5126     5310     +184     
============================================
  Files           473      493      +20     
  Lines         21970    23263    +1293     
  Branches       2351     2541     +190     
============================================
+ Hits          15838    16452     +614     
- Misses         4925     5533     +608     
- Partials       1207     1278      +71     
```


| [Impacted Files](https://codecov.io/gh/deepjavalibrary/djl/pull/1483?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) | Coverage Δ | |
|---|---|---|
| [api/src/main/java/ai/djl/modality/cv/Image.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L0ltYWdlLmphdmE=) | `69.23% <ø> (-4.11%)` | :arrow_down: |
| [...i/djl/modality/cv/translator/BigGANTranslator.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvQmlnR0FOVHJhbnNsYXRvci5qYXZh) | `21.42% <ø> (-5.24%)` | :arrow_down: |
| [...odality/cv/translator/BigGANTranslatorFactory.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvQmlnR0FOVHJhbnNsYXRvckZhY3RvcnkuamF2YQ==) | `33.33% <0.00%> (+8.33%)` | :arrow_up: |
| [...nslator/InstanceSegmentationTranslatorFactory.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvSW5zdGFuY2VTZWdtZW50YXRpb25UcmFuc2xhdG9yRmFjdG9yeS5qYXZh) | `14.28% <0.00%> (-3.90%)` | :arrow_down: |
| [.../modality/cv/translator/YoloTranslatorFactory.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvWW9sb1RyYW5zbGF0b3JGYWN0b3J5LmphdmE=) | `8.33% <0.00%> (-1.67%)` | :arrow_down: |
| [...i/djl/modality/cv/translator/YoloV5Translator.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvWW9sb1Y1VHJhbnNsYXRvci5qYXZh) | `5.69% <0.00%> (ø)` | |
| [...odality/cv/translator/YoloV5TranslatorFactory.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvWW9sb1Y1VHJhbnNsYXRvckZhY3RvcnkuamF2YQ==) | `8.33% <0.00%> (-1.67%)` | :arrow_down: |
| [...pi/src/main/java/ai/djl/ndarray/BytesSupplier.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL25kYXJyYXkvQnl0ZXNTdXBwbGllci5qYXZh) | `54.54% <0.00%> (-12.13%)` | :arrow_down: |
| [...i/src/main/java/ai/djl/ndarray/NDArrayAdapter.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL25kYXJyYXkvTkRBcnJheUFkYXB0ZXIuamF2YQ==) | `15.23% <0.00%> (-0.83%)` | :arrow_down: |
| [...l/training/loss/SigmoidBinaryCrossEntropyLoss.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL3RyYWluaW5nL2xvc3MvU2lnbW9pZEJpbmFyeUNyb3NzRW50cm9weUxvc3MuamF2YQ==) | `64.00% <0.00%> (ø)` | |
| ... and [162 more](https://codecov.io/gh/deepjavalibrary/djl/pull/1483/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/deepjavalibrary/djl/pull/1483?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/deepjavalibrary/djl/pull/1483?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary). Last update [f48182d...c7215ad](https://codecov.io/gh/deepjavalibrary/djl/pull/1483?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).


Patch:
diff --git a/.github/workflows/android_core_publish.yml b/.github/workflows/android_core_publish.yml
new file mode 100644
index 000000000..14366db75
--- /dev/null
+++ b/.github/workflows/android_core_publish.yml
@@ -0,0 +1,33 @@
+name: Android core API Publish
+
+on:
+  workflow_dispatch:
+    inputs:
+      mode:
+        description: 'staging/snapshot, default is snapshot'
+        required: true
+        default: 'snapshot'
+
+jobs:
+  publish-android-core:
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set up JDK 1.8
+        uses: actions/setup-java@v1
+        with:
+          java-version: 1.8
+      - uses: actions/cache@v2
+        with:
+          path: ~/.gradle/caches
+          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
+          restore-keys: |
+            ${{ runner.os }}-gradle-
+      - name: publish android core to staging
+        working-directory: android/core
+        run: ./gradlew publish -P${{ github.event.inputs.mode }}
+        env:
+          ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_signingKey }}
+          ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.ORG_GRADLE_PROJECT_signingPassword }}
+          ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.ORG_GRADLE_PROJECT_ossrhUsername }}
+          ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.ORG_GRADLE_PROJECT_ossrhPassword }}
diff --git a/.github/workflows/native_jni_s3_pytorch_android.yml b/.github/workflows/native_jni_s3_pytorch_android.yml
index b7459f4fb..716bee5c9 100644
--- a/.github/workflows/native_jni_s3_pytorch_android.yml
+++ b/.github/workflows/native_jni_s3_pytorch_android.yml
@@ -7,6 +7,7 @@ jobs:
   build-pytorch-jni-android:
     runs-on: ubuntu-18.04
     env:
+      PYTORCH_VERSION: "1.8.1"
       NDK_VERSION: "20.0.5594570"
 
     steps:
@@ -26,24 +27,7 @@ jobs:
       - name: build android
         run: |
           export ANDROID_NDK=${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}
-          ./gradlew :engines:pytorch:pytorch-native:compileAndroidJNI
-      - name: Upload compiled jni library
-        uses: actions/upload-artifact@v2
-        if: always()
-        with:
-          name: jnilib-Android
-          path: engines/pytorch/pytorch-native/jnilib
-
-  publish:
-    runs-on: ubuntu-18.04
-    needs: [build-pytorch-jni-android]
-    steps:
-      - uses: actions/checkout@v2
-      - name: Download compiledJNI Android
-        uses: actions/download-artifact@v1
-        with:
-          name: jnilib-Android
-          path: jnilib
+          ./gradlew :engines:pytorch:pytorch-native:compileAndroidJNI -Ppt_version=${PYTORCH_VERSION}
       - name: Configure AWS Credentials
         uses: aws-actions/configure-aws-credentials@v1
         with:
@@ -52,6 +36,5 @@ jobs:
           aws-region: us-east-2
       - name: Copy files to S3 with the AWS CLI
         run: |
-          PYTORCH_VERSION="$(cat gradle.properties | awk -F '=' '/pytorch_version/ {print $2}')"
-          aws s3 sync jnilib s3://djl-ai/publish/pytorch-${PYTORCH_VERSION}/jnilib
-          aws cloudfront create-invalidation --distribution-id E371VB8JQ6NRVY --paths "/pytorch-${PYTORCH_VERSION}/jnilib*"
+          aws s3 sync engines/pytorch/pytorch-native/jnilib s3://djl-ai/publish/pytorch/${PYTORCH_VERSION}/jnilib
+          aws cloudfront create-invalidation --distribution-id E371VB8JQ6NRVY --paths "/pytorch/${PYTORCH_VERSION}/jnilib*"
diff --git a/.github/workflows/native_publish_pytorch_android.yml b/.github/workflows/native_publish_pytorch_android.yml
index 6c2cd7633..f756c1961 100644
--- a/.github/workflows/native_publish_pytorch_android.yml
+++ b/.github/workflows/native_publish_pytorch_android.yml
@@ -11,8 +11,6 @@ on:
 jobs:
   release-android:
     runs-on: ubuntu-18.04
-    env:
-      NDK_VERSION: "20.0.5594570"
     steps:
       - uses: actions/checkout@v2
       - name: Set up JDK 1.8
@@ -26,13 +24,10 @@ jobs:
           key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
           restore-keys: |
             ${{ runner.os }}-gradle-
-      - name: Install NDK
-        run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;${NDK_VERSION}"
       - name: publish android core to staging
+        working-directory: android
         run: |
-          export ANDROID_NDK=${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}
-          cd android
-          ./gradlew :engines:pytorch-native:assemble
+          ./gradlew :pytorch-native:assemble
           ./gradlew publish -P${{ github.event.inputs.mode }}
         env:
           ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_signingKey }}
diff --git a/.github/workflows/native_s3_pytorch_android.yml b/.github/workflows/native_s3_pytorch_android.yml
index c80c0d286..40097df8d 100644
--- a/.github/workflows/native_s3_pytorch_android.yml
+++ b/.github/workflows/native_s3_pytorch_android.yml
@@ -10,8 +10,7 @@ jobs:
       matrix:
         format: ["armeabi-v7a", "arm64-v8a", "x86" ,"x86_64"]
     env:
-      PYTORCH_TAG_VERSION:    "v1.9.1"
-      PYTORCH_VERSION: "1.9.1"
+      PYTORCH_VERSION: "1.8.1"
       NDK_VERSION: "20.0.5594570"
     steps:
       - uses: actions/checkout@v2
@@ -24,7 +23,8 @@ jobs:
         with:
           python-version: '3.x'
       - name: clone pytorch
-        run: git clone https://github.com/pytorch/pytorch android_pytorch_tmp -b "${PYTORCH_TAG_VERSION}" --recursive
+        run: |
+          git clone https://github.com/pytorch/pytorch android_pytorch_tmp -b "v${PYTORCH_VERSION}" --recursive
       - name: install Python Dependencies
         run: pip install numpy ninja pyyaml mkl mkl-include setuptools cmake cffi typing_extensions
       - name: Install NDK
@@ -45,5 +45,5 @@ jobs:
           aws-region: us-east-2
       - name: Copy files to S3 with the AWS CLI
         run: |
-          aws s3 cp android_pytorch_tmp/build_android/${{ matrix.format }}_native.zip s3://djl-ai/publish/pytorch-${PYTORCH_VERSION}/android_native/
-          aws cloudfront create-invalidation --distribution-id E371VB8JQ6NRVY --paths "/pytorch-${PYTORCH_VERSION}/android_native*"
+          aws s3 cp android_pytorch_tmp/build_android/${{ matrix.format }}_native.zip s3://djl-ai/publish/pytorch/${PYTORCH_VERSION}/android_native/
+          aws cloudfront create-invalidation --distribution-id E371VB8JQ6NRVY --paths "/pytorch/${PYTORCH_VERSION}/android_native*"
diff --git a/android/build.gradle b/android/build.gradle
index 1870085bb..88d1750bb 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,15 +1,12 @@
 buildscript {
 
     repositories {
-        maven {
-            url "https://mlrepo.djl.ai/maven/"
-        }
         google()
         mavenCentral()
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.6.3'
+        classpath 'com.android.tools.build:gradle:4.2.2'
     }
 }
 
diff --git a/android/core/build.gradle b/android/core/build.gradle
index e67388c17..acf6c1021 100644
--- a/android/core/build.gradle
+++ b/android/core/build.gradle
@@ -3,12 +3,12 @@ apply plugin: 'maven-publish'
 apply plugin: 'signing'
 
 android {
-    compileSdkVersion 29
-    buildToolsVersion "29.0.3"
+    compileSdkVersion 32
+    buildToolsVersion "30.0.3"
 
     defaultConfig {
         minSdkVersion 26
-        targetSdkVersion 29
+        targetSdkVersion 32
         versionCode 1
         versionName "1.0"
 
@@ -105,9 +105,9 @@ afterEvaluate {
 
 dependencies {
     implementation "ai.djl:api:${version}"
-    testImplementation 'junit:junit:4.12'
+    testImplementation 'junit:junit:4.13.2'
     androidTestRuntimeOnly "ai.djl.pytorch:pytorch-engine:${version}"
     androidTestRuntimeOnly "ai.djl.android:pytorch-native:${version}"
-    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
-    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
 }
diff --git a/android/core/src/main/java/ai/djl/android/core/BitmapImageFactory.java b/android/core/src/main/java/ai/djl/android/core/BitmapImageFactory.java
index 7f4e82510..191fcfcd2 100644
--- a/android/core/src/main/java/ai/djl/android/core/BitmapImageFactory.java
+++ b/android/core/src/main/java/ai/djl/android/core/BitmapImageFactory.java
@@ -140,14 +140,15 @@ public class BitmapImageFactory extends ImageFactory {
 
         /** {@inheritDoc} */
         @Override
-        public Image getSubimage(int x, int y, int w, int h) {
+        public Image getSubImage(int x, int y, int w, int h) {
             return new BitMapWrapper(Bitmap.createBitmap(bitmap, x, y, w, h));
         }
 
         /** {@inheritDoc} */
         @Override
-        public Image duplicate(Type type) {
-            return new BitMapWrapper(bitmap.copy(getType(type), true));
+        public Image duplicate() {
+            Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
+            return new BitMapWrapper(mutableBitmap);
         }
 
         /** {@inheritDoc} */
@@ -255,13 +256,6 @@ public class BitmapImageFactory extends ImageFactory {
             oldBitmap.recycle();
         }
 
-        private Bitmap.Config getType(Type type) {
-            if (type == Type.TYPE_INT_ARGB) {
-                return Bitmap.Config.ARGB_8888;
-            }
-            throw new IllegalArgumentException("the type is not supported!");
-        }
-
         private int randomColor() {
             return Color.rgb(RandomUtils.nextInt(255), RandomUtils.nextInt(255), RandomUtils.nextInt(255));
         }
diff --git a/android/gradle.properties b/android/gradle.properties
index 7efb30735..d08f46a71 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -17,4 +17,4 @@ org.gradle.jvmargs=-Xmx1536m
 android.useAndroidX=true
 # Automatically convert third-party libraries to use AndroidX
 android.enableJetifier=true
-djl_version=0.13.0
+djl_version=0.15.0
diff --git a/android/pytorch-native/build.gradle b/android/pytorch-native/build.gradle
index b394e76a5..819abeaa9 100644
--- a/android/pytorch-native/build.gradle
+++ b/android/pytorch-native/build.gradle
@@ -2,15 +2,15 @@ apply plugin: 'com.android.library'
 apply plugin: 'maven-publish'
 apply plugin: 'signing'
 
-def pytorch_version = "1.9.1"
+def pytorch_version = "1.8.1"
 
 android {
-    compileSdkVersion 29
-    buildToolsVersion "29.0.3"
+    compileSdkVersion 32
+    buildToolsVersion "30.0.3"
 
     defaultConfig {
         minSdkVersion 24
-        targetSdkVersion 29
+        targetSdkVersion 32
         versionCode 1
         versionName "1.0"
 
@@ -105,7 +105,7 @@ afterEvaluate {
 
 task processResources {
     doLast {
-        def url = "https://publish.djl.ai/pytorch-${pytorch_version}/jnilib/${djl_version}/android"
+        def url = "https://publish.djl.ai/pytorch/${pytorch_version}/jnilib/${djl_version}/android"
         def abis = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]
         abis.each { abi ->
             def downloadPath = new URL("${url}/${abi}/libdjl_torch.so")
diff --git a/engines/pytorch/pytorch-native/build_android.sh b/engines/pytorch/pytorch-native/build_android.sh
old mode 100644
new mode 100755
index 71a50f138..46dee3254
--- a/engines/pytorch/pytorch-native/build_android.sh
+++ b/engines/pytorch/pytorch-native/build_android.sh
@@ -5,24 +5,25 @@ WORK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 export WORK_DIR
 NUM_PROC=1
 if [[ -n $(command -v nproc) ]]; then
-    NUM_PROC=$(nproc)
+  NUM_PROC=$(nproc)
 elif [[ -n $(command -v sysctl) ]]; then
-    NUM_PROC=$(sysctl -n hw.ncpu)
+  NUM_PROC=$(sysctl -n hw.ncpu)
 fi
 
 VERSION=$1
 FLAVOR=$2
 if [[ ! -d libtorch_android/"$FLAVOR" ]]; then
-    if [[ $FLAVOR != "armeabi-v7a" ]] && [[ $FLAVOR != "arm64-v8a" ]] && [[ $FLAVOR != "x86" ]] && [[ $FLAVOR != "x86_64" ]]; then
-        echo "$FLAVOR is not supported."
-        exit 1
-    else
-        mkdir -p libtorch_android/"$FLAVOR"
-        cd libtorch_android/"$FLAVOR"
-        curl -s https://publish.djl.ai/pytorch-"$VERSION/android_native/$FLAVOR"_native.zip | jar xv
-        mv install/include include
-        cd ../../
-    fi
+  if [[ $FLAVOR != "armeabi-v7a" ]] && [[ $FLAVOR != "arm64-v8a" ]] && [[ $FLAVOR != "x86" ]] && [[ $FLAVOR != "x86_64" ]]; then
+    echo "$FLAVOR is not supported."
+    exit 1
+  else
+    mkdir -p libtorch_android/"$FLAVOR"
+    cd libtorch_android/"$FLAVOR"
+    echo "Downloading https://publish.djl.ai/pytorch/$VERSION/android_native/${FLAVOR}_native.zip"
+    curl -s "https://publish.djl.ai/pytorch/$VERSION/android_native/${FLAVOR}_native.zip" | jar xv
+    mv install/include include
+    cd -
+  fi
 fi
 
 pushd .
@@ -31,6 +32,6 @@ rm -rf build
 mkdir build && cd build
 mkdir classes
 javac -sourcepath ../../pytorch-engine/src/main/java/ ../../pytorch-engine/src/main/java/ai/djl/pytorch/jni/PyTorchLibrary.java -h include -d classes
-cmake -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK"/build/cmake/android.toolchain.cmake -DANDROID_ABI="$FLAVOR" -DANDROID_NATIVE_API_LEVEL=21 -DBUILD_ANDROID=ON  ..
+cmake -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK"/build/cmake/android.toolchain.cmake -DANDROID_ABI="$FLAVOR" -DANDROID_NATIVE_API_LEVEL=21 -DBUILD_ANDROID=ON ..
 cmake --build . --config Release -- -j "${NUM_PROC}"
 popd
diff --git a/engines/pytorch/pytorch-native/src/main/native/ai_djl_pytorch_jni_PyTorchLibrary_nn_functional.cc b/engines/pytorch/pytorch-native/src/main/native/ai_djl_pytorch_jni_PyTorchLibrary_nn_functional.cc
index 84a7dfd63..503342b0a 100644
--- a/engines/pytorch/pytorch-native/src/main/native/ai_djl_pytorch_jni_PyTorchLibrary_nn_functional.cc
+++ b/engines/pytorch/pytorch-native/src/main/native/ai_djl_pytorch_jni_PyTorchLibrary_nn_functional.cc
@@ -147,6 +147,10 @@ JNIEXPORT jlong JNICALL Java_ai_djl_pytorch_jni_PyTorchLibrary_torchNNBatchNorm(
 JNIEXPORT jlong JNICALL Java_ai_djl_pytorch_jni_PyTorchLibrary_torchNNLayerNorm(
     JNIEnv* env, jobject jthis, jlong jinput, jlongArray jnormalizedshape, jlong jweight, jlong jbias, jdouble jeps) {
   API_BEGIN()
+#if defined(__ANDROID__)
+  env->ThrowNew(ENGINE_EXCEPTION_CLASS, "layerNorm is not supported on Android.");
+  return 0;
+#else
   const auto* tensor_ptr = reinterpret_cast<torch::Tensor*>(jinput);
   const auto normalized_shape_vec = djl::utils::jni::GetVecFromJLongArray(env, jnormalizedshape);
   torch::Tensor weight = {};
@@ -160,6 +164,7 @@ JNIEXPORT jlong JNICALL Java_ai_djl_pytorch_jni_PyTorchLibrary_torchNNLayerNorm(
   const auto* result_ptr = new torch::Tensor(torch::nn::functional::layer_norm(*tensor_ptr,
       torch::nn::functional::LayerNormFuncOptions(normalized_shape_vec).weight(weight).bias(bias).eps(jeps)));
   return reinterpret_cast<uintptr_t>(result_ptr);
+#endif
   API_END_RETURN()
 }
 


Test patch:
diff --git a/android/core/src/androidTest/java/ai/djl/android/core/BitmapWrapperTest.java b/android/core/src/androidTest/java/ai/djl/android/core/BitmapWrapperTest.java
index 921d7fabc..db992d2fa 100644
--- a/android/core/src/androidTest/java/ai/djl/android/core/BitmapWrapperTest.java
+++ b/android/core/src/androidTest/java/ai/djl/android/core/BitmapWrapperTest.java
@@ -13,33 +13,23 @@
 package ai.djl.android.core;
 
 import android.content.Context;
-import android.graphics.BitmapFactory;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.File;
 import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.List;
 
 import ai.djl.modality.cv.Image;
 import ai.djl.modality.cv.ImageFactory;
-import ai.djl.modality.cv.output.BoundingBox;
-import ai.djl.modality.cv.output.DetectedObjects;
-import ai.djl.modality.cv.output.Rectangle;
 import ai.djl.ndarray.NDArray;
 import ai.djl.ndarray.NDManager;
 import ai.djl.ndarray.types.DataType;
 import ai.djl.ndarray.types.Shape;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 
 /**
  * Instrumented test, which will execute on an Android device.
@@ -73,7 +63,7 @@ public class BitmapWrapperTest {
             ImageFactory factory = ImageFactory.getInstance();
             Image img = factory.fromUrl("https://github.com/deepjavalibrary/djl/raw/master/examples/src/test/resources/dog_bike_car.jpg");
             NDArray array = img.toNDArray(manager);
-            Image subImg = img.getSubimage(10, 20, 30, 40);
+            Image subImg = img.getSubImage(10, 20, 30, 40);
             NDArray subArray = subImg.toNDArray(manager);
             assertEquals(array.get("20:60,10:40,:"), subArray);
         }
@@ -85,7 +75,7 @@ public class BitmapWrapperTest {
             ImageFactory factory = ImageFactory.getInstance();
             Image img = factory.fromUrl("https://github.com/deepjavalibrary/djl/raw/master/examples/src/test/resources/dog_bike_car.jpg");
             NDArray array = img.toNDArray(manager);
-            NDArray duplicate = img.duplicate(Image.Type.TYPE_INT_ARGB).toNDArray(manager);
+            NDArray duplicate = img.duplicate().toNDArray(manager);
             assertEquals(array, duplicate);
         }
     }