Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 142bd641 authored by Chiou-Hao Hsu's avatar Chiou-Hao Hsu Committed by Ricardo Cerqueira
Browse files

BootAnimation: Play boot/shutdown animation and music

Add support to show customized boot and shut down animation, as
well as music.

Change-Id: I69c87640175a96a18833a763a34afd362bbfb487
Conflicts:
cmds/bootanimation/Android.mk
cmds/bootanimation/BootAnimation.cpp
parent 4e56d949
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -21,7 +21,8 @@ LOCAL_SHARED_LIBRARIES := \
    libEGL \
    libGLESv1_CM \
    libgui \
    libtinyalsa
    libtinyalsa \
    libmedia

LOCAL_MODULE:= bootanimation

+199 −6
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 The Android Open Source Project
 * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -23,6 +24,8 @@
#include <fcntl.h>
#include <utils/misc.h>
#include <signal.h>
#include <pthread.h>
#include <sys/select.h>

#include <cutils/properties.h>

@@ -49,12 +52,26 @@
#include <GLES/glext.h>
#include <EGL/eglext.h>

#include <media/AudioSystem.h>
#include <media/mediaplayer.h>

#include "BootAnimation.h"
#include "AudioPlayer.h"

#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"

#define OEM_SHUTDOWN_ANIMATION_FILE "/oem/media/shutdownanimation.zip"
#define SYSTEM_SHUTDOWN_ANIMATION_FILE "/system/media/shutdownanimation.zip"
#define SYSTEM_ENCRYPTED_SHUTDOWN_ANIMATION_FILE "/system/media/shutdownanimation-encrypted.zip"

#define OEM_BOOT_MUSIC_FILE "/oem/media/boot.wav"
#define SYSTEM_BOOT_MUSIC_FILE "/system/media/boot.wav"

#define OEM_SHUTDOWN_MUSIC_FILE "/oem/media/shutdown.wav"
#define SYSTEM_SHUTDOWN_MUSIC_FILE "/system/media/shutdown.wav"

#define EXIT_PROP_NAME "service.bootanim.exit"

extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
@@ -67,6 +84,36 @@ static const int ANIM_ENTRY_NAME_MAX = 256;

// ---------------------------------------------------------------------------

static pthread_mutex_t mp_lock;
static pthread_cond_t mp_cond;
static bool isMPlayerPrepared = false;
static bool isMPlayerCompleted = false;

class MPlayerListener : public MediaPlayerListener
{
    void notify(int msg, int ext1, int ext2, const Parcel *obj)
    {
        switch (msg) {
        case MEDIA_NOP: // interface test message
            break;
        case MEDIA_PREPARED:
            pthread_mutex_lock(&mp_lock);
            isMPlayerPrepared = true;
            pthread_cond_signal(&mp_cond);
            pthread_mutex_unlock(&mp_lock);
            break;
        case MEDIA_PLAYBACK_COMPLETE:
            pthread_mutex_lock(&mp_lock);
            isMPlayerCompleted = true;
            pthread_cond_signal(&mp_cond);
            pthread_mutex_unlock(&mp_lock);
            break;
        default:
            break;
        }
    }
};

BootAnimation::BootAnimation() : Thread(false), mZip(NULL)
{
    mSession = new SurfaceComposerClient();
@@ -286,18 +333,20 @@ status_t BootAnimation::readyToRun() {
    char decrypt[PROPERTY_VALUE_MAX];
    property_get("vold.decrypt", decrypt, "");

    // Use customized resources for boot and showdown animation
    // instead of system predefined boot animation files.
    bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);

    ZipFileRO* zipFile = NULL;
    if ((encryptedAnimation &&
            (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
            (access(getAnimationFileName(IMG_ENC), R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(getAnimationFileName(IMG_ENC))) != NULL)) ||

            ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
            ((access(getAnimationFileName(IMG_DATA), R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(getAnimationFileName(IMG_DATA))) != NULL)) ||

            ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
            ((access(getAnimationFileName(IMG_SYS), R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(getAnimationFileName(IMG_SYS))) != NULL))) {
        mZip = zipFile;
    }

@@ -451,6 +500,7 @@ bool BootAnimation::readFile(const char* name, String8& outString)

bool BootAnimation::movie()
{
    char value[PROPERTY_VALUE_MAX];
    String8 desString;

    if (!readFile("desc.txt", desString)) {
@@ -581,6 +631,13 @@ bool BootAnimation::movie()
    Region clearReg(Rect(mWidth, mHeight));
    clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));

    pthread_mutex_init(&mp_lock, NULL);
    pthread_cond_init(&mp_cond, NULL);

    property_get("persist.sys.silent", value, "null");
    if (strncmp(value, "1", 1) != 0) {
        playBackgroundMusic();
    }
    for (size_t i=0 ; i<pcount ; i++) {
        const Animation::Part& part(animation.parts[i]);
        const size_t fcount = part.frames.size();
@@ -667,9 +724,145 @@ bool BootAnimation::movie()
        }
    }

    ALOGD("waiting for media player to complete.");
    struct timespec timeout;
    clock_gettime(CLOCK_REALTIME, &timeout);
    timeout.tv_sec += 5; //timeout after 5s.

    pthread_mutex_lock(&mp_lock);
    while (!isMPlayerCompleted) {
        int err = pthread_cond_timedwait(&mp_cond, &mp_lock, &timeout);
        if (err == ETIMEDOUT) {
            break;
        }
    }
    pthread_mutex_unlock(&mp_lock);
    ALOGD("media player is completed.");

    pthread_cond_destroy(&mp_cond);
    pthread_mutex_destroy(&mp_lock);

    return false;
}

