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