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

Commit 98807e37 authored by Chiou-Hao Hsu's avatar Chiou-Hao Hsu Committed by Steve Kondik
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

bootanimtion: fix no boot sound due to error file descriptor

-wrong file descriptor passed to setDataSource(fd, offset, length)
 API and causes no sound
-use the default setDataSource(httpService,url,header) to parse
 the file of boot sound

CRs-Fixed: 731547

Change-Id: I437e7d797cd13b7f8f8e0fbf81d5de99c55c27d1

bootanimation: Only try to shutdown the mediaplayer if it was prepared

The bootanimation tries to shutdown the mediaplayer gracefully by waiting
on an asynchronous shutdown event for 5 seconds.

If, however, there is no boot sound, that asynchronous shutdown event will
never happen and the animation will be stopped for 5 seconds before shutting
down.

Visibly, this fixes the issue where the bootanimation would simply stop near
the end for exactly 5 seconds.

Change-Id: I77f5631368c7d9b9fef7941a6278af9c36032044

bootanimation: Fix compilation warnings

Change-Id: Iaf7e66811f3cecf8b5b1fa690941489a7a07f7fd

bootanimation: Move the bootanimation playaudio code

Move the bootanimation playaudio code, so that the audio plays
after the boot animation, because the audio manager is not ready and races
with the boot animation, if they the audio and boot animation play
together and makes the boot animation stutter.

SAMOSA-10

Change-Id: I56ab19ec06a4af2de84916cdc330e646dd5a8695
(cherry picked from commit 9ccd0254)

bootanimation: Use CLOCK_MONTONIC for pthread

Use CLOCK_REALTIME can mess up the pthread timed
wait if system clock changes.

Change-Id: I7e14958460ccd1ecd8c9aa3cdfa4e60dd2a083ec

[mikeioannina]: Adjust for cm-14.0

Change-Id: I437e7d797cd13b7f8f8e0fbf81d5de99c55c27d1
parent 69268863
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -24,7 +24,8 @@ LOCAL_SHARED_LIBRARIES := \
    libGLESv1_CM \
    libgui \
    libtinyalsa \
    libregionalization
    libregionalization \
    libmedia

LOCAL_MODULE:= bootanimation

+197 −7
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.
@@ -24,6 +25,8 @@
#include <utils/misc.h>
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include <sys/select.h>

#include <cutils/properties.h>

@@ -54,6 +57,10 @@
#include <GLES/glext.h>
#include <EGL/eglext.h>

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

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

@@ -63,6 +70,16 @@
#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"

namespace android {
@@ -71,6 +88,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), mClockEnabled(true) {
    mSession = new SurfaceComposerClient();
}
@@ -233,27 +280,136 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame)
// Return Value : File path
const char *BootAnimation::getAnimationFileName(ImageID image)
{
    const char *fileName[3] = { OEM_BOOTANIMATION_FILE,
    const char *fileName[2][3] = { { OEM_BOOTANIMATION_FILE,
            SYSTEM_BOOTANIMATION_FILE,
            SYSTEM_ENCRYPTED_BOOTANIMATION_FILE };
            SYSTEM_ENCRYPTED_BOOTANIMATION_FILE }, {
            OEM_SHUTDOWN_ANIMATION_FILE,
            SYSTEM_SHUTDOWN_ANIMATION_FILE,
            SYSTEM_ENCRYPTED_SHUTDOWN_ANIMATION_FILE } };
    int state;

    // Load animations of Carrier through regionalization environment
    if (Environment::isSupported()) {
        Environment* environment = new Environment();
        const char* animFile = environment->getMediaFile(
                Environment::ANIMATION_TYPE, Environment::BOOT_STATUS);
        ALOGE("Get Carrier Animation type: %d,status:%d", Environment::ANIMATION_TYPE,Environment::BOOT_STATUS);
        ALOGE("Get Carrier Animation type: %d, status:%d",
                Environment::ANIMATION_TYPE, Environment::BOOT_STATUS);
        if (animFile != NULL && strcmp(animFile, "") != 0) {
           return animFile;
        } else {
           ALOGD("Get Carrier Animation file: %s failed", animFile);
        }
        delete environment;
    }

    state = checkBootState() ? 0 : 1;

    return fileName[state][image];
}

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

    const 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];
}