char *BootAnimation::getAnimationFileName(ImageID image)
{
    char *fileName[2][3] = { { OEM_BOOTANIMATION_FILE,
            SYSTEM_BOOTANIMATION_FILE,
            SYSTEM_ENCRYPTED_BOOTANIMATION_FILE }, {
            OEM_SHUTDOWN_ANIMATION_FILE,
            SYSTEM_SHUTDOWN_ANIMATION_FILE,
            SYSTEM_ENCRYPTED_SHUTDOWN_ANIMATION_FILE} };
    int state;

    state = checkBootState() ? 0 : 1;

    return fileName[state][image];
}

char *BootAnimation::getBootRingtoneFileName(ImageID image)
{
    if (image == IMG_ENC) {
        return NULL;
    }

    char *fileName[2][2] = { { OEM_BOOT_MUSIC_FILE,
            SYSTEM_BOOT_MUSIC_FILE }, {
            OEM_SHUTDOWN_MUSIC_FILE,
            SYSTEM_SHUTDOWN_MUSIC_FILE } };
    int state;

    state = checkBootState() ? 0 : 1;

    return fileName[state][image];
}


void BootAnimation::playBackgroundMusic(void)
{
    //Shutdown music is playing in ShutdownThread.java
    if (!checkBootState()) {
        return;
    }

    /* Make sure sound cards are populated */
    FILE* fp = NULL;
    if ((fp = fopen("/proc/asound/cards", "r")) == NULL) {
        ALOGW("Cannot open /proc/asound/cards file to get sound card info.");
    }

    char value[PROPERTY_VALUE_MAX];
    property_get("qcom.audio.init", value, "null");
    if (strncmp(value, "complete", 8) != 0) {
        ALOGW("Audio service is not initiated.");
    }

    fclose(fp);

    char *fileName;
    if (((fileName = getBootRingtoneFileName(IMG_DATA)) != NULL && access(fileName, R_OK) == 0) ||
                ((fileName = getBootRingtoneFileName(IMG_SYS)) != NULL
                && access(fileName, R_OK) == 0)) {
        pthread_t tid;
        pthread_create(&tid, NULL, playMusic, (void *)fileName);
        pthread_join(tid, NULL);
    }
}
bool BootAnimation::checkBootState(void)
{
    char value[PROPERTY_VALUE_MAX];
    bool ret = true;

    property_get("sys.shutdown.requested", value, "null");
    if (strncmp(value, "null", 4) != 0) {
        ret = false;
    }

    return ret;
}

