summaryrefslogtreecommitdiff
path: root/cmds/bootanimation
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/bootanimation')
-rw-r--r--cmds/bootanimation/Android.bp3
-rw-r--r--cmds/bootanimation/BootAnimation.cpp59
-rw-r--r--cmds/bootanimation/BootAnimation.h2
-rw-r--r--cmds/bootanimation/BootAnimationUtil.cpp43
-rw-r--r--cmds/bootanimation/BootAnimationUtil.h7
-rw-r--r--cmds/bootanimation/audioplay.cpp81
-rw-r--r--cmds/bootanimation/audioplay.h5
-rw-r--r--cmds/bootanimation/bootanimation_main.cpp111
-rw-r--r--cmds/bootanimation/iot/Android.bp49
-rw-r--r--cmds/bootanimation/iot/BootAction.cpp33
-rw-r--r--cmds/bootanimation/iot/BootAction.h9
-rw-r--r--cmds/bootanimation/iot/BootParameters.cpp179
-rw-r--r--cmds/bootanimation/iot/BootParameters.h49
-rw-r--r--cmds/bootanimation/iot/BootParameters_test.cpp263
-rw-r--r--cmds/bootanimation/iot/iotbootanimation_main.cpp14
15 files changed, 678 insertions, 229 deletions
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 60a1cfbe9dd7..31bd612cc2c7 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -44,8 +44,11 @@ cc_binary {
product_is_iot: {
shared_libs: [
"libandroidthings",
+ "libandroidthings_protos",
"libchrome",
+ "libprotobuf-cpp-lite",
],
+ static_libs: ["libjsoncpp"],
srcs: [
"iot/iotbootanimation_main.cpp",
"iot/BootAction.cpp",
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index ed6c25dc49c3..46917e4f6062 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -114,7 +114,7 @@ BootAnimation::BootAnimation(sp<Callbacks> callbacks)
void BootAnimation::onFirstRef() {
status_t err = mSession->linkToComposerDeath(this);
- ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
+ SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
if (err == NO_ERROR) {
run("BootAnimation", PRIORITY_DISPLAY);
}
@@ -128,7 +128,7 @@ sp<SurfaceComposerClient> BootAnimation::session() const {
void BootAnimation::binderDied(const wp<IBinder>&)
{
// woah, surfaceflinger died!
- ALOGD("SurfaceFlinger died, exiting...");
+ SLOGD("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.
@@ -252,10 +252,12 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height)
status_t BootAnimation::readyToRun() {
mAssets.addDefaultAssets();
- sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
+ mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
+ if (mDisplayToken == nullptr)
+ return -1;
+
DisplayInfo dinfo;
- status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
+ status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo);
if (status)
return -1;
@@ -360,7 +362,7 @@ bool BootAnimation::threadLoop()
bool BootAnimation::android()
{
- ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+ SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
elapsedRealtime());
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
@@ -508,14 +510,14 @@ static bool parseColor(const char str[7], float color[3]) {
static bool readFile(ZipFileRO* zip, const char* name, String8& outString)
{
ZipEntryRO entry = zip->findEntryByName(name);
- ALOGE_IF(!entry, "couldn't find %s", name);
+ SLOGE_IF(!entry, "couldn't find %s", name);
if (!entry) {
return false;
}
FileMap* entryMap = zip->createEntryFileMap(entry);
zip->releaseEntry(entry);
- ALOGE_IF(!entryMap, "entryMap is null");
+ SLOGE_IF(!entryMap, "entryMap is null");
if (!entryMap) {
return false;
}
@@ -616,7 +618,7 @@ void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos)
size_t length = strftime(timeBuff, TIME_LENGTH, timeFormat, timeInfo);
if (length != TIME_LENGTH - 1) {
- ALOGE("Couldn't format time; abandoning boot animation clock");
+ SLOGE("Couldn't format time; abandoning boot animation clock");
mClockEnabled = false;
return;
}
@@ -654,13 +656,13 @@ bool BootAnimation::parseAnimationDesc(Animation& animation)
char pathType;
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
- // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
+ // SLOGD("> w=%d, h=%d, fps=%d", width, height, fps);
animation.width = width;
animation.height = height;
animation.fps = fps;
} else if (sscanf(l, " %c %d %d %s #%6s %16s %16s",
&pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) {
- //ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
+ //SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
// pathType, count, pause, path, color, clockPos1, clockPos2);
Animation::Part part;
part.playUntilComplete = pathType == 'c';
@@ -670,7 +672,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation)
part.audioData = NULL;
part.animation = NULL;
if (!parseColor(color, part.backgroundColor)) {
- ALOGE("> invalid color '#%s'", color);
+ SLOGE("> invalid color '#%s'", color);
part.backgroundColor[0] = 0.0f;
part.backgroundColor[1] = 0.0f;
part.backgroundColor[2] = 0.0f;
@@ -679,7 +681,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation)
animation.parts.add(part);
}
else if (strcmp(l, "$SYSTEM") == 0) {
- // ALOGD("> SYSTEM");
+ // SLOGD("> SYSTEM");
Animation::Part part;
part.playUntilComplete = false;
part.count = 1;
@@ -710,7 +712,7 @@ bool BootAnimation::preloadZip(Animation& animation)
while ((entry = zip->nextEntry(cookie)) != NULL) {
const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
- ALOGE("Error fetching entry file name");
+ SLOGE("Error fetching entry file name");
continue;
}
@@ -754,7 +756,7 @@ bool BootAnimation::preloadZip(Animation& animation)
}
}
} else {
- ALOGE("bootanimation.zip is compressed; must be only stored");
+ SLOGE("bootanimation.zip is compressed; must be only stored");
}
}
}
@@ -782,7 +784,7 @@ bool BootAnimation::preloadZip(Animation& animation)
frame.trimX = x;
frame.trimY = y;
} else {
- ALOGE("Error parsing trim.txt, line: %s", lineStr);
+ SLOGE("Error parsing trim.txt, line: %s", lineStr);
break;
}
}
@@ -860,12 +862,12 @@ bool BootAnimation::movie()
mTimeCheckThread = nullptr;
}
- releaseAnimation(animation);
-
if (clockFontInitialized) {
glDeleteTextures(1, &animation->clockFont.texture.name);
}
+ releaseAnimation(animation);
+
return false;
}
@@ -876,7 +878,7 @@ bool BootAnimation::playAnimation(const Animation& animation)
const int animationX = (mWidth - animation.width) / 2;
const int animationY = (mHeight - animation.height) / 2;
- ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+ SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
elapsedRealtime());
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
@@ -949,7 +951,7 @@ bool BootAnimation::playAnimation(const Animation& animation)
nsecs_t now = systemTime();
nsecs_t delay = frameDuration - (now - lastFrame);
- //ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
+ //SLOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
lastFrame = now;
if (delay > 0) {
@@ -1014,16 +1016,13 @@ void BootAnimation::handleViewport(nsecs_t timestep) {
// At the end of the animation, we switch to the viewport that DisplayManager will apply
// later. This changes the coordinate system, and means we must move the surface up by
// the inset amount.
- sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
-
Rect layerStackRect(0, 0, mWidth, mHeight - mTargetInset);
Rect displayRect(0, mTargetInset, mWidth, mHeight);
SurfaceComposerClient::Transaction t;
t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
.setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
- t.setDisplayProjection(dtoken, 0 /* orientation */, layerStackRect, displayRect);
+ t.setDisplayProjection(mDisplayToken, 0 /* orientation */, layerStackRect, displayRect);
t.apply();
mTargetInset = mCurrentInset = 0;
@@ -1048,13 +1047,13 @@ void BootAnimation::releaseAnimation(Animation* animation) const
BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
{
if (mLoadedFiles.indexOf(fn) >= 0) {
- ALOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
+ SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
fn.string());
return NULL;
}
ZipFileRO *zip = ZipFileRO::open(fn);
if (zip == NULL) {
- ALOGE("Failed to open animation zip \"%s\": %s",
+ SLOGE("Failed to open animation zip \"%s\": %s",
fn.string(), strerror(errno));
return NULL;
}
@@ -1143,7 +1142,7 @@ bool BootAnimation::TimeCheckThread::doThreadLoop() {
if (pollResult == 0) {
return true;
} else if (pollResult < 0) {
- ALOGE("Could not poll inotify events");
+ SLOGE("Could not poll inotify events");
return false;
}
@@ -1152,7 +1151,7 @@ bool BootAnimation::TimeCheckThread::doThreadLoop() {
if (length == 0) {
return true;
} else if (length < 0) {
- ALOGE("Could not read inotify events");
+ SLOGE("Could not read inotify events");
return false;
}
@@ -1183,7 +1182,7 @@ void BootAnimation::TimeCheckThread::addTimeDirWatch() {
status_t BootAnimation::TimeCheckThread::readyToRun() {
mInotifyFd = inotify_init();
if (mInotifyFd < 0) {
- ALOGE("Could not initialize inotify fd");
+ SLOGE("Could not initialize inotify fd");
return NO_INIT;
}
@@ -1191,7 +1190,7 @@ status_t BootAnimation::TimeCheckThread::readyToRun() {
if (mSystemWd < 0) {
close(mInotifyFd);
mInotifyFd = -1;
- ALOGE("Could not add watch for %s", SYSTEM_DATA_DIR_PATH);
+ SLOGE("Could not add watch for %s", SYSTEM_DATA_DIR_PATH);
return NO_INIT;
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 4fd5c0ef5f28..19616cb790c7 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -22,6 +22,7 @@
#include <androidfw/AssetManager.h>
#include <utils/Thread.h>
+#include <binder/IBinder.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
@@ -170,6 +171,7 @@ private:
EGLDisplay mDisplay;
EGLDisplay mContext;
EGLDisplay mSurface;
+ sp<IBinder> mDisplayToken;
sp<SurfaceControl> mFlingerSurfaceControl;
sp<Surface> mFlingerSurface;
bool mClockEnabled;
diff --git a/cmds/bootanimation/BootAnimationUtil.cpp b/cmds/bootanimation/BootAnimationUtil.cpp
index 7718daf61d81..1e417e938359 100644
--- a/cmds/bootanimation/BootAnimationUtil.cpp
+++ b/cmds/bootanimation/BootAnimationUtil.cpp
@@ -16,14 +16,30 @@
#include "BootAnimationUtil.h"
+#include <vector>
#include <inttypes.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
+#include <android-base/properties.h>
namespace android {
+namespace {
+
+static constexpr char PLAY_SOUND_PROP_NAME[] = "persist.sys.bootanim.play_sound";
+static constexpr char BOOT_COMPLETED_PROP_NAME[] = "sys.boot_completed";
+static constexpr char POWER_CTL_PROP_NAME[] = "sys.powerctl";
+static constexpr char BOOTREASON_PROP_NAME[] = "ro.boot.bootreason";
+static const std::vector<std::string> PLAY_SOUND_BOOTREASON_BLACKLIST {
+ "kernel_panic",
+ "Panic",
+ "Watchdog",
+};
+
+} // namespace
+
bool bootAnimationDisabled() {
char value[PROPERTY_VALUE_MAX];
@@ -58,4 +74,31 @@ void waitForSurfaceFlinger() {
}
}
+bool playSoundsAllowed() {
+ // Only play sounds for system boots, not runtime restarts.
+ if (android::base::GetBoolProperty(BOOT_COMPLETED_PROP_NAME, false)) {
+ return false;
+ }
+ // no audio while shutting down
+ if (!android::base::GetProperty(POWER_CTL_PROP_NAME, "").empty()) {
+ return false;
+ }
+ // Read the system property to see if we should play the sound.
+ // If it's not present, default to allowed.
+ if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) {
+ return false;
+ }
+
+ // Don't play sounds if this is a reboot due to an error.
+ char bootreason[PROPERTY_VALUE_MAX];
+ if (property_get(BOOTREASON_PROP_NAME, bootreason, nullptr) > 0) {
+ for (const auto& str : PLAY_SOUND_BOOTREASON_BLACKLIST) {
+ if (strcasecmp(str.c_str(), bootreason) == 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
} // namespace android
diff --git a/cmds/bootanimation/BootAnimationUtil.h b/cmds/bootanimation/BootAnimationUtil.h
index 60987cd1ccd1..1e1140a51763 100644
--- a/cmds/bootanimation/BootAnimationUtil.h
+++ b/cmds/bootanimation/BootAnimationUtil.h
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#ifndef ANDROID_BOOTANIMATION_UTIL_H
+#define ANDROID_BOOTANIMATION_UTIL_H
+
namespace android {
// Returns true if boot animation is disabled.
@@ -22,4 +25,8 @@ bool bootAnimationDisabled();
// Waits until the surface flinger is up.
void waitForSurfaceFlinger();
+// Returns whether sounds should be played during current boot.
+bool playSoundsAllowed();
} // namespace android
+
+#endif // ANDROID_BOOTANIMATION_UTIL_H
diff --git a/cmds/bootanimation/audioplay.cpp b/cmds/bootanimation/audioplay.cpp
index c546072e733a..874aab08862e 100644
--- a/cmds/bootanimation/audioplay.cpp
+++ b/cmds/bootanimation/audioplay.cpp
@@ -17,22 +17,27 @@
// cribbed from samples/native-audio
-#include "audioplay.h"
-
#define CHATTY ALOGD
#define LOG_TAG "audioplay"
+#include "audioplay.h"
+
#include <string.h>
#include <utils/Log.h>
+#include <utils/threads.h>
// for native audio
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
+#include "BootAnimationUtil.h"
+
namespace audioplay {
namespace {
+using namespace android;
+
// engine interfaces
static SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine;
@@ -305,6 +310,74 @@ bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** o
return true;
}
+class InitAudioThread : public Thread {
+public:
+ InitAudioThread(uint8_t* exampleAudioData, int exampleAudioLength)
+ : Thread(false),
+ mExampleAudioData(exampleAudioData),
+ mExampleAudioLength(exampleAudioLength) {}
+private:
+ virtual bool threadLoop() {
+ audioplay::create(mExampleAudioData, mExampleAudioLength);
+ // Exit immediately
+ return false;
+ }
+
+ uint8_t* mExampleAudioData;
+ int mExampleAudioLength;
+};
+
+// Typedef to aid readability.
+typedef android::BootAnimation::Animation Animation;
+
+class AudioAnimationCallbacks : public android::BootAnimation::Callbacks {
+public:
+ void init(const Vector<Animation::Part>& parts) override {
+ const Animation::Part* partWithAudio = nullptr;
+ for (const Animation::Part& part : parts) {
+ if (part.audioData != nullptr) {
+ partWithAudio = &part;
+ break;
+ }
+ }
+
+ if (partWithAudio == nullptr) {
+ return;
+ }
+
+ ALOGD("found audio.wav, creating playback engine");
+ // The audioData is used to initialize the audio system. Different data
+ // can be played later for other parts BUT the assumption is that they
+ // will all be the same format and only the format of this audioData
+ // will work correctly.
+ initAudioThread = new InitAudioThread(partWithAudio->audioData,
+ partWithAudio->audioLength);
+ initAudioThread->run("BootAnimation::InitAudioThread", PRIORITY_NORMAL);
+ };
+
+ void playPart(int partNumber, const Animation::Part& part, int playNumber) override {
+ // only play audio file the first time we animate the part
+ if (playNumber == 0 && part.audioData && playSoundsAllowed()) {
+ ALOGD("playing clip for part%d, size=%d",
+ partNumber, part.audioLength);
+ // Block until the audio engine is finished initializing.
+ if (initAudioThread != nullptr) {
+ initAudioThread->join();
+ }
+ audioplay::playClip(part.audioData, part.audioLength);
+ }
+ };
+
+ void shutdown() override {
+ // we've finally played everything we're going to play
+ audioplay::setPlaying(false);
+ audioplay::destroy();
+ };
+
+private:
+ sp<InitAudioThread> initAudioThread = nullptr;
+};
+
} // namespace
bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
@@ -397,4 +470,8 @@ void destroy() {
}
}
+sp<BootAnimation::Callbacks> createAnimationCallbacks() {
+ return new AudioAnimationCallbacks();
+}
+
} // namespace audioplay
diff --git a/cmds/bootanimation/audioplay.h b/cmds/bootanimation/audioplay.h
index 0e5705af0ad0..4704a702d50b 100644
--- a/cmds/bootanimation/audioplay.h
+++ b/cmds/bootanimation/audioplay.h
@@ -20,6 +20,8 @@
#include <string.h>
+#include "BootAnimation.h"
+
namespace audioplay {
// Initializes the engine with an example of the type of WAV clip to play.
@@ -32,6 +34,9 @@ bool playClip(const uint8_t* buf, int size);
void setPlaying(bool isPlaying);
void destroy();
+// Generates callbacks to integrate the audioplay system with the BootAnimation.
+android::sp<android::BootAnimation::Callbacks> createAnimationCallbacks();
+
}
#endif // AUDIOPLAY_H_
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index 8501982d071c..a52a5e92a840 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -26,8 +26,6 @@
#include <sys/resource.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
-#include <utils/threads.h>
-#include <android-base/properties.h>
#include "BootAnimation.h"
#include "BootAnimationUtil.h"
@@ -35,113 +33,6 @@
using namespace android;
-// ---------------------------------------------------------------------------
-
-namespace {
-
-// Create a typedef for readability.
-typedef android::BootAnimation::Animation Animation;
-
-static const char PLAY_SOUND_PROP_NAME[] = "persist.sys.bootanim.play_sound";
-static const char BOOT_COMPLETED_PROP_NAME[] = "sys.boot_completed";
-static const char POWER_CTL_PROP_NAME[] = "sys.powerctl";
-static const char BOOTREASON_PROP_NAME[] = "ro.boot.bootreason";
-static const std::vector<std::string> PLAY_SOUND_BOOTREASON_BLACKLIST {
- "kernel_panic",
- "Panic",
- "Watchdog",
-};
-
-class InitAudioThread : public Thread {
-public:
- InitAudioThread(uint8_t* exampleAudioData, int exampleAudioLength)
- : Thread(false),
- mExampleAudioData(exampleAudioData),
- mExampleAudioLength(exampleAudioLength) {}
-private:
- virtual bool threadLoop() {
- audioplay::create(mExampleAudioData, mExampleAudioLength);
- // Exit immediately
- return false;
- }
-
- uint8_t* mExampleAudioData;
- int mExampleAudioLength;
-};
-
-bool playSoundsAllowed() {
- // Only play sounds for system boots, not runtime restarts.
- if (android::base::GetBoolProperty(BOOT_COMPLETED_PROP_NAME, false)) {
- return false;
- }
- // no audio while shutting down
- if (!android::base::GetProperty(POWER_CTL_PROP_NAME, "").empty()) {
- return false;
- }
- // Read the system property to see if we should play the sound.
- // If it's not present, default to allowed.
- if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) {
- return false;
- }
-
- // Don't play sounds if this is a reboot due to an error.
- char bootreason[PROPERTY_VALUE_MAX];
- if (property_get(BOOTREASON_PROP_NAME, bootreason, nullptr) > 0) {
- for (const auto& str : PLAY_SOUND_BOOTREASON_BLACKLIST) {
- if (strcasecmp(str.c_str(), bootreason) == 0) {
- return false;
- }
- }
- }
- return true;
-}
-
-class AudioAnimationCallbacks : public android::BootAnimation::Callbacks {
-public:
- void init(const Vector<Animation::Part>& parts) override {
- const Animation::Part* partWithAudio = nullptr;
- for (const Animation::Part& part : parts) {
- if (part.audioData != nullptr) {
- partWithAudio = &part;
- }
- }
-
- if (partWithAudio == nullptr) {
- return;
- }
-
- ALOGD("found audio.wav, creating playback engine");
- initAudioThread = new InitAudioThread(partWithAudio->audioData,
- partWithAudio->audioLength);
- initAudioThread->run("BootAnimation::InitAudioThread", PRIORITY_NORMAL);
- };
-
- void playPart(int partNumber, const Animation::Part& part, int playNumber) override {
- // only play audio file the first time we animate the part
- if (playNumber == 0 && part.audioData && playSoundsAllowed()) {
- ALOGD("playing clip for part%d, size=%d",
- partNumber, part.audioLength);
- // Block until the audio engine is finished initializing.
- if (initAudioThread != nullptr) {
- initAudioThread->join();
- }
- audioplay::playClip(part.audioData, part.audioLength);
- }
- };
-
- void shutdown() override {
- // we've finally played everything we're going to play
- audioplay::setPlaying(false);
- audioplay::destroy();
- };
-
-private:
- sp<InitAudioThread> initAudioThread = nullptr;
-};
-
-} // namespace
-
-
int main()
{
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
@@ -156,7 +47,7 @@ int main()
waitForSurfaceFlinger();
// create the boot animation object
- sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
+ sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
ALOGV("Boot animation set up. Joining pool.");
IPCThreadState::self()->joinThreadPool();
diff --git a/cmds/bootanimation/iot/Android.bp b/cmds/bootanimation/iot/Android.bp
new file mode 100644
index 000000000000..1f248adcb9e1
--- /dev/null
+++ b/cmds/bootanimation/iot/Android.bp
@@ -0,0 +1,49 @@
+// Copyright (C) 2018 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.
+//
+
+// libbootanimation_iot_test
+// ===========================================================
+cc_test {
+ name: "libbootanimation_iot_test",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ shared_libs: [
+ "libandroidthings",
+ "libandroidthings_protos",
+ "libbase",
+ "libchrome",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ ],
+
+ static_libs: ["libjsoncpp"],
+
+ srcs: [
+ "BootParameters.cpp",
+ "BootParameters_test.cpp",
+ ],
+
+ enabled: false,
+ product_variables: {
+ product_is_iot: {
+ enabled: true,
+ },
+ },
+}
diff --git a/cmds/bootanimation/iot/BootAction.cpp b/cmds/bootanimation/iot/BootAction.cpp
index fa797444d569..8b55147110bc 100644
--- a/cmds/bootanimation/iot/BootAction.cpp
+++ b/cmds/bootanimation/iot/BootAction.cpp
@@ -32,7 +32,7 @@ BootAction::~BootAction() {
}
bool BootAction::init(const std::string& libraryPath,
- const std::vector<ABootActionParameter>& parameters) {
+ const std::unique_ptr<BootParameters>& bootParameters) {
APeripheralManagerClient* client = nullptr;
ALOGD("Connecting to peripheralmanager");
// Wait for peripheral manager to come up.
@@ -77,9 +77,32 @@ bool BootAction::init(const std::string& libraryPath,
mLibStartPart = reinterpret_cast<libStartPart>(loaded);
}
- ALOGD("Entering boot_action_init");
- bool result = mLibInit(parameters.data(), parameters.size());
- ALOGD("Returned from boot_action_init");
+ // SilentBoot is considered optional, if it isn't exported by the library
+ // and the boot is silent, no method is called.
+ loaded = nullptr;
+ if (!loadSymbol("boot_action_silent_boot", &loaded) || loaded == nullptr) {
+ ALOGW("No boot_action_silent_boot found, boot action will not be "
+ "executed during a silent boot.");
+ } else {
+ mLibSilentBoot = reinterpret_cast<libInit>(loaded);
+ }
+
+ bool result = true;
+ const auto& parameters = bootParameters->getParameters();
+ if (bootParameters->isSilentBoot()) {
+ if (mLibSilentBoot != nullptr) {
+ ALOGD("Entering boot_action_silent_boot");
+ result = mLibSilentBoot(parameters.data(), parameters.size());
+ ALOGD("Returned from boot_action_silent_boot");
+ } else {
+ ALOGW("Skipping missing boot_action_silent_boot");
+ }
+ } else {
+ ALOGD("Entering boot_action_init");
+ result = mLibInit(parameters.data(), parameters.size());
+ ALOGD("Returned from boot_action_init");
+ }
+
return result;
}
@@ -99,7 +122,7 @@ void BootAction::shutdown() {
bool BootAction::loadSymbol(const char* symbol, void** loaded) {
*loaded = dlsym(mLibHandle, symbol);
- if (loaded == nullptr) {
+ if (*loaded == nullptr) {
ALOGE("Unable to load symbol : %s :: %s", symbol, dlerror());
return false;
}
diff --git a/cmds/bootanimation/iot/BootAction.h b/cmds/bootanimation/iot/BootAction.h
index 5e2495fe6c51..7119c35db0f9 100644
--- a/cmds/bootanimation/iot/BootAction.h
+++ b/cmds/bootanimation/iot/BootAction.h
@@ -20,6 +20,8 @@
#include <string>
#include <vector>
+#include "BootParameters.h"
+
#include <boot_action/boot_action.h> // libandroidthings native API.
#include <utils/RefBase.h>
@@ -31,7 +33,7 @@ public:
// libraryPath is a fully qualified path to the target .so library.
bool init(const std::string& libraryPath,
- const std::vector<ABootActionParameter>& parameters);
+ const std::unique_ptr<BootParameters>& bootParameters);
// The animation is going to start playing partNumber for the playCount'th
// time, update the action as needed.
@@ -45,7 +47,7 @@ public:
private:
typedef bool (*libInit)(const ABootActionParameter* parameters,
- size_t num_parameters);
+ size_t numParameters);
typedef void (*libStartPart)(int partNumber, int playNumber);
typedef void (*libShutdown)();
@@ -55,6 +57,9 @@ private:
libInit mLibInit = nullptr;
libStartPart mLibStartPart = nullptr;
libShutdown mLibShutdown = nullptr;
+
+ // Called only if the boot is silent.
+ libInit mLibSilentBoot = nullptr;
};
} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp
index da6ad0d1f08f..30a9b2895c44 100644
--- a/cmds/bootanimation/iot/BootParameters.cpp
+++ b/cmds/bootanimation/iot/BootParameters.cpp
@@ -18,45 +18,52 @@
#define LOG_TAG "BootParameters"
+#include <errno.h>
#include <fcntl.h>
-#include <string>
-
#include <android-base/file.h>
-#include <base/json/json_parser.h>
-#include <base/json/json_reader.h>
-#include <base/json/json_value_converter.h>
+#include <json/json.h>
#include <utils/Log.h>
-using android::base::RemoveFileIfExists;
using android::base::ReadFileToString;
-using base::JSONReader;
-using base::JSONValueConverter;
-using base::Value;
+using android::base::RemoveFileIfExists;
+using android::base::WriteStringToFile;
+using Json::ArrayIndex;
+using Json::Reader;
+using Json::Value;
namespace android {
namespace {
-// Brightness and volume are stored as integer strings in next_boot.json.
-// They are divided by this constant to produce the actual float values in
-// range [0.0, 1.0]. This constant must match its counterpart in
-// DeviceManager.
-constexpr const float kFloatScaleFactor = 1000.0f;
+// Keys for deprecated parameters. Devices that OTA from N to O and that used
+// the hidden BootParameters API will store these in the JSON blob. To support
+// the transition from N to O, these keys are mapped to the new parameters.
+constexpr const char *kKeyLegacyVolume = "volume";
+constexpr const char *kKeyLegacyAnimationsDisabled = "boot_animation_disabled";
+constexpr const char *kKeyLegacyParamNames = "param_names";
+constexpr const char *kKeyLegacyParamValues = "param_values";
+
+constexpr const char *kNextBootFile = "/data/misc/bootanimation/next_boot.proto";
+constexpr const char *kLastBootFile = "/data/misc/bootanimation/last_boot.proto";
-constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
-constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
+constexpr const char *kLegacyNextBootFile = "/data/misc/bootanimation/next_boot.json";
+constexpr const char *kLegacyLastBootFile = "/data/misc/bootanimation/last_boot.json";
-void swapBootConfigs() {
- // rename() will fail if next_boot.json doesn't exist, so delete
- // last_boot.json manually first.
+void removeLegacyFiles() {
std::string err;
- if (!RemoveFileIfExists(kLastBootFile, &err))
- ALOGE("Unable to delete last boot file: %s", err.c_str());
+ if (!RemoveFileIfExists(kLegacyLastBootFile, &err)) {
+ ALOGW("Unable to delete %s: %s", kLegacyLastBootFile, err.c_str());
+ }
- if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
- ALOGE("Unable to swap boot files: %s", strerror(errno));
+ err.clear();
+ if (!RemoveFileIfExists(kLegacyNextBootFile, &err)) {
+ ALOGW("Unable to delete %s: %s", kLegacyNextBootFile, err.c_str());
+ }
+}
+void createNextBootFile() {
+ errno = 0;
int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
if (fd == -1) {
ALOGE("Unable to create next boot file: %s", strerror(errno));
@@ -71,54 +78,120 @@ void swapBootConfigs() {
} // namespace
-BootParameters::SavedBootParameters::SavedBootParameters()
- : brightness(-kFloatScaleFactor), volume(-kFloatScaleFactor) {}
-
-void BootParameters::SavedBootParameters::RegisterJSONConverter(
- JSONValueConverter<SavedBootParameters>* converter) {
- converter->RegisterIntField("brightness", &SavedBootParameters::brightness);
- converter->RegisterIntField("volume", &SavedBootParameters::volume);
- converter->RegisterRepeatedString("param_names",
- &SavedBootParameters::param_names);
- converter->RegisterRepeatedString("param_values",
- &SavedBootParameters::param_values);
+// Renames the 'next' boot file to the 'last' file and reads its contents.
+bool BootParameters::swapAndLoadBootConfigContents(const char *lastBootFile,
+ const char *nextBootFile,
+ std::string *contents) {
+ if (!ReadFileToString(nextBootFile, contents)) {
+ RemoveFileIfExists(lastBootFile);
+ return false;
+ }
+
+ errno = 0;
+ if (rename(nextBootFile, lastBootFile) && errno != ENOENT)
+ ALOGE("Unable to swap boot files: %s", strerror(errno));
+
+ return true;
}
BootParameters::BootParameters() {
- swapBootConfigs();
loadParameters();
}
+// Saves the boot parameters state to disk so the framework can read it.
+void BootParameters::storeParameters() {
+ errno = 0;
+ if (!WriteStringToFile(mProto.SerializeAsString(), kLastBootFile)) {
+ ALOGE("Failed to write boot parameters to %s: %s", kLastBootFile, strerror(errno));
+ }
+
+ // WriteStringToFile sets the file permissions to 0666, but these are not
+ // honored by the system.
+ errno = 0;
+ if (chmod(kLastBootFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) {
+ ALOGE("Failed to set permissions for %s: %s", kLastBootFile, strerror(errno));
+ }
+}
+
+// Load the boot parameters from disk, try the old location and format if the
+// file does not exist. Note:
+// - Parse errors result in defaults being used (a normal boot).
+// - Legacy boot parameters default to a silent boot.
void BootParameters::loadParameters() {
+ // Precedence is given to the new file format (.proto).
std::string contents;
- if (!ReadFileToString(kLastBootFile, &contents)) {
- if (errno != ENOENT)
- ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
+ if (swapAndLoadBootConfigContents(kLastBootFile, kNextBootFile, &contents)) {
+ parseBootParameters(contents);
+ } else if (swapAndLoadBootConfigContents(kLegacyLastBootFile, kLegacyNextBootFile, &contents)) {
+ parseLegacyBootParameters(contents);
+ storeParameters();
+ removeLegacyFiles();
+ }
+
+ createNextBootFile();
+}
+void BootParameters::parseBootParameters(const std::string &contents) {
+ if (!mProto.ParseFromString(contents)) {
+ ALOGW("Failed to parse parameters from %s", kLastBootFile);
return;
}
- std::unique_ptr<Value> json = JSONReader::Read(contents);
- if (json.get() == nullptr) {
+ loadStateFromProto();
+}
+
+// Parses the JSON in the proto.
+void BootParameters::parseLegacyBootParameters(const std::string &contents) {
+ Value json;
+ if (!Reader().parse(contents, json)) {
+ ALOGW("Failed to parse parameters from %s", kLegacyLastBootFile);
return;
}
- JSONValueConverter<SavedBootParameters> converter;
- if (converter.Convert(*(json.get()), &mRawParameters)) {
- mBrightness = mRawParameters.brightness / kFloatScaleFactor;
- mVolume = mRawParameters.volume / kFloatScaleFactor;
-
- if (mRawParameters.param_names.size() == mRawParameters.param_values.size()) {
- for (size_t i = 0; i < mRawParameters.param_names.size(); i++) {
- mParameters.push_back({
- .key = mRawParameters.param_names[i]->c_str(),
- .value = mRawParameters.param_values[i]->c_str()
- });
+ int volume = 0;
+ bool bootAnimationDisabled = true;
+
+ Value &jsonValue = json[kKeyLegacyVolume];
+ if (jsonValue.isIntegral()) {
+ volume = jsonValue.asInt();
+ }
+
+ jsonValue = json[kKeyLegacyAnimationsDisabled];
+ if (jsonValue.isIntegral()) {
+ bootAnimationDisabled = jsonValue.asInt() == 1;
+ }
+
+ // Assume a silent boot unless all of the following are true -
+ // 1. The volume is neither 0 nor -1000 (the legacy default value).
+ // 2. The boot animations are explicitly enabled.
+ // Note: brightness was never used.
+ mProto.set_silent_boot((volume == 0) || (volume == -1000) || bootAnimationDisabled);
+
+ Value &keys = json[kKeyLegacyParamNames];
+ Value &values = json[kKeyLegacyParamValues];
+ if (keys.isArray() && values.isArray() && (keys.size() == values.size())) {
+ for (ArrayIndex i = 0; i < keys.size(); ++i) {
+ auto &key = keys[i];
+ auto &value = values[i];
+ if (key.isString() && value.isString()) {
+ auto userParameter = mProto.add_user_parameter();
+ userParameter->set_key(key.asString());
+ userParameter->set_value(value.asString());
}
- } else {
- ALOGW("Parameter names and values size mismatch");
}
}
+
+ loadStateFromProto();
+}
+
+void BootParameters::loadStateFromProto() {
+ // A missing key returns a safe, default value.
+ // Ignore invalid or missing parameters.
+ mIsSilentBoot = mProto.silent_boot();
+
+ for (const auto &param : mProto.user_parameter()) {
+ mParameters.push_back({.key = param.key().c_str(), .value = param.value().c_str()});
+ }
}
} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
index c10bd44bc2ca..cbd1ca61cfc3 100644
--- a/cmds/bootanimation/iot/BootParameters.h
+++ b/cmds/bootanimation/iot/BootParameters.h
@@ -18,10 +18,11 @@
#define _BOOTANIMATION_BOOT_PARAMETERS_H_
#include <list>
+#include <string>
#include <vector>
-#include <base/json/json_value_converter.h>
#include <boot_action/boot_action.h> // libandroidthings native API.
+#include <boot_parameters.pb.h>
namespace android {
@@ -32,39 +33,39 @@ public:
// to clear the parameters for next boot.
BootParameters();
- // Returns true if volume/brightness were explicitly set on reboot.
- bool hasVolume() const { return mVolume >= 0; }
- bool hasBrightness() const { return mBrightness >= 0; }
-
- // Returns volume/brightness in [0,1], or -1 if unset.
- float getVolume() const { return mVolume; }
- float getBrightness() const { return mBrightness; }
+ // Returns whether or not this is a silent boot.
+ bool isSilentBoot() const { return mIsSilentBoot; }
// Returns the additional boot parameters that were set on reboot.
const std::vector<ABootActionParameter>& getParameters() const { return mParameters; }
-private:
- // Raw boot saved_parameters loaded from .json.
- struct SavedBootParameters {
- int brightness;
- int volume;
- std::vector<std::unique_ptr<std::string>> param_names;
- std::vector<std::unique_ptr<std::string>> param_values;
+ // Exposed for testing. Sets the parameters to the serialized proto.
+ void parseBootParameters(const std::string &contents);
+
+ // For devices that OTA from N to O.
+ // Exposed for testing. Sets the parameters to the raw JSON.
+ void parseLegacyBootParameters(const std::string &contents);
- SavedBootParameters();
- static void RegisterJSONConverter(
- ::base::JSONValueConverter<SavedBootParameters>* converter);
- };
+ // Exposed for testing. Loads the contents from |nextBootFile| and replaces
+ // |lastBootFile| with |nextBootFile|.
+ static bool swapAndLoadBootConfigContents(const char *lastBootFile, const char *nextBootFile,
+ std::string *contents);
+ private:
void loadParameters();
- float mVolume = -1.f;
- float mBrightness = -1.f;
+ // Replaces the legacy JSON blob with the updated version, allowing the
+ // framework to read it.
+ void storeParameters();
+
+ void loadStateFromProto();
+
+ bool mIsSilentBoot = false;
+
std::vector<ABootActionParameter> mParameters;
- // ABootActionParameter is just a raw pointer so we need to keep the
- // original strings around to avoid losing them.
- SavedBootParameters mRawParameters;
+ // Store the proto because mParameters makes a shallow copy.
+ android::things::proto::BootParameters mProto;
};
} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters_test.cpp b/cmds/bootanimation/iot/BootParameters_test.cpp
new file mode 100644
index 000000000000..d55bce6eecc3
--- /dev/null
+++ b/cmds/bootanimation/iot/BootParameters_test.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "BootParameters.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <boot_parameters.pb.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace {
+
+TEST(BootParametersTest, TestNoBootParametersIsNotSilent) {
+ android::things::proto::BootParameters proto;
+
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ ASSERT_FALSE(bootParameters.isSilentBoot());
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestParseIsSilent) {
+ android::things::proto::BootParameters proto;
+ proto.set_silent_boot(true);
+
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseIsNotSilent) {
+ android::things::proto::BootParameters proto;
+ proto.set_silent_boot(false);
+
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ ASSERT_FALSE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseBootParameters) {
+ android::things::proto::BootParameters proto;
+ proto.set_silent_boot(false);
+
+ auto userParameter = proto.add_user_parameter();
+ userParameter->set_key("key1");
+ userParameter->set_value("value1");
+
+ userParameter = proto.add_user_parameter();
+ userParameter->set_key("key2");
+ userParameter->set_value("value2");
+
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ auto &parameters = bootParameters.getParameters();
+ ASSERT_EQ(2u, parameters.size());
+ ASSERT_STREQ(parameters[0].key, "key1");
+ ASSERT_STREQ(parameters[0].value, "value1");
+ ASSERT_STREQ(parameters[1].key, "key2");
+ ASSERT_STREQ(parameters[1].value, "value2");
+}
+
+TEST(BootParametersTest, TestParseLegacyDisableBootAnimationIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":100,
+ "boot_animation_disabled":1,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyZeroVolumeIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":0,
+ "boot_animation_disabled":0,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyDefaultVolumeIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":-1000,
+ "boot_animation_disabled":0,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyNotSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":500,
+ "boot_animation_disabled":0,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_FALSE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyParameters) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":100,
+ "boot_animation_disabled":1,
+ "param_names":["key1", "key2"],
+ "param_values":["value1", "value2"]
+ }
+ )");
+
+ auto parameters = bootParameters.getParameters();
+ ASSERT_EQ(2u, parameters.size());
+ ASSERT_STREQ(parameters[0].key, "key1");
+ ASSERT_STREQ(parameters[0].value, "value1");
+ ASSERT_STREQ(parameters[1].key, "key2");
+ ASSERT_STREQ(parameters[1].value, "value2");
+}
+
+TEST(BootParametersTest, TestParseLegacyZeroParameters) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":100,
+ "boot_animation_disabled":1,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestMalformedLegacyParametersAreSkipped) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":500,
+ "volume":500,
+ "boot_animation_disabled":0,
+ "param_names":["key1", "key2"],
+ "param_values":[1, "value2"]
+ }
+ )");
+
+ auto parameters = bootParameters.getParameters();
+ ASSERT_EQ(1u, parameters.size());
+ ASSERT_STREQ(parameters[0].key, "key2");
+ ASSERT_STREQ(parameters[0].value, "value2");
+}
+
+TEST(BootParametersTest, TestLegacyUnequalParameterSizesAreSkipped) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":500,
+ "volume":500,
+ "boot_animation_disabled":0,
+ "param_names":["key1", "key2"],
+ "param_values":["value1"]
+ }
+ )");
+
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestMissingLegacyBootParametersIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":500
+ }
+ )");
+
+ EXPECT_TRUE(bootParameters.isSilentBoot());
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestLastFileIsRemovedOnError) {
+ TemporaryFile lastFile;
+ TemporaryDir tempDir;
+ std::string nonExistentFilePath(std::string(tempDir.path) + "/nonexistent");
+ std::string contents;
+
+ BootParameters::swapAndLoadBootConfigContents(lastFile.path, nonExistentFilePath.c_str(),
+ &contents);
+
+ struct stat buf;
+ ASSERT_EQ(-1, lstat(lastFile.path, &buf));
+ ASSERT_TRUE(contents.empty());
+}
+
+TEST(BootParametersTest, TestNextFileIsRemovedLastFileExistsOnSuccess) {
+ TemporaryFile lastFile;
+ TemporaryFile nextFile;
+
+ base::WriteStringToFile("foo", nextFile.path);
+
+ std::string contents;
+ // Expected side effects:
+ // - |next_file| is moved to |last_file|
+ // - |contents| is the contents of |next_file| before being moved.
+ BootParameters::swapAndLoadBootConfigContents(lastFile.path, nextFile.path, &contents);
+
+ struct stat buf;
+ ASSERT_EQ(0, lstat(lastFile.path, &buf));
+ ASSERT_EQ(-1, lstat(nextFile.path, &buf));
+ ASSERT_EQ(contents, "foo");
+
+ contents.clear();
+ ASSERT_TRUE(base::ReadFileToString(lastFile.path, &contents));
+ ASSERT_EQ(contents, "foo");
+}
+
+} // namespace
+
+} // namespace android
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
index 00cef430135e..2a3d3766ab38 100644
--- a/cmds/bootanimation/iot/iotbootanimation_main.cpp
+++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp
@@ -59,7 +59,7 @@ public:
}
mBootAction = new BootAction();
- if (!mBootAction->init(library_path, mBootParameters->getParameters())) {
+ if (!mBootAction->init(library_path, mBootParameters)) {
mBootAction = NULL;
}
};
@@ -116,8 +116,16 @@ int main() {
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
- sp<BootAnimation> boot = new BootAnimation(
- new BootActionAnimationCallbacks(std::move(bootParameters)));
+ bool isSilentBoot = bootParameters->isSilentBoot();
+ sp<BootActionAnimationCallbacks> callbacks =
+ new BootActionAnimationCallbacks(std::move(bootParameters));
+
+ // On silent boot, animations aren't displayed.
+ if (isSilentBoot) {
+ callbacks->init({});
+ } else {
+ sp<BootAnimation> boot = new BootAnimation(callbacks);
+ }
IPCThreadState::self()->joinThreadPool();
return 0;