1 /*
2  * Copyright (C) 2014 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 #define LOG_TAG "BitmapSerializeUtils"
18 
19 #include <jni.h>
20 #include <nativehelper/JNIHelp.h>
21 
22 #include <android/bitmap.h>
23 #include <android/log.h>
24 
25 namespace android {
26 
27 #define RGBA_8888_COLOR_DEPTH 4
28 
writeAllBytes(const int fd,void * buffer,const size_t byteCount)29 static bool writeAllBytes(const int fd, void* buffer, const size_t byteCount) {
30     char* writeBuffer = static_cast<char*>(buffer);
31     size_t remainingBytes = byteCount;
32     while (remainingBytes > 0) {
33         ssize_t writtenByteCount = TEMP_FAILURE_RETRY(write(fd, writeBuffer, remainingBytes));
34         if (writtenByteCount == -1) {
35             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
36                     "Error writing to buffer: %d", errno);
37             return false;
38         }
39         remainingBytes -= writtenByteCount;
40         writeBuffer += writtenByteCount;
41     }
42     return true;
43 }
44 
readAllBytes(const int fd,void * buffer,const size_t byteCount)45 static bool readAllBytes(const int fd, void* buffer, const size_t byteCount) {
46     char* readBuffer = static_cast<char*>(buffer);
47     size_t remainingBytes = byteCount;
48     while (remainingBytes > 0) {
49         ssize_t readByteCount = TEMP_FAILURE_RETRY(read(fd, readBuffer, remainingBytes));
50         if (readByteCount == -1) {
51             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
52                     "Error reading from buffer: %d", errno);
53             return false;
54         }
55 
56         remainingBytes -= readByteCount;
57         readBuffer += readByteCount;
58 
59         if (readByteCount == 0 && remainingBytes > 0) {
60             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
61                     "File closed before all bytes were read. %zu/%zu remaining", remainingBytes,
62                     byteCount);
63             return false;
64         }
65     }
66     return true;
67 }
68 
throwException(JNIEnv * env,const char * className,const char * message)69 static void throwException(JNIEnv* env, const char* className, const char* message) {
70     jclass exceptionClass = env->FindClass(className);
71     env->ThrowNew(exceptionClass, message);
72 }
73 
throwIllegalStateException(JNIEnv * env,char * message)74 static void throwIllegalStateException(JNIEnv* env, char *message) {
75     const char* className = "java/lang/IllegalStateException";
76     throwException(env, className, message);
77 }
78 
throwIllegalArgumentException(JNIEnv * env,char * message)79 static void throwIllegalArgumentException(JNIEnv* env, char* message) {
80     const char* className = "java/lang/IllegalArgumentException";
81     throwException(env, className, message);
82 }
83 
readBitmapPixels(JNIEnv * env,jclass,jobject jbitmap,jint fd)84 static void readBitmapPixels(JNIEnv* env, jclass /* clazz */, jobject jbitmap, jint fd) {
85     // Read the info.
86     AndroidBitmapInfo readInfo;
87     bool read = readAllBytes(fd, (void*) &readInfo, sizeof(AndroidBitmapInfo));
88     if (!read) {
89         throwIllegalStateException(env, (char*) "Cannot read bitmap info");
90         return;
91     }
92 
93     // Get the info of the target bitmap.
94     AndroidBitmapInfo targetInfo;
95     int result = AndroidBitmap_getInfo(env, jbitmap, &targetInfo);
96     if (result < 0) {
97         throwIllegalStateException(env, (char*) "Cannot get bitmap info");
98         return;
99     }
100 
101     // Enforce we can reuse the bitmap.
102     if (readInfo.width != targetInfo.width || readInfo.height != targetInfo.height
103             || readInfo.stride != targetInfo.stride || readInfo.format != targetInfo.format
104             || readInfo.flags != targetInfo.flags) {
105         throwIllegalArgumentException(env, (char*) "Cannot reuse bitmap");
106         return;
107     }
108 
109     // Lock the pixels.
110     void* pixels;
111     result = AndroidBitmap_lockPixels(env, jbitmap, &pixels);
112     if (result < 0) {
113         throwIllegalStateException(env, (char*) "Cannot lock bitmap pixels");
114         return;
115     }
116 
117     // Read the pixels.
118     size_t byteCount = readInfo.stride * readInfo.height;
119     read = readAllBytes(fd, (void*) pixels, byteCount);
120     if (!read) {
121         throwIllegalStateException(env, (char*) "Cannot read bitmap pixels");
122         return;
123     }
124 
125     // Unlock the pixels.
126     result = AndroidBitmap_unlockPixels(env, jbitmap);
127     if (result < 0) {
128         throwIllegalStateException(env, (char*) "Cannot unlock bitmap pixels");
129     }
130 }
131 
writeBitmapPixels(JNIEnv * env,jclass,jobject jbitmap,jint fd)132 static void writeBitmapPixels(JNIEnv* env, jclass /* clazz */, jobject jbitmap, jint fd) {
133     // Get the info.
134     AndroidBitmapInfo info;
135     int result = AndroidBitmap_getInfo(env, jbitmap, &info);
136     if (result < 0) {
137         throwIllegalStateException(env, (char*) "Cannot get bitmap info");
138         return;
139     }
140 
141     // Write the info.
142     bool written = writeAllBytes(fd, (void*) &info, sizeof(AndroidBitmapInfo));
143     if (!written) {
144         throwIllegalStateException(env, (char*) "Cannot write bitmap info");
145         return;
146     }
147 
148     // Lock the pixels.
149     void* pixels;
150     result = AndroidBitmap_lockPixels(env, jbitmap, &pixels);
151     if (result < 0) {
152         throwIllegalStateException(env, (char*) "Cannot lock bitmap pixels");
153         return;
154     }
155 
156     // Write the pixels.
157     size_t byteCount = info.stride * info.height;
158     written = writeAllBytes(fd, (void*) pixels, byteCount);
159     if (!written) {
160         throwIllegalStateException(env, (char*) "Cannot write bitmap pixels");
161         return;
162     }
163 
164     // Unlock the pixels.
165     result = AndroidBitmap_unlockPixels(env, jbitmap);
166     if (result < 0) {
167         throwIllegalStateException(env, (char*) "Cannot unlock bitmap pixels");
168     }
169 }
170 
171 static const JNINativeMethod sMethods[] = {
172     {"nativeReadBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) readBitmapPixels},
173     {"nativeWriteBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) writeBitmapPixels},
174 };
175 
register_com_android_printspooler_util_BitmapSerializeUtils(JNIEnv * env)176 int register_com_android_printspooler_util_BitmapSerializeUtils(JNIEnv* env) {
177     return jniRegisterNativeMethods(env, "com/android/printspooler/util/BitmapSerializeUtils",
178         sMethods, NELEM(sMethods));
179 }
180 
181 }
182 
JNI_OnLoad(JavaVM * jvm,void *)183 jint JNI_OnLoad(JavaVM* jvm, void*) {
184     JNIEnv *env = NULL;
185     if (jvm->GetEnv((void**) &env, JNI_VERSION_1_6)) {
186         return JNI_ERR;
187     }
188 
189     if (android::register_com_android_printspooler_util_BitmapSerializeUtils(env) == -1) {
190         return JNI_ERR;
191     }
192 
193     return JNI_VERSION_1_6;
194 }
195