diff options
-rw-r--r-- | cmds/bootanimation/Android.mk | 3 | ||||
-rw-r--r-- | cmds/bootanimation/BootAnimation.cpp | 283 | ||||
-rw-r--r-- | cmds/bootanimation/BootAnimation.h | 9 | ||||
-rw-r--r-- | services/core/java/com/android/server/power/ShutdownThread.java | 150 |
4 files changed, 430 insertions, 15 deletions
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index cc3b6f8..a2d5675 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -23,7 +23,8 @@ LOCAL_SHARED_LIBRARIES := \ libEGL \ libGLESv1_CM \ libgui \ - libtinyalsa + libtinyalsa \ + libmedia ifeq ($(TARGET_CONTINUOUS_SPLASH_ENABLED),true) LOCAL_CFLAGS += -DCONTINUOUS_SPLASH diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 5b9ecd0..f7a75d3 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -1,5 +1,6 @@ /* * 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" @@ -62,6 +69,16 @@ #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip" #define THEME_BOOTANIMATION_FILE "/data/system/theme/bootanimation.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 { @@ -70,6 +87,87 @@ 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; + } + } +}; + +static long getFreeMemory(void) +{ + int fd = open("/proc/meminfo", O_RDONLY); + const char* const sums[] = { "MemFree:", "Cached:", NULL }; + const int sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 }; + int num = 2; + + if (fd < 0) { + ALOGW("Unable to open /proc/meminfo"); + return -1; + } + + char buffer[256]; + const int len = read(fd, buffer, sizeof(buffer)-1); + close(fd); + + if (len < 0) { + ALOGW("Unable to read /proc/meminfo"); + return -1; + } + buffer[len] = 0; + + size_t numFound = 0; + long mem = 0; + + char* p = buffer; + while (*p && numFound < num) { + int i = 0; + while (sums[i]) { + if (strncmp(p, sums[i], sumsLen[i]) == 0) { + p += sumsLen[i]; + while (*p == ' ') p++; + char* num = p; + while (*p >= '0' && *p <= '9') p++; + if (*p != 0) { + *p = 0; + p++; + if (*p == 0) p--; + } + mem += atoll(num); + numFound++; + break; + } + i++; + } + p++; + } + + return numFound > 0 ? mem : -1; +} + BootAnimation::BootAnimation() : Thread(false), mZip(NULL) { mSession = new SurfaceComposerClient(); @@ -289,12 +387,14 @@ 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(THEME_BOOTANIMATION_FILE, R_OK) == 0) && ((zipFile = ZipFileRO::open(THEME_BOOTANIMATION_FILE)) != NULL)) || @@ -303,8 +403,13 @@ status_t BootAnimation::readyToRun() { ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) || ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && - ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) { + ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL)) || + ((access(getAnimationFileName(IMG_DATA), R_OK) == 0) && + ((zipFile = ZipFileRO::open(getAnimationFileName(IMG_DATA))) != NULL)) || + + ((access(getAnimationFileName(IMG_SYS), R_OK) == 0) && + ((zipFile = ZipFileRO::open(getAnimationFileName(IMG_SYS))) != NULL))) { mZip = zipFile; } @@ -458,6 +563,7 @@ bool BootAnimation::readFile(const char* name, String8& outString) bool BootAnimation::movie() { + char value[PROPERTY_VALUE_MAX]; String8 desString; if (!readFile("desc.txt", desString)) { @@ -589,11 +695,37 @@ 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(); glBindTexture(GL_TEXTURE_2D, 0); + /*calculate if we need to runtime save memory + * condition: runtime free memory is less than the textures that will used. + * needSaveMem default to be false + */ + GLint mMaxTextureSize; + bool needSaveMem = false; + GLuint mTextureid; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + //ALOGD("freemem:%ld, %d", getFreeMemory(), mMaxTextureSize); + if(getFreeMemory() < mMaxTextureSize * mMaxTextureSize * fcount / 1024) { + ALOGD("Use save memory method, maybe small fps in actual."); + needSaveMem = true; + glGenTextures(1, &mTextureid); + glBindTexture(GL_TEXTURE_2D, mTextureid); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + } + for (int r=0 ; !part.count || r<part.count ; r++) { // Exit any non playuntil complete parts immediately if(exitPending() && !part.playUntilComplete) @@ -614,10 +746,10 @@ bool BootAnimation::movie() const Animation::Frame& frame(part.frames[j]); nsecs_t lastFrame = systemTime(); - if (r > 0) { + if (r > 0 && !needSaveMem) { glBindTexture(GL_TEXTURE_2D, frame.tid); } else { - if (part.count != 1) { + if (!needSaveMem && part.count != 1) { glGenTextures(1, &frame.tid); glBindTexture(GL_TEXTURE_2D, frame.tid); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -670,17 +802,156 @@ bool BootAnimation::movie() } // free the textures for this part - if (part.count != 1) { + if (!needSaveMem && part.count != 1) { for (size_t j=0 ; j<fcount ; j++) { const Animation::Frame& frame(part.frames[j]); glDeleteTextures(1, &frame.tid); } } + + if (needSaveMem) { + glDeleteTextures(1, &mTextureid); + } + + } + + 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); + + if (mp->setDataSource(NULL, fileName, NULL) == NO_ERROR) { + mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE); + mp->prepare(); + } else { + ALOGE("failed to setDataSource for %s", fileName); + return NULL; + } + + //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; +} // --------------------------------------------------------------------------- } diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index f968b25..758f7f7 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -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 diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index 596828e..aa403eb 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -34,7 +34,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; @@ -53,8 +57,9 @@ 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 java.lang.reflect.Method; import dalvik.system.PathClassLoader; @@ -62,7 +67,11 @@ import dalvik.system.PathClassLoader; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintWriter; +import java.io.OutputStreamWriter; public final class ShutdownThread extends Thread { // constants @@ -119,10 +128,21 @@ 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 ProgressDialog mProgressDialog; + private static AudioManager mAudioManager; + private ShutdownThread() { } @@ -302,6 +322,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. @@ -377,10 +418,20 @@ public final class ShutdownThread extends Thread { pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); pd.setIndeterminate(true); } - pd.setCancelable(false); - pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - pd.show(); + //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; @@ -511,6 +562,40 @@ 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) { + } + } + if (!isShutdownMusicPlaying) { + Log.i(TAG, "play shutdown music complete."); + } + } + // Shutdown radios. shutdownRadios(MAX_RADIO_WAIT_TIME); if (mRebootUpdate) { @@ -601,11 +686,10 @@ public final class ShutdownThread extends Thread { ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); final IBluetoothManager bluetooth = IBluetoothManager.Stub.asInterface(ServiceManager.checkService( - BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE)); + 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 @@ -838,4 +922,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); + } + } + }; } |