void* playMusic(void* arg)
{
    int index = 0;
    char *fileName = (char *)arg;
    sp<MediaPlayer> mp = new MediaPlayer();
    sp<MPlayerListener> mListener = new MPlayerListener();
    if (mp != NULL) {
        ALOGD("starting to play %s", fileName);
        mp->setListener(mListener);

        FILE * fp = fopen(fileName, "r");
        if (fp == NULL) {
            ALOGW("failed to open %s", fileName);
            fclose(fp);
            return NULL;
        }
        fseek(fp, 0, SEEK_END);
        int length = ftell(fp);
        rewind(fp);

        if (mp->setDataSource((long)fp, 0, length) == NO_ERROR) {
            mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE);
            mp->prepare();
        }

        audio_devices_t device = AudioSystem::getDevicesForStream(AUDIO_STREAM_ENFORCED_AUDIBLE);
        AudioSystem::initStreamVolume(AUDIO_STREAM_ENFORCED_AUDIBLE,0,7);
        AudioSystem::setStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, 7, device);

        AudioSystem::getStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, &index, device);
        if (index != 0) {
            ALOGD("playing %s", fileName);
            mp->seekTo(0);
            mp->start();
        } else {
            ALOGW("current volume is zero.");
        }

        fclose(fp);
    }
    return NULL;
}
// ---------------------------------------------------------------------------

}
+8 −1
Original line number Diff line number Diff line
@@ -87,12 +87,18 @@ private:
    bool readFile(const char* name, String8& outString);
    bool movie();

    enum ImageID { IMG_DATA = 0, IMG_SYS = 1, IMG_ENC = 2 };
    char *getAnimationFileName(ImageID image);
    char *getBootRingtoneFileName(ImageID image);
    void playBackgroundMusic();
    bool checkBootState();
    void checkExit();
    void checkShowAndroid();

    sp<SurfaceComposerClient>       mSession;
    sp<AudioPlayer>                 mAudioPlayer;
    AssetManager mAssets;
    Texture     mAndroid[2];
    Texture     mAndroid[3];
    int         mWidth;
    int         mHeight;
    EGLDisplay  mDisplay;
@@ -103,6 +109,7 @@ private:
    ZipFileRO   *mZip;
};

static void* playMusic(void* arg);
// ---------------------------------------------------------------------------

}; // namespace android
+175 −36
Original line number Diff line number Diff line
@@ -32,7 +32,11 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -46,12 +50,22 @@ import android.os.storage.IMountShutdownObserver;

import com.android.internal.telephony.ITelephony;
import com.android.server.pm.PackageManagerService;

import com.android.server.power.PowerManagerService;
import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManager;
import java.lang.reflect.Method;
import dalvik.system.PathClassLoader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;

import java.lang.reflect.Method;

