Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
16 #include <algorithm>
17 #include <endian.h>
18 #include <fcntl.h>
19 #include <string>
20 #include <sys/mman.h>
21 #include <sys/stat.h>
22 #include <vector>
23 #include "mozilla/FileUtils.h"
24 #include "mozilla/NullPtr.h"
25 #include "png.h"
27 #include "android/log.h"
28 #include "GonkDisplay.h"
29 #include "hardware/gralloc.h"
31 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
32 #define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "Gonk", ## args)
33 #define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "Gonk", ## args)
35 using namespace mozilla;
36 using namespace std;
38 static pthread_t sAnimationThread;
39 static bool sRunAnimation;
41 /* See http://www.pkware.com/documents/casestudies/APPNOTE.TXT */
42 struct local_file_header {
43 uint32_t signature;
44 uint16_t min_version;
45 uint16_t general_flag;
46 uint16_t compression;
47 uint16_t lastmod_time;
48 uint16_t lastmod_date;
49 uint32_t crc32;
50 uint32_t compressed_size;
51 uint32_t uncompressed_size;
52 uint16_t filename_size;
53 uint16_t extra_field_size;
54 char data[0];
56 uint32_t GetDataSize() const
57 {
58 return letoh32(uncompressed_size);
59 }
61 uint32_t GetSize() const
62 {
63 /* XXX account for data descriptor */
64 return sizeof(local_file_header) + letoh16(filename_size) +
65 letoh16(extra_field_size) + GetDataSize();
66 }
68 const char * GetData() const
69 {
70 return data + letoh16(filename_size) + letoh16(extra_field_size);
71 }
72 } __attribute__((__packed__));
74 struct data_descriptor {
75 uint32_t crc32;
76 uint32_t compressed_size;
77 uint32_t uncompressed_size;
78 } __attribute__((__packed__));
80 struct cdir_entry {
81 uint32_t signature;
82 uint16_t creator_version;
83 uint16_t min_version;
84 uint16_t general_flag;
85 uint16_t compression;
86 uint16_t lastmod_time;
87 uint16_t lastmod_date;
88 uint32_t crc32;
89 uint32_t compressed_size;
90 uint32_t uncompressed_size;
91 uint16_t filename_size;
92 uint16_t extra_field_size;
93 uint16_t file_comment_size;
94 uint16_t disk_num;
95 uint16_t internal_attr;
96 uint32_t external_attr;
97 uint32_t offset;
98 char data[0];
100 uint32_t GetDataSize() const
101 {
102 return letoh32(compressed_size);
103 }
105 uint32_t GetSize() const
106 {
107 return sizeof(cdir_entry) + letoh16(filename_size) +
108 letoh16(extra_field_size) + letoh16(file_comment_size);
109 }
111 bool Valid() const
112 {
113 return signature == htole32(0x02014b50);
114 }
115 } __attribute__((__packed__));
117 struct cdir_end {
118 uint32_t signature;
119 uint16_t disk_num;
120 uint16_t cdir_disk;
121 uint16_t disk_entries;
122 uint16_t cdir_entries;
123 uint32_t cdir_size;
124 uint32_t cdir_offset;
125 uint16_t comment_size;
126 char comment[0];
128 bool Valid() const
129 {
130 return signature == htole32(0x06054b50);
131 }
132 } __attribute__((__packed__));
134 /* We don't have access to libjar and the zip reader in android
135 * doesn't quite fit what we want to do. */
136 class ZipReader {
137 const char *mBuf;
138 const cdir_end *mEnd;
139 const char *mCdir_limit;
140 uint32_t mBuflen;
142 public:
143 ZipReader() : mBuf(nullptr) {}
144 ~ZipReader() {
145 if (mBuf)
146 munmap((void *)mBuf, mBuflen);
147 }
149 bool OpenArchive(const char *path)
150 {
151 int fd;
152 do {
153 fd = open(path, O_RDONLY);
154 } while (fd == -1 && errno == EINTR);
155 if (fd == -1)
156 return false;
158 struct stat sb;
159 if (fstat(fd, &sb) == -1 || sb.st_size < sizeof(cdir_end)) {
160 close(fd);
161 return false;
162 }
164 mBuflen = sb.st_size;
165 mBuf = (char *)mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
166 close(fd);
168 if (!mBuf) {
169 return false;
170 }
172 madvise(mBuf, sb.st_size, MADV_SEQUENTIAL);
174 mEnd = (cdir_end *)(mBuf + mBuflen - sizeof(cdir_end));
175 while (!mEnd->Valid() &&
176 (char *)mEnd > mBuf) {
177 mEnd = (cdir_end *)((char *)mEnd - 1);
178 }
180 mCdir_limit = mBuf + letoh32(mEnd->cdir_offset) + letoh32(mEnd->cdir_size);
182 if (!mEnd->Valid() || mCdir_limit > (char *)mEnd) {
183 munmap((void *)mBuf, mBuflen);
184 mBuf = nullptr;
185 return false;
186 }
188 return true;
189 }
191 /* Pass null to get the first cdir entry */
192 const cdir_entry * GetNextEntry(const cdir_entry *prev)
193 {
194 const cdir_entry *entry;
195 if (prev)
196 entry = (cdir_entry *)((char *)prev + prev->GetSize());
197 else
198 entry = (cdir_entry *)(mBuf + letoh32(mEnd->cdir_offset));
200 if (((char *)entry + entry->GetSize()) > mCdir_limit ||
201 !entry->Valid())
202 return nullptr;
203 return entry;
204 }
206 string GetEntryName(const cdir_entry *entry)
207 {
208 uint16_t len = letoh16(entry->filename_size);
210 string name;
211 name.append(entry->data, len);
212 return name;
213 }
215 const local_file_header * GetLocalEntry(const cdir_entry *entry)
216 {
217 const local_file_header * data =
218 (local_file_header *)(mBuf + letoh32(entry->offset));
219 if (((char *)data + data->GetSize()) > (char *)mEnd)
220 return nullptr;
221 return data;
222 }
223 };
225 struct AnimationFrame {
226 char path[256];
227 char *buf;
228 const local_file_header *file;
229 uint32_t width;
230 uint32_t height;
231 uint16_t bytepp;
233 AnimationFrame() : buf(nullptr) {}
234 AnimationFrame(const AnimationFrame &frame) : buf(nullptr) {
235 strncpy(path, frame.path, sizeof(path));
236 file = frame.file;
237 }
238 ~AnimationFrame()
239 {
240 if (buf)
241 free(buf);
242 }
244 bool operator<(const AnimationFrame &other) const
245 {
246 return strcmp(path, other.path) < 0;
247 }
249 void ReadPngFrame(int outputFormat);
250 };
252 struct AnimationPart {
253 int32_t count;
254 int32_t pause;
255 char path[256];
256 vector<AnimationFrame> frames;
257 };
259 struct RawReadState {
260 const char *start;
261 uint32_t offset;
262 uint32_t length;
263 };
265 static void
266 RawReader(png_structp png_ptr, png_bytep data, png_size_t length)
267 {
268 RawReadState *state = (RawReadState *)png_get_io_ptr(png_ptr);
269 if (length > (state->length - state->offset))
270 png_error(png_ptr, "PNG read overrun");
272 memcpy(data, state->start + state->offset, length);
273 state->offset += length;
274 }
276 static void
277 TransformTo565(png_structp png_ptr, png_row_infop row_info, png_bytep data)
278 {
279 uint16_t *outbuf = (uint16_t *)data;
280 uint8_t *inbuf = (uint8_t *)data;
281 for (int i = 0; i < row_info->rowbytes; i += 3) {
282 *outbuf++ = ((inbuf[i] & 0xF8) << 8) |
283 ((inbuf[i + 1] & 0xFC) << 3) |
284 ((inbuf[i + 2] ) >> 3);
285 }
286 }
288 void
289 AnimationFrame::ReadPngFrame(int outputFormat)
290 {
291 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
292 static const png_byte unused_chunks[] =
293 { 98, 75, 71, 68, '\0', /* bKGD */
294 99, 72, 82, 77, '\0', /* cHRM */
295 104, 73, 83, 84, '\0', /* hIST */
296 105, 67, 67, 80, '\0', /* iCCP */
297 105, 84, 88, 116, '\0', /* iTXt */
298 111, 70, 70, 115, '\0', /* oFFs */
299 112, 67, 65, 76, '\0', /* pCAL */
300 115, 67, 65, 76, '\0', /* sCAL */
301 112, 72, 89, 115, '\0', /* pHYs */
302 115, 66, 73, 84, '\0', /* sBIT */
303 115, 80, 76, 84, '\0', /* sPLT */
304 116, 69, 88, 116, '\0', /* tEXt */
305 116, 73, 77, 69, '\0', /* tIME */
306 122, 84, 88, 116, '\0'}; /* zTXt */
307 static const png_byte tRNS_chunk[] =
308 {116, 82, 78, 83, '\0'}; /* tRNS */
309 #endif
311 png_structp pngread = png_create_read_struct(PNG_LIBPNG_VER_STRING,
312 nullptr, nullptr, nullptr);
314 if (!pngread)
315 return;
317 png_infop pnginfo = png_create_info_struct(pngread);
319 if (!pnginfo) {
320 png_destroy_read_struct(&pngread, &pnginfo, nullptr);
321 return;
322 }
324 if (setjmp(png_jmpbuf(pngread))) {
325 // libpng reported an error and longjumped here. Clean up and return.
326 png_destroy_read_struct(&pngread, &pnginfo, nullptr);
327 return;
328 }
330 RawReadState state;
331 state.start = file->GetData();
332 state.length = file->GetDataSize();
333 state.offset = 0;
335 png_set_read_fn(pngread, &state, RawReader);
337 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
338 /* Ignore unused chunks */
339 png_set_keep_unknown_chunks(pngread, 1, unused_chunks,
340 (int)sizeof(unused_chunks)/5);
342 /* Ignore the tRNS chunk if we only want opaque output */
343 if (outputFormat == HAL_PIXEL_FORMAT_RGB_888 ||
344 outputFormat == HAL_PIXEL_FORMAT_RGB_565) {
345 png_set_keep_unknown_chunks(pngread, 1, tRNS_chunk, 1);
346 }
347 #endif
349 png_read_info(pngread, pnginfo);
351 width = png_get_image_width(pngread, pnginfo);
352 height = png_get_image_height(pngread, pnginfo);
354 switch (outputFormat) {
355 case HAL_PIXEL_FORMAT_BGRA_8888:
356 png_set_bgr(pngread);
357 // FALL THROUGH
358 case HAL_PIXEL_FORMAT_RGBA_8888:
359 case HAL_PIXEL_FORMAT_RGBX_8888:
360 bytepp = 4;
361 png_set_filler(pngread, 0xFF, PNG_FILLER_AFTER);
362 break;
363 case HAL_PIXEL_FORMAT_RGB_888:
364 bytepp = 3;
365 png_set_strip_alpha(pngread);
366 break;
367 default:
368 LOGW("Unknown pixel format %d. Assuming RGB 565.", outputFormat);
369 // FALL THROUGH
370 case HAL_PIXEL_FORMAT_RGB_565:
371 bytepp = 2;
372 png_set_strip_alpha(pngread);
373 png_set_read_user_transform_fn(pngread, TransformTo565);
374 break;
375 }
377 // An extra row is added to give libpng enough space when
378 // decoding 3/4 bytepp inputs for 2 bytepp output surfaces
379 buf = (char *)malloc(width * (height + 1) * bytepp);
381 vector<char *> rows(height + 1);
382 uint32_t stride = width * bytepp;
383 for (int i = 0; i < height; i++) {
384 rows[i] = buf + (stride * i);
385 }
386 rows[height] = nullptr;
387 png_set_strip_16(pngread);
388 png_set_palette_to_rgb(pngread);
389 png_set_gray_to_rgb(pngread);
390 png_read_image(pngread, (png_bytepp)&rows.front());
391 png_destroy_read_struct(&pngread, &pnginfo, nullptr);
392 }
394 static void *
395 AnimationThread(void *)
396 {
397 ZipReader reader;
398 if (!reader.OpenArchive("/system/media/bootanimation.zip")) {
399 LOGW("Could not open boot animation");
400 return nullptr;
401 }
403 const cdir_entry *entry = nullptr;
404 const local_file_header *file = nullptr;
405 while ((entry = reader.GetNextEntry(entry))) {
406 string name = reader.GetEntryName(entry);
407 if (!name.compare("desc.txt")) {
408 file = reader.GetLocalEntry(entry);
409 break;
410 }
411 }
413 if (!file) {
414 LOGW("Could not find desc.txt in boot animation");
415 return nullptr;
416 }
418 GonkDisplay *display = GetGonkDisplay();
419 int format = display->surfaceformat;
421 hw_module_t const *module;
422 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) {
423 LOGW("Could not get gralloc module");
424 return nullptr;
425 }
426 gralloc_module_t const *grmodule =
427 reinterpret_cast<gralloc_module_t const*>(module);
429 string descCopy;
430 descCopy.append(file->GetData(), entry->GetDataSize());
431 int32_t width, height, fps;
432 const char *line = descCopy.c_str();
433 const char *end;
434 bool headerRead = true;
435 vector<AnimationPart> parts;
437 /*
438 * bootanimation.zip
439 *
440 * This is the boot animation file format that Android uses.
441 * It's a zip file with a directories containing png frames
442 * and a desc.txt that describes how they should be played.
443 *
444 * desc.txt contains two types of lines
445 * 1. [width] [height] [fps]
446 * There is one of these lines per bootanimation.
447 * If the width and height are smaller than the screen,
448 * the frames are centered on a black background.
449 * XXX: Currently we stretch instead of centering the frame.
450 * 2. p [count] [pause] [path]
451 * This describes one animation part.
452 * Each animation part is played in sequence.
453 * An animation part contains all the files/frames in the
454 * directory specified in [path]
455 * [count] indicates the number of times this part repeats.
456 * [pause] indicates the number of frames that this part
457 * should pause for after playing the full sequence but
458 * before repeating.
459 */
461 do {
462 end = strstr(line, "\n");
464 AnimationPart part;
465 if (headerRead &&
466 sscanf(line, "%d %d %d", &width, &height, &fps) == 3) {
467 headerRead = false;
468 } else if (sscanf(line, "p %d %d %s",
469 &part.count, &part.pause, part.path)) {
470 parts.push_back(part);
471 }
472 } while (end && *(line = end + 1));
474 for (uint32_t i = 0; i < parts.size(); i++) {
475 AnimationPart &part = parts[i];
476 entry = nullptr;
477 char search[256];
478 snprintf(search, sizeof(search), "%s/", part.path);
479 while ((entry = reader.GetNextEntry(entry))) {
480 string name = reader.GetEntryName(entry);
481 if (name.find(search) ||
482 !entry->GetDataSize() ||
483 name.length() >= 256)
484 continue;
486 part.frames.push_back();
487 AnimationFrame &frame = part.frames.back();
488 strcpy(frame.path, name.c_str());
489 frame.file = reader.GetLocalEntry(entry);
490 }
492 sort(part.frames.begin(), part.frames.end());
493 }
495 uint32_t frameDelayUs = 1000000 / fps;
497 for (uint32_t i = 0; i < parts.size(); i++) {
498 AnimationPart &part = parts[i];
500 uint32_t j = 0;
501 while (sRunAnimation && (!part.count || j++ < part.count)) {
502 for (uint32_t k = 0; k < part.frames.size(); k++) {
503 struct timeval tv1, tv2;
504 gettimeofday(&tv1, nullptr);
505 AnimationFrame &frame = part.frames[k];
506 if (!frame.buf) {
507 frame.ReadPngFrame(format);
508 }
510 ANativeWindowBuffer *buf = display->DequeueBuffer();
511 if (!buf) {
512 LOGW("Failed to get an ANativeWindowBuffer");
513 break;
514 }
516 void *vaddr;
517 if (grmodule->lock(grmodule, buf->handle,
518 GRALLOC_USAGE_SW_READ_NEVER |
519 GRALLOC_USAGE_SW_WRITE_OFTEN |
520 GRALLOC_USAGE_HW_FB,
521 0, 0, width, height, &vaddr)) {
522 LOGW("Failed to lock buffer_handle_t");
523 display->QueueBuffer(buf);
524 break;
525 }
527 if (buf->height == frame.height && buf->width == frame.width) {
528 memcpy(vaddr, frame.buf,
529 frame.width * frame.height * frame.bytepp);
530 } else if (buf->height >= frame.height &&
531 buf->width >= frame.width) {
532 int startx = (buf->width - frame.width) / 2;
533 int starty = (buf->height - frame.height) / 2;
535 int src_stride = frame.width * frame.bytepp;
536 int dst_stride = buf->stride * frame.bytepp;
538 char *src = frame.buf;
539 char *dst = (char *) vaddr + starty * dst_stride + startx * frame.bytepp;
541 for (int i = 0; i < frame.height; i++) {
542 memcpy(dst, src, src_stride);
543 src += src_stride;
544 dst += dst_stride;
545 }
546 }
547 grmodule->unlock(grmodule, buf->handle);
549 gettimeofday(&tv2, nullptr);
551 timersub(&tv2, &tv1, &tv2);
553 if (tv2.tv_usec < frameDelayUs) {
554 usleep(frameDelayUs - tv2.tv_usec);
555 } else {
556 LOGW("Frame delay is %d us but decoding took %d us",
557 frameDelayUs, tv2.tv_usec);
558 }
560 display->QueueBuffer(buf);
562 if (part.count && j >= part.count) {
563 free(frame.buf);
564 frame.buf = nullptr;
565 }
566 }
567 usleep(frameDelayUs * part.pause);
568 }
569 }
571 return nullptr;
572 }
574 void
575 StartBootAnimation()
576 {
577 sRunAnimation = true;
578 pthread_create(&sAnimationThread, nullptr, AnimationThread, nullptr);
579 }
582 void
583 StopBootAnimation()
584 {
585 if (sRunAnimation) {
586 sRunAnimation = false;
587 pthread_join(sAnimationThread, nullptr);
588 }
589 }