Exit boot animation cleanly.

The desc.txt file can now mark parts as 'must finish cleanly' by using
'c' as the part line prefix rather than 'p'.  If so indicated, if the
bootanimation is asked to quit it will do so only after waiting to
finish that part.

I considered either making init.c service killing smarter or promoting
bootanim to be a bindable service with a requestExit method.  However,
these changes are probably too big/risky given our ship date.  So
I used a property as a mailbox between SurfaceFlinger and bootanim.

Bug: 6679877
Change-Id: I1f8dd9e7da1ea80a483b31fa14c4a5645922d774
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 225be9c..6b5048607 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -54,6 +54,7 @@
 #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 EXIT_PROP_NAME "service.bootanim.exit"
 
 extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
                            const struct timespec *request,
@@ -297,6 +298,9 @@
         r = movie();
     }
 
+    // No need to force exit anymore
+    property_set(EXIT_PROP_NAME, "0");
+
     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     eglDestroyContext(mDisplay, mContext);
     eglDestroySurface(mDisplay, mSurface);
@@ -363,6 +367,8 @@
         const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
         if (sleepTime > 0)
             usleep(sleepTime);
+
+        checkExit();
     } while (!exitPending());
 
     glDeleteTextures(1, &mAndroid[0].name);
@@ -371,6 +377,16 @@
 }
 
 
+void BootAnimation::checkExit() {
+    // Allow surface flinger to gracefully request shutdown
+    char value[PROPERTY_VALUE_MAX];
+    property_get(EXIT_PROP_NAME, value, "0");
+    int exitnow = atoi(value);
+    if (exitnow) {
+        requestExit();
+    }
+}
+
 bool BootAnimation::movie()
 {
     ZipFileRO& zip(mZip);
@@ -397,20 +413,23 @@
         const char* l = line.string();
         int fps, width, height, count, pause;
         char path[256];
+        char pathType;
         if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
-            //ALOGD("> w=%d, h=%d, fps=%d", fps, width, height);
+            //LOGD("> w=%d, h=%d, fps=%d", width, height, fps);
             animation.width = width;
             animation.height = height;
             animation.fps = fps;
         }
-        if (sscanf(l, "p %d %d %s", &count, &pause, path) == 3) {
-            //ALOGD("> count=%d, pause=%d, path=%s", count, pause, path);
+        else if (sscanf(l, " %c %d %d %s", &pathType, &count, &pause, path) == 4) {
+            //LOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path);
             Animation::Part part;
+            part.playUntilComplete = pathType == 'c';
             part.count = count;
             part.pause = pause;
             part.path = path;
             animation.parts.add(part);
         }
+
         s = ++endl;
     }
 
@@ -472,13 +491,17 @@
     Region clearReg(Rect(mWidth, mHeight));
     clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));
 
-    for (int i=0 ; i<pcount && !exitPending() ; i++) {
+    for (int i=0 ; i<pcount ; i++) {
         const Animation::Part& part(animation.parts[i]);
         const size_t fcount = part.frames.size();
         glBindTexture(GL_TEXTURE_2D, 0);
 
         for (int r=0 ; !part.count || r<part.count ; r++) {
-            for (int j=0 ; j<fcount && !exitPending(); j++) {
+            // Exit any non playuntil complete parts immediately
+            if(exitPending() && !part.playUntilComplete)
+                break;
+
+            for (int j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
                 const Animation::Frame& frame(part.frames[j]);
                 nsecs_t lastFrame = systemTime();
 
@@ -525,8 +548,15 @@
                         err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
                     } while (err<0 && errno == EINTR);
                 }
+
+                checkExit();
             }
+
             usleep(part.pause * ns2us(frameDuration));
+
+            // For infinite parts, we've now played them at least once, so perhaps exit
+            if(exitPending() && !part.count)
+                break;
         }
 
         // free the textures for this part