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

Commit 25496c2e authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "BootAnimation: Play boot/shutdown animation and music"

parents 527dd24e b7309fef
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -17,7 +17,8 @@ LOCAL_SHARED_LIBRARIES := \
	libskia \
    libEGL \
    libGLESv1_CM \
    libgui
    libgui \
    libmedia

LOCAL_C_INCLUDES := \
	$(call include-path-for, corecg graphics)
+196 −7
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 The Android Open Source Project
 * Copyright (c) 2012-2013, 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.
@@ -22,6 +23,8 @@
#include <fcntl.h>
#include <utils/misc.h>
#include <signal.h>
#include <pthread.h>
#include <sys/select.h>

#include <cutils/properties.h>

@@ -50,21 +53,66 @@
#include <GLES/glext.h>
#include <EGL/eglext.h>

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

#include "BootAnimation.h"

#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip"
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"

#define USER_SHUTDOWN_ANIMATION_FILE "/data/local/shutdownanimation.zip"
#define SYSTEM_SHUTDOWN_ANIMATION_FILE "/system/media/shutdownanimation.zip"
#define SYSTEM_ENCRYPTED_SHUTDOWN_ANIMATION_FILE "/system/media/shutdownanimation-encrypted.zip"

#define USER_BOOT_MUSIC_FILE "/data/local/boot.wav"
#define SYSTEM_BOOT_MUSIC_FILE "/system/media/boot.wav"

#define USER_SHUTDOWN_MUSIC_FILE "/data/local/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,
                           const struct timespec *request,
                           struct timespec *remain);


namespace android {

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

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)
{
    mSession = new SurfaceComposerClient();
@@ -287,17 +335,19 @@ 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);

    if ((encryptedAnimation &&
            (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
            (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) ||
            (access(getAnimationFileName(IMG_ENC), R_OK) == 0) &&
            (mZip.open(getAnimationFileName(IMG_ENC)) == NO_ERROR)) ||

            ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) &&
            (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) ||
            ((access(getAnimationFileName(IMG_DATA), R_OK) == 0) &&
            (mZip.open(getAnimationFileName(IMG_DATA)) == NO_ERROR)) ||

            ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
            (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) {
            ((access(getAnimationFileName(IMG_SYS), R_OK) == 0) &&
            (mZip.open(getAnimationFileName(IMG_SYS)) == NO_ERROR))) {
        mAndroidAnimation = false;
    }

@@ -404,6 +454,7 @@ void BootAnimation::checkExit() {

bool BootAnimation::movie()
{
    char value[PROPERTY_VALUE_MAX];
    ZipFileRO& zip(mZip);

    size_t numEntries = zip.getNumEntries();
@@ -506,6 +557,14 @@ 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 (int i=0 ; i<pcount ; i++) {
        const Animation::Part& part(animation.parts[i]);
        const size_t fcount = part.frames.size();
@@ -583,9 +642,139 @@ 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] = { { USER_BOOTANIMATION_FILE,
            SYSTEM_BOOTANIMATION_FILE,
            SYSTEM_ENCRYPTED_BOOTANIMATION_FILE }, {
            USER_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] = { { USER_BOOT_MUSIC_FILE,
            SYSTEM_BOOT_MUSIC_FILE }, {
            USER_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);
        if (mp->setDataSource(fileName, NULL) == NO_ERROR) {
            mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE);
            mp->prepare();
        }

        //waiting for media player is prepared.
        pthread_mutex_lock(&mp_lock);
        while (!isMPlayerPrepared) {
            pthread_cond_wait(&mp_cond, &mp_lock);
        }
        pthread_mutex_unlock(&mp_lock);

        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.");
        }
    }
    return NULL;
}
// ---------------------------------------------------------------------------

}
+8 −1
Original line number Diff line number Diff line
@@ -89,11 +89,17 @@ private:
    bool android();
    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;
    AssetManager mAssets;
    Texture     mAndroid[2];
    Texture     mAndroid[3];
    int         mWidth;
    int         mHeight;
    EGLDisplay  mDisplay;
@@ -105,6 +111,7 @@ private:
    ZipFileRO   mZip;
};

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

}; // namespace android
+154 −13
Original line number Diff line number Diff line
@@ -33,7 +33,11 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -48,11 +52,22 @@ import android.telephony.MSimTelephonyManager;

import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.msim.ITelephonyMSim;
import com.android.server.power.PowerManagerService;

import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManager;
import java.lang.reflect.Method;

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";
@@ -89,9 +104,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 USER_BOOTANIMATION_FILE = "/data/local/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 MUSIC_SHUTDOWN_FILE = "/system/media/shutdown.wav";
    private static final String MUSIC_QRD_SHUTDOWN_FILE = "/data/qrd_theme/boot/shutdown.wav";

    private boolean isShutdownMusicPlaying = false;

    private static AlertDialog sConfirmDialog;

    private static AudioManager mAudioManager;
    
    private ShutdownThread() {
    }
 
@@ -192,6 +218,30 @@ public final class ShutdownThread extends Thread {
        shutdownInner(context, confirm);
    }

    private static String getShutdownMusicFilePath() {
        final String[] fileName = {MUSIC_QRD_SHUTDOWN_FILE,
                MUSIC_SHUTDOWN_FILE};
        File checkFile = null;
        int i = 0;
        for( ; i < fileName.length; i ++) {
            checkFile = new File(fileName[i]);
            if (checkFile.exists())
                break;
        }
        if (i >= fileName.length)
            return null;
        return fileName[i];
    }

    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.
@@ -215,6 +265,11 @@ 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);
@@ -225,6 +280,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);
@@ -333,6 +389,39 @@ public final class ShutdownThread extends Thread {
            }
        }

        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);

@@ -464,15 +553,13 @@ public final class ShutdownThread extends Thread {
                    }
                    if (!radioOff) {
                        try {
                            boolean subRadioOff = true;
                            if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
                                final ITelephonyMSim mphone = ITelephonyMSim.Stub.asInterface(
                                        ServiceManager.checkService("phone_msim"));
                                for (int i = 0; i < MSimTelephonyManager.getDefault().
                                        getPhoneCount(); i++) {
                                    subRadioOff = subRadioOff && !mphone.isRadioOn(i);
                                    radioOff = radioOff && !mphone.isRadioOn(i);
                                }
                                radioOff = subRadioOff;
                            } else {
                                radioOff = !phone.isRadioOn();
                            }
@@ -576,4 +663,58 @@ public final class ShutdownThread extends Thread {
        //Unknown exception
        }
     }

    private static boolean checkAnimationFileExist() {
        if (new File(USER_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() {
        boolean isSilent = mAudioManager.isSilentMode();
        SystemProperties.set("persist.sys.silent", isSilent ? "1" : "0");
        return isSilent;
    }

    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);
            }
        }
    };
}