static 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(NULL, fileName, NULL) == NO_ERROR) {
            mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE);
            mp->prepare();
        } else {
           ALOGE("Get Carrier Animation file,since it's not support carrier");
            ALOGE("failed to setDataSource for %s", fileName);
            return NULL;
        }

    return fileName[image];
        // 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;
}

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

    const char *fileName;
    if (((fileName = getBootRingtoneFileName(IMG_OEM)) != 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;
}

status_t BootAnimation::readyToRun() {
@@ -712,10 +868,17 @@ bool BootAnimation::playAnimation(const Animation& animation)
    const int xc = (mWidth - animation.width) / 2;
    const int yc = ((mHeight - animation.height) / 2);
    nsecs_t frameDuration = s2ns(1) / animation.fps;
    char value[PROPERTY_VALUE_MAX];

    Region clearReg(Rect(mWidth, mHeight));
    clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));

    pthread_mutex_init(&mp_lock, NULL);
    pthread_condattr_t attr;
    pthread_condattr_init(&attr);
    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
    pthread_cond_init(&mp_cond, &attr);

    for (size_t i=0 ; i<pcount ; i++) {
        const Animation::Part& part(animation.parts[i]);
        const size_t fcount = part.frames.size();
@@ -816,6 +979,33 @@ bool BootAnimation::playAnimation(const Animation& animation)
            }
        }
    }

    property_get("persist.sys.silent", value, "null");
    if (strncmp(value, "1", 1) != 0) {
       ALOGD("playing boot audio here");
       playBackgroundMusic();
    }

    if (isMPlayerPrepared) {
        ALOGD("waiting for media player to complete.");
        struct timespec timeout;
        clock_gettime(CLOCK_MONOTONIC, &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 true;
}

+4 −1
Original line number Diff line number Diff line
@@ -94,6 +94,9 @@ private:
     */
    enum ImageID { IMG_OEM = 0, IMG_SYS = 1, IMG_ENC = 2 };
    const char *getAnimationFileName(ImageID image);
    const char *getBootRingtoneFileName(ImageID image);
    void playBackgroundMusic();
    bool checkBootState();
    status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
    status_t initTexture(const Animation::Frame& frame);
    bool android();
@@ -110,7 +113,7 @@ private:
    sp<SurfaceComposerClient>       mSession;
    sp<AudioPlayer>                 mAudioPlayer;
    AssetManager mAssets;
    Texture     mAndroid[2];
    Texture     mAndroid[3];
    Texture     mClock;
    int         mWidth;
    int         mHeight;
+143 −3
Original line number Diff line number Diff line
@@ -35,8 +35,12 @@ 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.FileUtils;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.RemoteException;
@@ -56,16 +60,22 @@ import android.widget.ListView;

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 cyanogenmod.providers.CMSettings;

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

public final class ShutdownThread extends Thread {
    // constants
@@ -127,6 +137,17 @@ public final class ShutdownThread extends Thread {
    private PowerManager.WakeLock mScreenWakeLock;
    private Handler mHandler;

    private static AudioManager mAudioManager;
    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 OEM_SHUTDOWN_MUSIC_FILE = "/oem/media/shutdown.wav";
    private static final String SHUTDOWN_MUSIC_FILE = "/system/media/shutdown.wav";

    private boolean isShutdownMusicPlaying = false;

    private static AlertDialog sConfirmDialog;
    private ProgressDialog mProgressDialog;

@@ -311,6 +332,28 @@ 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.
@@ -407,10 +450,20 @@ public final class ShutdownThread extends Thread {

            pd.setIndeterminate(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.
            pd.setCancelable(false);
            pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

            pd.show();
        }

        sInstance.mProgressDialog = pd;
        sInstance.mContext = context;
@@ -541,6 +594,41 @@ public final class ShutdownThread extends Thread {
            sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
        }

        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) {
                    // Do nothing
                }
            }
            if (!isShutdownMusicPlaying) {
                Log.i(TAG, "play shutdown music complete.");
            }
        }

        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);
        if (mRebootHasProgressBar) {
@@ -835,4 +923,56 @@ public final class ShutdownThread extends Thread {
            Log.w(TAG, "Timed out waiting for uncrypt.");
        }
    }

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