public final class ShutdownThread extends Thread {
    // constants
    private static final String TAG = "ShutdownThread";
@@ -93,9 +107,20 @@ public final class ShutdownThread extends Thread {
    private PowerManager.WakeLock mCpuWakeLock;
    private PowerManager.WakeLock mScreenWakeLock;
    private Handler mHandler;
    private static MediaPlayer mMediaPlayer;
    private static final String OEM_BOOTANIMATION_FILE = "/oem/media/shutdownanimation.zip";
    private static final String SYSTEM_BOOTANIMATION_FILE = "/system/media/shutdownanimation.zip";
    private static final String SYSTEM_ENCRYPTED_BOOTANIMATION_FILE = "/system/media/shutdownanimation-encrypted.zip";

    private static final String SHUTDOWN_MUSIC_FILE = "/system/media/shutdown.wav";
    private static final String OEM_SHUTDOWN_MUSIC_FILE = "/oem/media/shutdown.wav";

    private boolean isShutdownMusicPlaying = false;

    private static AlertDialog sConfirmDialog;

    private static AudioManager mAudioManager;
    
    private ShutdownThread() {
    }
 
@@ -157,7 +182,8 @@ public final class ShutdownThread extends Thread {
                                    ? com.android.internal.R.string.reboot_title
                                    : com.android.internal.R.string.power_off)
                    .setMessage(resourceId)
                    .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                    .setPositiveButton(com.android.internal.R.string.yes,
                            new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            beginShutdownSequence(context);
                        }
@@ -210,6 +236,27 @@ public final class ShutdownThread extends Thread {
        shutdownInner(context, confirm);
    }

    private static String getShutdownMusicFilePath() {
        final String[] fileName = {OEM_SHUTDOWN_MUSIC_FILE, SHUTDOWN_MUSIC_FILE};
        File checkFile = null;
        for(String music : fileName) {
            checkFile = new File(music);
            if (checkFile.exists()) {
                return music;
            }
        }
        return null;
    }

    private static void lockDevice() {
        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager
                .getService(Context.WINDOW_SERVICE));
        try {
            wm.updateRotation(false, false);
        } catch (RemoteException e) {
            Log.w(TAG, "boot animation can not lock device!");
        }
    }
    /**
     * Request a reboot into safe mode.  Must be called from a Looper thread in which its UI
     * is shown.
@@ -233,6 +280,12 @@ public final class ShutdownThread extends Thread {
            sIsStarted = true;
        }

        //acquire audio focus to make the other apps to stop playing muisc
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mAudioManager.requestAudioFocus(null,
                AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

        if (!checkAnimationFileExist()) {
            // throw up an indeterminate system dialog to indicate radio is
            // shutting down.
            ProgressDialog pd = new ProgressDialog(context);
@@ -243,6 +296,7 @@ public final class ShutdownThread extends Thread {
            pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

            pd.show();
        }

        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -359,6 +413,40 @@ public final class ShutdownThread extends Thread {
            pm.shutdown();
        }

        String shutDownFile = null;

        //showShutdownAnimation() is called from here to sync
        //music and animation properly
        if(checkAnimationFileExist()) {
            lockDevice();
            showShutdownAnimation();

            if (!isSilentMode()
                    && (shutDownFile = getShutdownMusicFilePath()) != null) {
                isShutdownMusicPlaying = true;
                shutdownMusicHandler.obtainMessage(0, shutDownFile).sendToTarget();
            }
        }

        Log.i(TAG, "wait for shutdown music");
        final long endTimeForMusic = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
        synchronized (mActionDoneSync) {
            while (isShutdownMusicPlaying) {
                long delay = endTimeForMusic - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "play shutdown music timeout!");
                    break;
                }
                try {
                    mActionDoneSync.wait(delay);
                } catch (InterruptedException e) {
                }
            }
            if (!isShutdownMusicPlaying) {
                Log.i(TAG, "play shutdown music complete.");
            }
        }

        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);

@@ -423,8 +511,7 @@ public final class ShutdownThread extends Thread {
                        BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));

                try {
                    nfcOff = nfc == null ||
                             nfc.getState() == NfcAdapter.STATE_OFF;
                    nfcOff = nfc == null || nfc.getState() == NfcAdapter.STATE_OFF;
                    if (!nfcOff) {
                        Log.w(TAG, "Turning off NFC...");
                        nfc.disable(false); // Don't persist new state
@@ -560,9 +647,10 @@ public final class ShutdownThread extends Thread {
                m = cl.getMethod("rebootOrShutdown", new Class[] {boolean.class, String.class});
                m.invoke(cl.newInstance(), reboot, reason);
            } catch (NoSuchMethodException ex) {
                               Log.e(TAG, "rebootOrShutdown method not found in class " + deviceShutdownClassName);
                Log.e(TAG, "rebootOrShutdown method not found in class "
                        + deviceShutdownClassName);
            } catch (Exception ex) {
                               Log.e(TAG, "Unknown exception hit while trying to invode rebootOrShutdown");
                Log.e(TAG, "Unknown exception hit while trying to invoke rebootOrShutdown");
            }
        } catch(ClassNotFoundException e) {
            Log.e(TAG, "Unable to find class " + deviceShutdownClassName);
@@ -570,5 +658,56 @@ public final class ShutdownThread extends Thread {
            Log.e(TAG, "Unknown exception while trying to invoke rebootOrShutdown");
        }
    }

    private static boolean checkAnimationFileExist() {
        if (new File(OEM_BOOTANIMATION_FILE).exists()
                || new File(SYSTEM_BOOTANIMATION_FILE).exists()
                || new File(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE).exists())
            return true;
        else
            return false;
    }

    private static boolean isSilentMode() {
        return mAudioManager.isSilentMode();
    }

    private static void showShutdownAnimation() {
        /*
         * When boot completed, "service.bootanim.exit" property is set to 1.
         * Bootanimation checks this property to stop showing the boot animation.
         * Since we use the same code for shutdown animation, we
         * need to reset this property to 0. If this is not set to 0 then shutdown
         * will stop and exit after displaying the first frame of the animation
         */
        SystemProperties.set("service.bootanim.exit", "0");

        SystemProperties.set("ctl.start", "bootanim");
    }

    private Handler shutdownMusicHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            String path = (String) msg.obj;
            mMediaPlayer = new MediaPlayer();
            try
            {
                mMediaPlayer.reset();
                mMediaPlayer.setDataSource(path);
                mMediaPlayer.prepare();
                mMediaPlayer.start();
                mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
                    @Override
                    public void onCompletion(MediaPlayer mp) {
                        synchronized (mActionDoneSync) {
                            isShutdownMusicPlaying = false;
                            mActionDoneSync.notifyAll();
                        }
                    }
                });
            } catch (IOException e) {
                Log.d(TAG, "play shutdown music error:" + e);
            }
        }
    };
}