From 3ccf1d0424bc014f3eeacbe28c0891bafb772d9f Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Mon, 4 Jul 2016 12:11:03 +0200 Subject: [PATCH] Add a copy of the boot animation service Will show a app specific icon on boot. --- android/bootanimation/Android.mk | 53 +++ android/bootanimation/BootAnimation.cpp | 323 +++++++++++++++++++ android/bootanimation/BootAnimation.h | 106 ++++++ android/bootanimation/bootanimation_main.cpp | 69 ++++ 4 files changed, 551 insertions(+) create mode 100644 android/bootanimation/Android.mk create mode 100644 android/bootanimation/BootAnimation.cpp create mode 100644 android/bootanimation/BootAnimation.h create mode 100644 android/bootanimation/bootanimation_main.cpp diff --git a/android/bootanimation/Android.mk b/android/bootanimation/Android.mk new file mode 100644 index 0000000..7f2daaf --- /dev/null +++ b/android/bootanimation/Android.mk @@ -0,0 +1,53 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + bootanimation_main.cpp \ + BootAnimation.cpp + +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + +LOCAL_C_INCLUDES += external/tinyalsa/include + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + liblog \ + libandroidfw \ + libutils \ + libbinder \ + libui \ + libskia \ + libEGL \ + libGLESv1_CM \ + libgui \ + libtinyalsa + +LOCAL_MODULE := anbox_bootanimation + +LOCAL_OVERRIDES_PACKAGES := bootanimation + +ifdef TARGET_32_BIT_SURFACEFLINGER +LOCAL_32_BIT_ONLY := true +endif + +include $(BUILD_EXECUTABLE) + +# +# Add symlink to bootanimation +# + +ALL_TOOLS:= bootanimation +SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(ALL_TOOLS)) +$(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE) +$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk + @echo "Symlink: $@ -> $(TOOLBOX_BINARY)" + @mkdir -p $(dir $@) + @rm -rf $@ + $(hide) ln -sf $(TOOLBOX_BINARY) $@ + +ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS) + +# We need this so that the installed files could be picked up based on the +# local module name +ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \ + $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS) diff --git a/android/bootanimation/BootAnimation.cpp b/android/bootanimation/BootAnimation.cpp new file mode 100644 index 0000000..898e17e --- /dev/null +++ b/android/bootanimation/BootAnimation.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "BootAnimation" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "BootAnimation.h" + +#define EXIT_PROP_NAME "service.bootanim.exit" + +#ifndef D +# define D(...) do{}while(0) +#endif + +extern "C" int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain); + +namespace android { + +static const int ANIM_ENTRY_NAME_MAX = 256; + +// --------------------------------------------------------------------------- + +BootAnimation::BootAnimation() : Thread(false) +{ + mSession = new SurfaceComposerClient(); +} + +BootAnimation::~BootAnimation() { +} + +void BootAnimation::onFirstRef() { + status_t err = mSession->linkToComposerDeath(this); + ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err)); + if (err == NO_ERROR) { + run("BootAnimation", PRIORITY_DISPLAY); + } +} + +sp BootAnimation::session() const { + return mSession; +} + + +void BootAnimation::binderDied(const wp&) +{ + // woah, surfaceflinger died! + ALOGD("SurfaceFlinger died, exiting..."); + + // calling requestExit() is not enough here because the Surface code + // might be blocked on a condition variable that will never be updated. + kill( getpid(), SIGKILL ); + requestExit(); +} + +status_t BootAnimation::initTexture(Texture* texture, void* imageData, size_t size) +{ + SkBitmap bitmap; + + SkImageDecoder::DecodeMemory(imageData, size, + &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode); + + // ensure we can call getPixels(). No need to call unlock, since the + // bitmap will go out of scope when we return from this method. + bitmap.lockPixels(); + + const int w = bitmap.width(); + const int h = bitmap.height(); + const void* p = bitmap.getPixels(); + + GLint crop[4] = { 0, h, w, -h }; + texture->w = w; + texture->h = h; + + glGenTextures(1, &texture->name); + glBindTexture(GL_TEXTURE_2D, texture->name); + + switch (bitmap.colorType()) { + case kAlpha_8_SkColorType: + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, + GL_UNSIGNED_BYTE, p); + break; + case kARGB_4444_SkColorType: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_SHORT_4_4_4_4, p); + break; + case kN32_SkColorType: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, p); + break; + case kRGB_565_SkColorType: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, p); + break; + default: + break; + } + + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + return NO_ERROR; +} + +status_t BootAnimation::readyToRun() { + mAssets.addDefaultAssets(); + + sp dtoken(SurfaceComposerClient::getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain)); + DisplayInfo dinfo; + status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo); + if (status) + return -1; + + // create the native surface + sp control = session()->createSurface(String8("BootAnimation"), + dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); + + SurfaceComposerClient::openGlobalTransaction(); + control->setLayer(0x40000000); + SurfaceComposerClient::closeGlobalTransaction(); + + sp s = control->getSurface(); + + // initialize opengl and egl + const EGLint attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_NONE + }; + EGLint w, h, dummy; + EGLint numConfigs; + EGLConfig config; + EGLSurface surface; + EGLContext context; + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + eglInitialize(display, 0, 0); + eglChooseConfig(display, attribs, &config, 1, &numConfigs); + surface = eglCreateWindowSurface(display, config, s.get(), NULL); + context = eglCreateContext(display, config, NULL, NULL); + eglQuerySurface(display, surface, EGL_WIDTH, &w); + eglQuerySurface(display, surface, EGL_HEIGHT, &h); + + if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) + return NO_INIT; + + mDisplay = display; + mContext = context; + mSurface = surface; + mWidth = w; + mHeight = h; + mFlingerSurfaceControl = control; + mFlingerSurface = s; + + return NO_ERROR; +} + +bool BootAnimation::threadLoop() +{ + bool r; + r = android(); + + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(mDisplay, mContext); + eglDestroySurface(mDisplay, mSurface); + mFlingerSurface.clear(); + mFlingerSurfaceControl.clear(); + eglTerminate(mDisplay); + IPCThreadState::self()->stopProcess(); + return r; +} + +bool BootAnimation::android() +{ + int fd = qemu_pipe_open("anbox:bootanimation"); + if(!fd) { + return false; + } + + qemud_channel_send(fd, "retrieve-icon", -1); + + unsigned long current_size = 0; + unsigned long bytes_received = 0; + void *buffer = 0; + do + { + current_size += 4096; + buffer = realloc(buffer, current_size); + int t = read(fd, buffer + bytes_received, 4096); + if (t <= 0) + break; + printf("received %d bytes\n", t); + bytes_received += t; + } while(1); + + close(fd); + + if (bytes_received == 0) { + return false; + } + + initTexture(&mAndroid[0], buffer, bytes_received); + + // clear screen + glShadeModel(GL_FLAT); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mDisplay, mSurface); + + glEnable(GL_TEXTURE_2D); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + const GLint xc = (mWidth - mAndroid[0].w) / 2; + const GLint yc = (mHeight - mAndroid[0].h) / 2; + const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); + + glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), + updateRect.height()); + + const nsecs_t startTime = systemTime(); + do { + nsecs_t now = systemTime(); + double time = now - startTime; + + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + + glEnable(GL_BLEND); + + glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); + glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h); + + EGLBoolean res = eglSwapBuffers(mDisplay, mSurface); + if (res == EGL_FALSE) + break; + + // 12fps: don't animate too fast to preserve CPU + const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now); + if (sleepTime > 0) + usleep(sleepTime); + + checkExit(); + } while (!exitPending()); + + glDeleteTextures(1, &mAndroid[0].name); + return false; +} + + +void BootAnimation::checkExit() { + // Allow surface flinger to gracefully request shutdown + char value[PROPERTY_VALUE_MAX]; + property_get(EXIT_PROP_NAME, value, "0"); + int exitnow = atoi(value); + if (exitnow) { + requestExit(); + } +} + +// --------------------------------------------------------------------------- + +} +; // namespace android diff --git a/android/bootanimation/BootAnimation.h b/android/bootanimation/BootAnimation.h new file mode 100644 index 0000000..0eb243f --- /dev/null +++ b/android/bootanimation/BootAnimation.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BOOTANIMATION_H +#define ANDROID_BOOTANIMATION_H + +#include +#include + +#include +#include + +#include +#include + +class SkBitmap; + +namespace android { + +class Surface; +class SurfaceComposerClient; +class SurfaceControl; + +// --------------------------------------------------------------------------- + +class BootAnimation : public Thread, public IBinder::DeathRecipient +{ +public: + BootAnimation(); + virtual ~BootAnimation(); + + sp session() const; + +private: + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + virtual void binderDied(const wp& who); + + struct Texture { + GLint w; + GLint h; + GLuint name; + }; + + struct Animation { + struct Frame { + String8 name; + FileMap* map; + mutable GLuint tid; + bool operator < (const Frame& rhs) const { + return name < rhs.name; + } + }; + struct Part { + int count; + int pause; + String8 path; + SortedVector frames; + bool playUntilComplete; + float backgroundColor[3]; + FileMap* audioFile; + }; + int fps; + int width; + int height; + Vector parts; + }; + + status_t initTexture(Texture* texture, void *buffer, size_t size); + bool android(); + bool movie(); + + void checkExit(); + + sp mSession; + AssetManager mAssets; + Texture mAndroid[2]; + int mWidth; + int mHeight; + EGLDisplay mDisplay; + EGLDisplay mContext; + EGLDisplay mSurface; + sp mFlingerSurfaceControl; + sp mFlingerSurface; + ZipFileRO *mZip; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_BOOTANIMATION_H diff --git a/android/bootanimation/bootanimation_main.cpp b/android/bootanimation/bootanimation_main.cpp new file mode 100644 index 0000000..2a1dee6 --- /dev/null +++ b/android/bootanimation/bootanimation_main.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BootAnimation" + +#include + +#include +#include +#include + +#include +#include + +#if defined(HAVE_PTHREADS) +# include +# include +#endif + +#include "BootAnimation.h" + +#include +#include + +using namespace android; + +#ifndef D +# define D(...) do{}while(0) +#endif + + +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ +#if defined(HAVE_PTHREADS) + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY); +#endif + + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.nobootanimation", value, "0"); + int noBootAnimation = atoi(value); + ALOGI_IF(noBootAnimation, "boot animation disabled"); + if (!noBootAnimation) { + + sp proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + // create the boot animation object + sp boot = new BootAnimation(); + + IPCThreadState::self()->joinThreadPool(); + + } + return 0; +}