OSDN Git Service

donut snapshot
authorJean-Baptiste Queru <jbq@google.com>
Tue, 21 Jul 2009 18:16:54 +0000 (11:16 -0700)
committerJean-Baptiste Queru <jbq@google.com>
Tue, 21 Jul 2009 18:16:54 +0000 (11:16 -0700)
95 files changed:
NOTICE
camera/libcameraservice/CameraService.cpp
camera/libcameraservice/CameraService.h
cmds/keystore/Android.mk [new file with mode: 0644]
cmds/keystore/certtool.h [new file with mode: 0644]
cmds/keystore/common.h [new file with mode: 0644]
cmds/keystore/keymgmt.c [new file with mode: 0644]
cmds/keystore/keymgmt.h [new file with mode: 0644]
cmds/keystore/keystore_get.h [new file with mode: 0644]
cmds/keystore/netkeystore.c [new file with mode: 0644]
cmds/keystore/netkeystore.h [new file with mode: 0644]
cmds/runtime/main_runtime.cpp
include/private/opengles/gl_context.h
include/tts/TtsEngine.h [new file with mode: 0644]
include/ui/Camera.h
include/ui/Point.h
include/ui/Rect.h
include/utils/AssetManager.h
include/utils/BackupHelpers.h [new file with mode: 0644]
include/utils/ByteOrder.h
include/utils/Parcel.h
include/utils/ResourceTypes.h
include/utils/TimeUtils.h [deleted file]
include/utils/backup_helpers.h [deleted file]
libs/audioflinger/A2dpAudioInterface.cpp
libs/audioflinger/A2dpAudioInterface.h
libs/audioflinger/AudioBufferProvider.h
libs/audioflinger/AudioDumpInterface.h
libs/audioflinger/AudioFlinger.cpp
libs/audioflinger/AudioFlinger.h
libs/audioflinger/AudioHardwareGeneric.cpp
libs/audioflinger/AudioHardwareGeneric.h
libs/audioflinger/AudioHardwareStub.cpp
libs/audioflinger/AudioHardwareStub.h
libs/surfaceflinger/Android.mk
libs/surfaceflinger/BootAnimation.cpp [deleted file]
libs/surfaceflinger/BootAnimation.h [deleted file]
libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
libs/surfaceflinger/LayerBitmap.cpp
libs/surfaceflinger/SurfaceFlinger.cpp
libs/surfaceflinger/SurfaceFlinger.h
libs/surfaceflinger/VRamHeap.cpp
libs/ui/Android.mk
libs/ui/Camera.cpp
libs/ui/ISurfaceFlingerClient.cpp
libs/ui/Overlay.cpp
libs/ui/Point.cpp [deleted file]
libs/ui/Rect.cpp
libs/ui/Time.cpp [deleted file]
libs/utils/Android.mk
libs/utils/Asset.cpp
libs/utils/AssetManager.cpp
libs/utils/BackupData.cpp [new file with mode: 0644]
libs/utils/BackupHelpers.cpp [new file with mode: 0644]
libs/utils/CharacterData.h [moved from libs/utils/characterData.h with 100% similarity]
libs/utils/Parcel.cpp
libs/utils/ResourceTypes.cpp
libs/utils/Unicode.cpp
libs/utils/ZipEntry.cpp
libs/utils/ZipFile.cpp
libs/utils/ZipFileCRO.cpp
libs/utils/ZipFileRO.cpp
libs/utils/ZipUtils.cpp
libs/utils/file_backup_helper.cpp [deleted file]
libs/utils/futex_synchro.c
opengl/libagl/array.cpp
opengl/libagl/light.cpp
opengl/libagl/matrix.cpp
opengl/libagl/texture.cpp
opengl/libs/Android.mk
opengl/libs/EGL/egl.cpp
opengl/libs/EGL/gpu.cpp
opengl/tests/angeles/Android.mk
opengl/tests/filter/Android.mk
opengl/tests/finish/Android.mk
opengl/tests/lighting1709/Android.mk [new file with mode: 0644]
opengl/tests/lighting1709/AndroidManifest.xml [new file with mode: 0644]
opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java [new file with mode: 0644]
opengl/tests/textures/Android.mk
opengl/tests/tritex/Android.mk
opengl/tools/glgen/gen
opengl/tools/glgen/src/JniCodeEmitter.java
opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
vpn/java/android/net/vpn/IVpnService.aidl [new file with mode: 0644]
vpn/java/android/net/vpn/L2tpIpsecProfile.java [new file with mode: 0644]
vpn/java/android/net/vpn/L2tpIpsecPskProfile.java [new file with mode: 0644]
vpn/java/android/net/vpn/L2tpProfile.java [new file with mode: 0644]
vpn/java/android/net/vpn/PptpProfile.java [new file with mode: 0644]
vpn/java/android/net/vpn/VpnManager.java [new file with mode: 0644]
vpn/java/android/net/vpn/VpnProfile.aidl [new file with mode: 0644]
vpn/java/android/net/vpn/VpnProfile.java [new file with mode: 0644]
vpn/java/android/net/vpn/VpnState.java [new file with mode: 0644]
vpn/java/android/net/vpn/VpnType.java [new file with mode: 0644]

diff --git a/NOTICE b/NOTICE
index 267a6aa..bb9c5f2 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -220,3 +220,54 @@ the Apache2 License.
 
    END OF TERMS AND CONDITIONS
 
+
+
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+Unicode Data Files include all data files under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/,
+and http://www.unicode.org/cldr/data/ . Unicode Software includes any
+source code published in the Unicode Standard or under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/, and
+http://www.unicode.org/cldr/data/.
+
+NOTICE TO USER: Carefully read the following legal agreement. BY
+DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA
+FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY
+ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF
+THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY,
+DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright Â© 1991-2008 Unicode, Inc. All rights reserved. Distributed
+under the Terms of Use in http://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation (the
+"Data Files") or Unicode software and any associated documentation (the
+"Software") to deal in the Data Files or Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, and/or sell copies of the Data Files or Software,
+and to permit persons to whom the Data Files or Software are furnished to
+do so, provided that (a) the above copyright notice(s) and this permission
+notice appear with all copies of the Data Files or Software, (b) both the
+above copyright notice(s) and this permission notice appear in associated
+documentation, and (c) there is clear notice in each modified Data File
+or in the Software as well as in the documentation associated with the
+Data File(s) or Software that the data or software has been modified.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in these Data Files or Software without prior written
+authorization of the copyright holder.
index f85ea9f..022fe5a 100644 (file)
@@ -32,6 +32,7 @@
 #include <media/AudioSystem.h>
 #include "CameraService.h"
 
+#include <cutils/atomic.h>
 #include <cutils/properties.h>
 
 namespace android {
@@ -42,6 +43,7 @@ extern "C" {
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <pthread.h>
+#include <signal.h>
 }
 
 // When you enable this, as well as DEBUG_REFS=1 and
@@ -63,6 +65,10 @@ extern "C" {
 static int debug_frame_cnt;
 #endif
 
+static int getCallingPid() {
+    return IPCThreadState::self()->getCallingPid();
+}
+
 // ----------------------------------------------------------------------------
 
 void CameraService::instantiate() {
@@ -76,6 +82,7 @@ CameraService::CameraService() :
     BnCameraService()
 {
     LOGI("CameraService started: pid=%d", getpid());
+    mUsers = 0;
 }
 
 CameraService::~CameraService()
@@ -87,72 +94,105 @@ CameraService::~CameraService()
 
 sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient)
 {
-    LOGD("Connect E from ICameraClient %p", cameraClient->asBinder().get());
+    int callingPid = getCallingPid();
+    LOGD("CameraService::connect E (pid %d, client %p)", callingPid,
+            cameraClient->asBinder().get());
 
-    Mutex::Autolock lock(mLock);
+    Mutex::Autolock lock(mServiceLock);
     sp<Client> client;
     if (mClient != 0) {
         sp<Client> currentClient = mClient.promote();
         if (currentClient != 0) {
             sp<ICameraClient> currentCameraClient(currentClient->getCameraClient());
             if (cameraClient->asBinder() == currentCameraClient->asBinder()) {
-                // this is the same client reconnecting...
-                LOGD("Connect X same client (%p) is reconnecting...", cameraClient->asBinder().get());
+                // This is the same client reconnecting...
+                LOGD("CameraService::connect X (pid %d, same client %p) is reconnecting...",
+                    callingPid, cameraClient->asBinder().get());
                 return currentClient;
             } else {
-                // it's another client... reject it
-                LOGD("new client (%p) attempting to connect - rejected", cameraClient->asBinder().get());
+                // It's another client... reject it
+                LOGD("CameraService::connect X (pid %d, new client %p) rejected. "
+                    "(old pid %d, old client %p)",
+                    callingPid, cameraClient->asBinder().get(),
+                    currentClient->mClientPid, currentCameraClient->asBinder().get());
+                if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) {
+                    LOGD("The old client is dead!");
+                }
                 return client;
             }
         } else {
             // can't promote, the previous client has died...
-            LOGD("new client connecting, old reference was dangling...");
+            LOGD("New client (pid %d) connecting, old reference was dangling...",
+                    callingPid);
             mClient.clear();
         }
     }
 
+    if (mUsers > 0) {
+        LOGD("Still have client, rejected");
+        return client;
+    }
+
     // create a new Client object
-    client = new Client(this, cameraClient, IPCThreadState::self()->getCallingPid());
+    client = new Client(this, cameraClient, callingPid);
     mClient = client;
 #if DEBUG_CLIENT_REFERENCES
     // Enable tracking for this object, and track increments and decrements of
     // the refcount.
     client->trackMe(true, true);
 #endif
-    LOGD("Connect X");
+    LOGD("CameraService::connect X");
     return client;
 }
 
 void CameraService::removeClient(const sp<ICameraClient>& cameraClient)
 {
-    // declar this outside the lock to make absolutely sure the
+    int callingPid = getCallingPid();
+
+    // Declare this outside the lock to make absolutely sure the
     // destructor won't be called with the lock held.
     sp<Client> client;
 
-    Mutex::Autolock lock(mLock);
+    Mutex::Autolock lock(mServiceLock);
 
     if (mClient == 0) {
         // This happens when we have already disconnected.
-        LOGV("mClient is null.");
+        LOGD("removeClient (pid %d): already disconnected", callingPid);
         return;
     }
 
-    // Promote mClient. It should never fail because we're called from
-    // a binder call, so someone has to have a strong reference.
+    // Promote mClient. It can fail if we are called from this path:
+    // Client::~Client() -> disconnect() -> removeClient().
     client = mClient.promote();
     if (client == 0) {
-        LOGW("can't get a strong reference on mClient!");
+        LOGD("removeClient (pid %d): no more strong reference", callingPid);
         mClient.clear();
         return;
     }
 
     if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) {
         // ugh! that's not our client!!
-        LOGW("removeClient() called, but mClient doesn't match!");
+        LOGW("removeClient (pid %d): mClient doesn't match!", callingPid);
     } else {
         // okay, good, forget about mClient
         mClient.clear();
     }
+
+    LOGD("removeClient (pid %d) done", callingPid);
+}
+
+// The reason we need this count is a new CameraService::connect() request may
+// come in while the previous Client's destructor has not been run or is still
+// running. If the last strong reference of the previous Client is gone but
+// destructor has not been run, we should not allow the new Client to be created
+// because we need to wait for the previous Client to tear down the hardware
+// first.
+void CameraService::incUsers() {
+    android_atomic_inc(&mUsers);
+}
+
+void CameraService::decUsers() {
+    android_atomic_dec(&mUsers);
 }
 
 static sp<MediaPlayer> newMediaPlayer(const char *file) 
@@ -177,7 +217,8 @@ static sp<MediaPlayer> newMediaPlayer(const char *file)
 CameraService::Client::Client(const sp<CameraService>& cameraService,
         const sp<ICameraClient>& cameraClient, pid_t clientPid)
 {
-    LOGD("Client E constructor");
+    int callingPid = getCallingPid();
+    LOGD("Client::Client E (pid %d)", callingPid);
     mCameraService = cameraService;
     mCameraClient = cameraClient;
     mClientPid = clientPid;
@@ -189,22 +230,28 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
 
     // Callback is disabled by default
     mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
-    LOGD("Client X constructor");
+    cameraService->incUsers();
+    LOGD("Client::Client X (pid %d)", callingPid);
 }
 
 status_t CameraService::Client::checkPid()
 {
-    if (mClientPid == IPCThreadState::self()->getCallingPid()) return NO_ERROR;
-    LOGW("Attempt to use locked camera (%p) from different process", getCameraClient()->asBinder().get());
+    int callingPid = getCallingPid();
+    if (mClientPid == callingPid) return NO_ERROR;
+    LOGW("Attempt to use locked camera (client %p) from different process "
+        " (old pid %d, new pid %d)",
+        getCameraClient()->asBinder().get(), mClientPid, callingPid);
     return -EBUSY;
 }
 
 status_t CameraService::Client::lock()
 {
+    int callingPid = getCallingPid();
+    LOGD("lock from pid %d (mClientPid %d)", callingPid, mClientPid);
     Mutex::Autolock _l(mLock);
     // lock camera to this client if the the camera is unlocked
     if (mClientPid == 0) {
-        mClientPid = IPCThreadState::self()->getCallingPid();
+        mClientPid = callingPid;
         return NO_ERROR;
     }
     // returns NO_ERROR if the client already owns the camera, -EBUSY otherwise
@@ -213,13 +260,14 @@ status_t CameraService::Client::lock()
 
 status_t CameraService::Client::unlock()
 {
+    int callingPid = getCallingPid();
+    LOGD("unlock from pid %d (mClientPid %d)", callingPid, mClientPid);    
     Mutex::Autolock _l(mLock);
     // allow anyone to use camera
-    LOGV("unlock (%p)", getCameraClient()->asBinder().get());
     status_t result = checkPid();
     if (result == NO_ERROR) {
         mClientPid = 0;
-
+        LOGD("clear mCameraClient (pid %d)", callingPid);
         // we need to remove the reference so that when app goes
         // away, the reference count goes to 0.
         mCameraClient.clear();
@@ -229,15 +277,17 @@ status_t CameraService::Client::unlock()
 
 status_t CameraService::Client::connect(const sp<ICameraClient>& client)
 {
+    int callingPid = getCallingPid();
+
     // connect a new process to the camera
-    LOGV("connect (%p)", client->asBinder().get());
+    LOGD("Client::connect E (pid %d, client %p)", callingPid, client->asBinder().get());
 
     // I hate this hack, but things get really ugly when the media recorder
     // service is handing back the camera to the app. The ICameraClient
     // destructor will be called during the same IPC, making it look like
     // the remote client is trying to disconnect. This hack temporarily
     // sets the mClientPid to an invalid pid to prevent the hardware from
-    //  being torn down.
+    // being torn down.
     {
 
         // hold a reference to the old client or we will deadlock if the client is
@@ -245,25 +295,30 @@ status_t CameraService::Client::connect(const sp<ICameraClient>& client)
         sp<ICameraClient> oldClient;
         {
             Mutex::Autolock _l(mLock);
-            if (mClientPid != 0) {
-                LOGW("Tried to connect to locked camera");
+            if (mClientPid != 0 && checkPid() != NO_ERROR) {
+                LOGW("Tried to connect to locked camera (old pid %d, new pid %d)",
+                        mClientPid, callingPid);
                 return -EBUSY;
             }
             oldClient = mCameraClient;
 
             // did the client actually change?
-            if (client->asBinder() == mCameraClient->asBinder()) return NO_ERROR;
+            if (client->asBinder() == mCameraClient->asBinder()) {
+                LOGD("Connect to the same client");
+                return NO_ERROR;
+            }
 
             mCameraClient = client;
             mClientPid = -1;
             mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
-            LOGV("connect new process (%d) to existing camera client", mClientPid);
+            LOGD("Connect to the new client (pid %d, client %p)",
+                callingPid, mCameraClient->asBinder().get());
         }
 
     }
     // the old client destructor is called when oldClient goes out of scope
     // now we set the new PID to lock the interface again
-    mClientPid = IPCThreadState::self()->getCallingPid();
+    mClientPid = callingPid;
 
     return NO_ERROR;
 }
@@ -280,8 +335,11 @@ static void *unregister_surface(void *arg)
 
 CameraService::Client::~Client()
 {
+    int callingPid = getCallingPid();
+
     // tear down client
-    LOGD("Client (%p)  E destructor", getCameraClient()->asBinder().get());
+    LOGD("Client::~Client E (pid %d, client %p)",
+            callingPid, getCameraClient()->asBinder().get());
     if (mSurface != 0 && !mUseOverlay) {
 #if HAVE_ANDROID_OS
         pthread_t thr;
@@ -307,49 +365,59 @@ CameraService::Client::~Client()
     }
 
     // make sure we tear down the hardware
-    mClientPid = IPCThreadState::self()->getCallingPid();
+    mClientPid = callingPid;
     disconnect();
-    LOGD("Client X destructor");
+    LOGD("Client::~Client X (pid %d)", mClientPid);
 }
 
 void CameraService::Client::disconnect()
 {
-    LOGD("Client (%p) E disconnect from (%d)",
-            getCameraClient()->asBinder().get(),
-            IPCThreadState::self()->getCallingPid());
+    int callingPid = getCallingPid();
+
+    LOGD("Client::disconnect() E (pid %d client %p)",
+            callingPid, getCameraClient()->asBinder().get());
+
     Mutex::Autolock lock(mLock);
     if (mClientPid <= 0) {
-        LOGV("camera is unlocked, don't tear down hardware");
+        LOGD("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
         return;
     }
     if (checkPid() != NO_ERROR) {
-        LOGV("Different client - don't disconnect");
+        LOGD("Different client - don't disconnect");
         return;
     }
 
-    mCameraService->removeClient(mCameraClient);
-    if (mHardware != 0) {
-        LOGV("hardware teardown");
-        // Before destroying mHardware, we must make sure it's in the
-        // idle state.
-        mHardware->stopPreview();
-        // Cancel all picture callbacks.
-        mHardware->cancelPicture(true, true, true);
-        // Release the hardware resources.
-        mHardware->release();
-    }
+    // Make sure disconnect() is done once and once only, whether it is called
+    // from the user directly, or called by the destructor.
+    if (mHardware == 0) return;
+
+    LOGD("hardware teardown");
+    // Before destroying mHardware, we must make sure it's in the
+    // idle state.
+    mHardware->stopPreview();
+    // Cancel all picture callbacks.
+    mHardware->cancelPicture(true, true, true);
+    // Release the hardware resources.
+    mHardware->release();
     mHardware.clear();
-    LOGD("Client X disconnect");
+
+    mCameraService->removeClient(mCameraClient);
+    mCameraService->decUsers();
+
+    LOGD("Client::disconnect() X (pid %d)", callingPid);
 }
 
 // pass the buffered ISurface to the camera service
 status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
 {
-    LOGD("setPreviewDisplay(%p)", surface.get());
+    LOGD("setPreviewDisplay(%p) (pid %d)",
+         ((surface == NULL) ? NULL : surface.get()), getCallingPid());
     Mutex::Autolock lock(mLock);
     status_t result = checkPid();
     if (result != NO_ERROR) return result;
+
     Mutex::Autolock surfaceLock(mSurfaceLock);
+    result = NO_ERROR;
     // asBinder() is safe on NULL (returns NULL)
     if (surface->asBinder() != mSurface->asBinder()) {
         if (mSurface != 0 && !mUseOverlay) {
@@ -357,24 +425,35 @@ status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
             mSurface->unregisterBuffers();
         }
         mSurface = surface;
+        // If preview has been already started, set overlay or register preview
+        // buffers now.
+        if (mHardware->previewEnabled()) {
+            if (mUseOverlay) {
+                result = setOverlay();
+            } else if (mSurface != 0) {
+                result = registerPreviewBuffers();
+            }
+        }
     }
-    return NO_ERROR;
+    return result;
 }
 
 // set the preview callback flag to affect how the received frames from
 // preview are handled.
 void CameraService::Client::setPreviewCallbackFlag(int callback_flag)
 {
-    LOGV("setPreviewCallbackFlag");
+    LOGV("setPreviewCallbackFlag (pid %d)", getCallingPid());
     Mutex::Autolock lock(mLock);
     if (checkPid() != NO_ERROR) return;
     mPreviewCallbackFlag = callback_flag;
 }
 
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
 status_t CameraService::Client::startCameraMode(camera_mode mode)
 {
-    LOGD("startCameraMode(%d)", mode);
+    int callingPid = getCallingPid();
+
+    LOGD("startCameraMode(%d) (pid %d)", mode, callingPid);
 
     /* we cannot call into mHardware with mLock held because
      * mHardware has callbacks onto us which acquire this lock
@@ -389,23 +468,25 @@ status_t CameraService::Client::startCameraMode(camera_mode mode)
         return INVALID_OPERATION;
     }
 
-    if (mSurface == 0) {
-        LOGE("setPreviewDisplay must be called before startCameraMode!");
-        return INVALID_OPERATION;
-    }
-
     switch(mode) {
     case CAMERA_RECORDING_MODE:
+        if (mSurface == 0) {
+            LOGE("setPreviewDisplay must be called before startRecordingMode.");
+            return INVALID_OPERATION;
+        }
         return startRecordingMode();
 
     default: // CAMERA_PREVIEW_MODE
+        if (mSurface == 0) {
+            LOGD("mSurface is not set yet.");
+        }
         return startPreviewMode();
     }
 }
 
 status_t CameraService::Client::startRecordingMode()
 {
-    LOGV("startRecordingMode");
+    LOGD("startRecordingMode (pid %d)", getCallingPid());
 
     status_t ret = UNKNOWN_ERROR;
 
@@ -431,9 +512,65 @@ status_t CameraService::Client::startRecordingMode()
     return ret;
 }
 
+status_t CameraService::Client::setOverlay()
+{
+    LOGD("setOverlay");
+    int w, h;
+    CameraParameters params(mHardware->getParameters());
+    params.getPreviewSize(&w, &h);
+
+    const char *format = params.getPreviewFormat();
+    int fmt;
+    if (!strcmp(format, "yuv422i"))
+        fmt = OVERLAY_FORMAT_YCbCr_422_I;
+    else if (!strcmp(format, "rgb565"))
+        fmt = OVERLAY_FORMAT_RGB_565;
+    else {
+        LOGE("Invalid preview format for overlays");
+        return -EINVAL;
+    }
+
+    status_t ret = NO_ERROR;
+    if (mSurface != 0) {
+        sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
+        ret = mHardware->setOverlay(new Overlay(ref));
+    } else {
+        ret = mHardware->setOverlay(NULL);
+    }
+    if (ret != NO_ERROR) {
+        LOGE("mHardware->setOverlay() failed with status %d\n", ret);
+    }
+    return ret;
+}
+
+status_t CameraService::Client::registerPreviewBuffers()
+{
+    int w, h;
+    CameraParameters params(mHardware->getParameters());
+    params.getPreviewSize(&w, &h);
+
+    uint32_t transform = 0;
+    if (params.getOrientation() ==
+        CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
+      LOGV("portrait mode");
+      transform = ISurface::BufferHeap::ROT_90;
+    }
+    ISurface::BufferHeap buffers(w, h, w, h,
+                                 PIXEL_FORMAT_YCbCr_420_SP,
+                                 transform,
+                                 0,
+                                 mHardware->getPreviewHeap());
+
+    status_t ret = mSurface->registerBuffers(buffers);
+    if (ret != NO_ERROR) {
+        LOGE("registerBuffers failed with status %d", ret);
+    }
+    return ret;
+}
+
 status_t CameraService::Client::startPreviewMode()
 {
-    LOGV("startPreviewMode");
+    LOGD("startPreviewMode (pid %d)", getCallingPid());
 
     // if preview has been enabled, nothing needs to be done
     if (mHardware->previewEnabled()) {
@@ -444,55 +581,24 @@ status_t CameraService::Client::startPreviewMode()
 #if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
     debug_frame_cnt = 0;
 #endif
-    status_t ret = UNKNOWN_ERROR;
-    int w, h;
-    CameraParameters params(mHardware->getParameters());
-    params.getPreviewSize(&w, &h);
+    status_t ret = NO_ERROR;
 
     if (mUseOverlay) {
-        const char *format = params.getPreviewFormat();
-        int fmt;
-        LOGD("Use Overlays");
-        if (!strcmp(format, "yuv422i"))
-            fmt = OVERLAY_FORMAT_YCbCr_422_I;
-        else if (!strcmp(format, "rgb565"))
-            fmt = OVERLAY_FORMAT_RGB_565;
-        else {
-            LOGE("Invalid preview format for overlays");
-            return -EINVAL;
-        }
-        sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
-        ret = mHardware->setOverlay(new Overlay(ref));
-        if (ret != NO_ERROR) {
-            LOGE("mHardware->setOverlay() failed with status %d\n", ret);
-            return ret;
+        // If preview display has been set, set overlay now.
+        if (mSurface != 0) {
+            ret = setOverlay();
         }
+        if (ret != NO_ERROR) return ret;
         ret = mHardware->startPreview(NULL, mCameraService.get());
-        if (ret != NO_ERROR)
-            LOGE("mHardware->startPreview() failed with status %d\n", ret);
-
     } else {
         ret = mHardware->startPreview(previewCallback,
                                       mCameraService.get());
-        if (ret == NO_ERROR) {
-
-            mSurface->unregisterBuffers();
-
-            uint32_t transform = 0;
-            if (params.getOrientation() ==
-                CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
-              LOGV("portrait mode");
-              transform = ISurface::BufferHeap::ROT_90;
-            }
-            ISurface::BufferHeap buffers(w, h, w, h,
-                                         PIXEL_FORMAT_YCbCr_420_SP,
-                                         transform,
-                                         0,
-                                         mHardware->getPreviewHeap());
-
-            mSurface->registerBuffers(buffers);
-        } else {
-          LOGE("mHardware->startPreview() failed with status %d", ret);
+        if (ret != NO_ERROR) return ret;
+        // If preview display has been set, register preview buffers now.
+        if (mSurface != 0) {
+           // Unregister here because the surface registered with raw heap.
+           mSurface->unregisterBuffers();
+           ret = registerPreviewBuffers();
         }
     }
     return ret;
@@ -500,11 +606,15 @@ status_t CameraService::Client::startPreviewMode()
 
 status_t CameraService::Client::startPreview()
 {
+    LOGD("startPreview (pid %d)", getCallingPid());
+    
     return startCameraMode(CAMERA_PREVIEW_MODE);
 }
 
 status_t CameraService::Client::startRecording()
 {
+    LOGD("startRecording (pid %d)", getCallingPid());
+
     if (mMediaPlayerBeep.get() != NULL) {
         mMediaPlayerBeep->seekTo(0);
         mMediaPlayerBeep->start();
@@ -515,7 +625,7 @@ status_t CameraService::Client::startRecording()
 // stop preview mode
 void CameraService::Client::stopPreview()
 {
-    LOGD("stopPreview()");
+    LOGD("stopPreview (pid %d)", getCallingPid());
 
     Mutex::Autolock lock(mLock);
     if (checkPid() != NO_ERROR) return;
@@ -537,7 +647,7 @@ void CameraService::Client::stopPreview()
 // stop recording mode
 void CameraService::Client::stopRecording()
 {
-    LOGV("stopRecording()");
+    LOGD("stopRecording (pid %d)", getCallingPid());
 
     Mutex::Autolock lock(mLock);
     if (checkPid() != NO_ERROR) return;
@@ -552,15 +662,13 @@ void CameraService::Client::stopRecording()
         mMediaPlayerBeep->start();
     }
     mHardware->stopRecording();
-    LOGV("stopRecording(), hardware stopped OK");
+    LOGD("stopRecording(), hardware stopped OK");
     mPreviewBuffer.clear();
 }
 
 // release a recording frame
 void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem)
 {
-    LOGV("releaseRecordingFrame()");
-
     Mutex::Autolock lock(mLock);
     if (checkPid() != NO_ERROR) return;
 
@@ -592,7 +700,7 @@ sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user)
     sp<Client> client = 0;
     CameraService *service = static_cast<CameraService*>(user);
     if (service != NULL) {
-        Mutex::Autolock ourLock(service->mLock);
+        Mutex::Autolock ourLock(service->mServiceLock);
         if (service->mClient != 0) {
             client = service->mClient.promote();
             if (client == 0) {
@@ -704,7 +812,7 @@ void CameraService::Client::recordingCallback(const sp<IMemory>& mem, void* user
 // take a picture - image is returned in callback
 status_t CameraService::Client::autoFocus()
 {
-    LOGV("autoFocus");
+    LOGD("autoFocus (pid %d)", getCallingPid());
 
     Mutex::Autolock lock(mLock);
     status_t result = checkPid();
@@ -722,7 +830,7 @@ status_t CameraService::Client::autoFocus()
 // take a picture - image is returned in callback
 status_t CameraService::Client::takePicture()
 {
-    LOGD("takePicture");
+    LOGD("takePicture (pid %d)", getCallingPid());
 
     Mutex::Autolock lock(mLock);
     status_t result = checkPid();
@@ -920,6 +1028,7 @@ void CameraService::Client::postAutoFocus(bool focused)
 
 void CameraService::Client::postShutter()
 {
+    LOGD("postShutter");
     mCameraClient->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
 }
 
@@ -1029,12 +1138,12 @@ status_t CameraService::dump(int fd, const Vector<String16>& args)
     if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
         snprintf(buffer, SIZE, "Permission Denial: "
                 "can't dump CameraService from pid=%d, uid=%d\n",
-                IPCThreadState::self()->getCallingPid(),
+                getCallingPid(),
                 IPCThreadState::self()->getCallingUid());
         result.append(buffer);
         write(fd, result.string(), result.size());
     } else {
-        AutoMutex lock(&mLock);
+        AutoMutex lock(&mServiceLock);
         if (mClient != 0) {
             sp<Client> currentClient = mClient.promote();
             sprintf(buffer, "Client (%p) PID: %d\n",
@@ -1052,8 +1161,6 @@ status_t CameraService::dump(int fd, const Vector<String16>& args)
 }
 
 
-#if DEBUG_HEAP_LEAKS
-
 #define CHECK_INTERFACE(interface, data, reply) \
         do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
             LOGW("Call incorrectly routed to " #interface); \
@@ -1085,6 +1192,7 @@ status_t CameraService::onTransact(
 
     status_t err = BnCameraService::onTransact(code, data, reply, flags);
 
+#if DEBUG_HEAP_LEAKS
     LOGD("+++ onTransact err %d code %d", err, code);
 
     if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
@@ -1120,9 +1228,9 @@ status_t CameraService::onTransact(
             break;
         }
     }
+#endif // DEBUG_HEAP_LEAKS
+
     return err;
 }
 
-#endif // DEBUG_HEAP_LEAKS
-
 }; // namespace android
index 6752f26..0f07673 100644 (file)
@@ -58,10 +58,8 @@ public:
 
             void            removeClient(const sp<ICameraClient>& cameraClient);
 
-#if DEBUG_HEAP_LEAKS
     virtual status_t onTransact(
         uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
-#endif
 
 private:
 
@@ -159,6 +157,8 @@ private:
         status_t                startCameraMode(camera_mode mode);
         status_t                startPreviewMode();
         status_t                startRecordingMode();
+        status_t                setOverlay();
+        status_t                registerPreviewBuffers();
 
         // Ensures atomicity among the public methods
         mutable     Mutex                       mLock;
@@ -196,7 +196,12 @@ private:
                             CameraService();
     virtual                 ~CameraService();
 
-    mutable     Mutex                       mLock;
+    // We use a count for number of clients (shoule only be 0 or 1).
+    volatile    int32_t                     mUsers;
+    virtual     void                        incUsers();
+    virtual     void                        decUsers();
+
+    mutable     Mutex                       mServiceLock;
                 wp<Client>                  mClient;
 
 #if DEBUG_HEAP_LEAKS
diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk
new file mode 100644 (file)
index 0000000..3daf44e
--- /dev/null
@@ -0,0 +1,22 @@
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    netkeystore.c keymgmt.c
+
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, system-core)/cutils \
+    external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils libssl
+
+LOCAL_STATIC_LIBRARIES :=
+
+LOCAL_MODULE:= keystore
+
+include $(BUILD_EXECUTABLE)
+
+endif # !simulator))
diff --git a/cmds/keystore/certtool.h b/cmds/keystore/certtool.h
new file mode 100644 (file)
index 0000000..aefad66
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+**
+** Copyright 2009, 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.
+*/
+
+#ifndef __CERTTOOL_H__
+#define __CERTTOOL_H__
+
+#include <stdio.h>
+#include <string.h>
+#include <cutils/sockets.h>
+#include <cutils/log.h>
+
+#include "common.h"
+#include "netkeystore.h"
+
+#define CERT_NAME_LEN (2 * MAX_KEY_NAME_LENGTH + 2)
+
+/*
+ * The specific function 'get_cert' is used in daemons to get the key value
+ * from keystore. Caller should allocate the buffer and the length of the buffer
+ * should be MAX_KEY_VALUE_LENGTH.
+ */
+static inline int get_cert(const char *certname, unsigned char *value, int *size)
+{
+    int count, fd, ret = -1;
+    LPC_MARSHAL cmd;
+    char delimiter[] = "_";
+    char *namespace, *keyname;
+    char *context = NULL;
+    char cname[CERT_NAME_LEN];
+
+    if ((certname == NULL) || (value == NULL)) {
+        LOGE("get_cert: certname or value is null\n");
+        return -1;
+    }
+
+    if (strlcpy(cname, certname, CERT_NAME_LEN) >= CERT_NAME_LEN) {
+        LOGE("get_cert: keyname is too long\n");
+        return -1;
+    }
+
+    fd = socket_local_client(SOCKET_PATH,
+                             ANDROID_SOCKET_NAMESPACE_RESERVED,
+                             SOCK_STREAM);
+    if (fd == -1) {
+        LOGE("Keystore service is not up and running.\n");
+        return -1;
+    }
+
+    cmd.opcode = GET;
+    if (((namespace = strtok_r(cname, delimiter, &context)) == NULL) ||
+        ((keyname = strtok_r(NULL, delimiter, &context)) == NULL)) {
+        goto err;
+    }
+    if ((cmd.len = snprintf((char*)cmd.data, BUFFER_MAX, "%s %s", namespace, keyname))
+        > (2 * MAX_KEY_NAME_LENGTH + 1)) goto err;
+
+    if (write_marshal(fd, &cmd)) {
+        LOGE("Incorrect command or command line is too long.\n");
+        goto err;
+    }
+    if (read_marshal(fd, &cmd)) {
+        LOGE("Failed to read the result.\n");
+        goto err;
+    }
+
+    // copy the result if succeeded.
+    if (!cmd.retcode && cmd.len <= BUFFER_MAX) {
+        memcpy(value, cmd.data, cmd.len);
+        ret = 0;
+        *size = cmd.len;
+    }
+err:
+    close(fd);
+    return ret;
+}
+
+#endif
diff --git a/cmds/keystore/common.h b/cmds/keystore/common.h
new file mode 100644 (file)
index 0000000..a18114e
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+**
+** Copyright 2009, 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.
+*/
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#define SOCKET_PATH             "keystore"
+#define KEYSTORE_DIR            "/data/misc/keystore/"
+
+#define READ_TIMEOUT            3
+#define MAX_KEY_NAME_LENGTH     64
+#define MAX_NAMESPACE_LENGTH    MAX_KEY_NAME_LENGTH
+#define MAX_KEY_VALUE_LENGTH    4096
+
+#define BUFFER_MAX              MAX_KEY_VALUE_LENGTH
+
+typedef enum {
+    BOOTUP,
+    UNINITIALIZED,
+    LOCKED,
+    UNLOCKED,
+} KEYSTORE_STATE;
+
+typedef enum {
+    LOCK,
+    UNLOCK,
+    PASSWD,
+    GETSTATE,
+    LISTKEYS,
+    GET,
+    PUT,
+    REMOVE,
+    RESET,
+    MAX_OPCODE
+} KEYSTORE_OPCODE;
+
+typedef struct {
+    uint32_t  len;
+    union {
+        uint32_t  opcode;
+        uint32_t  retcode;
+    };
+    unsigned char data[BUFFER_MAX + 1];
+} LPC_MARSHAL;
+
+#endif
diff --git a/cmds/keystore/keymgmt.c b/cmds/keystore/keymgmt.c
new file mode 100644 (file)
index 0000000..66edd56
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+** Copyright 2009, 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <cutils/log.h>
+
+#include "common.h"
+#include "keymgmt.h"
+
+static int  retry_count = 0;
+static unsigned char iv[IV_LEN];
+static KEYSTORE_STATE state = BOOTUP;
+static AES_KEY encryptKey, decryptKey;
+
+inline void unlock_keystore(unsigned char *master_key)
+{
+    AES_set_encrypt_key(master_key, AES_KEY_LEN, &encryptKey);
+    AES_set_decrypt_key(master_key, AES_KEY_LEN, &decryptKey);
+    memset(master_key, 0, sizeof(master_key));
+    state = UNLOCKED;
+}
+
+inline void lock_keystore()
+{
+    memset(&encryptKey, 0 , sizeof(AES_KEY));
+    memset(&decryptKey, 0 , sizeof(AES_KEY));
+    state = LOCKED;
+}
+
+inline void get_encrypt_key(char *passwd, AES_KEY *key)
+{
+    unsigned char user_key[USER_KEY_LEN];
+    gen_key(passwd, user_key, USER_KEY_LEN);
+    AES_set_encrypt_key(user_key, AES_KEY_LEN, key);
+}
+
+inline void get_decrypt_key(char *passwd, AES_KEY *key)
+{
+    unsigned char user_key[USER_KEY_LEN];
+    gen_key(passwd, user_key, USER_KEY_LEN);
+    AES_set_decrypt_key(user_key, AES_KEY_LEN, key);
+}
+
+static int gen_random_blob(unsigned char *key, int size)
+{
+    int ret = 0;
+    int fd = open("/dev/urandom", O_RDONLY);
+    if (fd == -1) return -1;
+    if (read(fd, key, size) != size) ret = -1;
+    close(fd);
+    return ret;
+}
+
+static int encrypt_n_save(AES_KEY *enc_key, DATA_BLOB *blob,
+                          const char *keyfile)
+{
+    int size, fd, ret = -1;
+    unsigned char enc_blob[MAX_BLOB_LEN];
+
+    char tmpfile[KEYFILE_LEN];
+    strcpy(tmpfile, keyfile);
+    strcat(tmpfile, ".tmp");
+
+    // prepare the blob
+    memcpy(blob->iv, iv, IV_LEN);
+    blob->blob_size = get_blob_size(blob);
+    memcpy(enc_blob, blob->blob, blob->blob_size);
+    AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char *)blob->blob,
+                    blob->blob_size, enc_key, iv, AES_ENCRYPT);
+    // write to keyfile
+    size = data_blob_size(blob);
+    if ((fd = open(tmpfile, O_CREAT|O_RDWR)) == -1) return -1;
+    if (write(fd, blob, size) == size) ret = 0;
+    close(fd);
+    if (!ret) {
+        unlink(keyfile);
+        rename(tmpfile, keyfile);
+        chmod(keyfile, 0440);
+    }
+    return ret;
+}
+
+static int load_n_decrypt(const char *keyname, const char *keyfile,
+                          AES_KEY *key, DATA_BLOB *blob)
+{
+    int fd, ret = -1;
+    if ((fd = open(keyfile, O_RDONLY)) == -1) return -1;
+    // get the encrypted blob and iv
+    if ((read(fd, blob->iv, sizeof(blob->iv)) != sizeof(blob->iv)) ||
+        (read(fd, &blob->blob_size, sizeof(uint32_t)) != sizeof(uint32_t)) ||
+        (blob->blob_size > MAX_BLOB_LEN)) {
+        goto err;
+    } else {
+        unsigned char enc_blob[MAX_BLOB_LEN];
+        if (read(fd, enc_blob, blob->blob_size) !=
+            (int) blob->blob_size) goto err;
+        // decrypt the blob
+        AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char*)blob->blob,
+                        blob->blob_size, key, blob->iv, AES_DECRYPT);
+        if (strcmp(keyname, (char*)blob->keyname) == 0) ret = 0;
+    }
+err:
+    close(fd);
+    return ret;
+}
+
+static int store_master_key(char *upasswd, unsigned char *master_key)
+{
+    AES_KEY key;
+    DATA_BLOB blob;
+
+    // prepare the blob
+    strlcpy(blob.keyname, MASTER_KEY_TAG, USER_KEY_LEN);
+    blob.value_size = USER_KEY_LEN;
+    memcpy((void*)blob.value, (const void*)master_key, USER_KEY_LEN);
+
+    // generate the encryption key
+    get_encrypt_key(upasswd, &key);
+    return encrypt_n_save(&key, &blob, MASTER_KEY);
+}
+
+static int get_master_key(char *upasswd, unsigned char *master_key)
+{
+    AES_KEY key;
+    int size, ret = 0;
+    DATA_BLOB blob;
+
+    get_decrypt_key(upasswd, &key);
+    ret = load_n_decrypt(MASTER_KEY_TAG, MASTER_KEY, &key, &blob);
+    if (!ret) memcpy(master_key, blob.value, blob.value_size);
+    return ret;
+}
+
+static int create_master_key(char *upasswd)
+{
+    int ret;
+    unsigned char mpasswd[AES_KEY_LEN];
+    unsigned char master_key[USER_KEY_LEN];
+
+    gen_random_blob(mpasswd, AES_KEY_LEN);
+    gen_key((char*)mpasswd, master_key, USER_KEY_LEN);
+    if ((ret = store_master_key(upasswd, master_key)) == 0) {
+        unlock_keystore(master_key);
+    }
+    memset(master_key, 0, USER_KEY_LEN);
+    memset(mpasswd, 0, AES_KEY_LEN);
+
+    return ret;
+}
+
+static int change_passwd(char *data)
+{
+    unsigned char master_key[USER_KEY_LEN];
+    char *old_pass, *new_pass = NULL, *p, *delimiter=" ";
+    int ret, count = 0;
+    char *context = NULL;
+
+    old_pass = p = strtok_r(data, delimiter, &context);
+    while (p != NULL) {
+        count++;
+        new_pass = p;
+        p = strtok_r(NULL, delimiter, &context);
+    }
+    if (count != 2) return -1;
+    if (strlen(new_pass) < MIN_PASSWD_LENGTH) return -1;
+    if ((ret = get_master_key(old_pass, master_key)) == 0) {
+        ret = store_master_key(new_pass, master_key);
+        retry_count = 0;
+    } else {
+        ret = MAX_RETRY_COUNT - ++retry_count;
+        if (ret == 0) {
+            retry_count = 0;
+            LOGE("passwd:reach max retry count, reset the keystore now.");
+            reset_keystore();
+            return -1;
+        }
+
+    }
+    return ret;
+}
+
+int remove_key(const char *namespace, const char *keyname)
+{
+    char keyfile[KEYFILE_LEN];
+
+    if (state != UNLOCKED) return -state;
+    sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
+    return unlink(keyfile);
+}
+
+int put_key(const char *namespace, const char *keyname,
+            unsigned char *data, int size)
+{
+    DATA_BLOB blob;
+    uint32_t  real_size;
+    char keyfile[KEYFILE_LEN];
+
+    if (state != UNLOCKED) {
+        LOGE("Can not store key with current state %d\n", state);
+        return -state;
+    }
+    sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
+    // flatten the args
+    strcpy(blob.keyname, keyname);
+    blob.value_size = size;
+    memcpy(blob.value, data, size);
+    return encrypt_n_save(&encryptKey, &blob, keyfile);
+}
+
+int get_key(const char *namespace, const char *keyname,
+            unsigned char *data, int *size)
+{
+    int ret;
+    DATA_BLOB blob;
+    uint32_t  blob_size;
+    char keyfile[KEYFILE_LEN];
+
+    if (state != UNLOCKED) {
+        LOGE("Can not retrieve key value with current state %d\n", state);
+        return -state;
+    }
+    sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
+    ret = load_n_decrypt(keyname, keyfile, &decryptKey, &blob);
+    if (!ret) {
+        if ((blob.value_size > MAX_KEY_VALUE_LENGTH)) {
+            ret = -1;
+        } else {
+            *size = blob.value_size;
+            memcpy(data, blob.value, *size);
+        }
+    }
+    return ret;
+}
+
+int list_keys(const char *namespace, char reply[BUFFER_MAX])
+{
+    DIR *d;
+    struct dirent *de;
+
+    if (state != UNLOCKED) {
+        LOGE("Can not list key with current state %d\n", state);
+        return -1;
+    }
+
+    if (!namespace || ((d = opendir("."))) == NULL) {
+        LOGE("cannot open keystore dir or namespace is null\n");
+        return -1;
+    }
+    while ((de = readdir(d))) {
+        char *prefix, *name, *keyfile = de->d_name;
+        char *context = NULL;
+
+        if (de->d_type != DT_REG) continue;
+        if ((prefix = strtok_r(keyfile, NAME_DELIMITER, &context))
+            == NULL) continue;
+        if (strcmp(prefix, namespace)) continue;
+        if ((name = strtok_r(NULL, NAME_DELIMITER, &context)) == NULL) continue;
+        // append the key name into reply
+        if (reply[0] != 0) strlcat(reply, " ", BUFFER_MAX);
+        if (strlcat(reply, name, BUFFER_MAX) >= BUFFER_MAX) {
+            LOGE("too many files under keystore directory\n");
+            return -1;
+        }
+    }
+    closedir(d);
+    return 0;
+}
+
+int passwd(char *data)
+{
+    if (state == UNINITIALIZED) {
+        if (strchr(data, ' ')) return -1;
+        if (strlen(data) < MIN_PASSWD_LENGTH) return -1;
+        return create_master_key(data);
+    }
+    return change_passwd(data);
+}
+
+int lock()
+{
+    switch(state) {
+        case UNLOCKED:
+            lock_keystore();
+        case LOCKED:
+            return 0;
+        default:
+            return -1;
+    }
+}
+
+int unlock(char *passwd)
+{
+    unsigned char master_key[USER_KEY_LEN];
+    int ret = get_master_key(passwd, master_key);
+    if (!ret) {
+        unlock_keystore(master_key);
+        retry_count = 0;
+    } else {
+        ret = MAX_RETRY_COUNT - ++retry_count;
+        if (ret == 0) {
+            retry_count = 0;
+            LOGE("unlock:reach max retry count, reset the keystore now.");
+            reset_keystore();
+            return -1;
+        }
+    }
+    return ret;
+}
+
+KEYSTORE_STATE get_state()
+{
+    return state;
+}
+
+int reset_keystore()
+{
+    DIR *d;
+    struct dirent *de;
+
+    if ((d = opendir(".")) == NULL) {
+        LOGE("cannot open keystore dir\n");
+        return -1;
+    }
+    while ((de = readdir(d))) unlink(de->d_name);
+    closedir(d);
+    state = UNINITIALIZED;
+    LOGI("keystore is reset.");
+    return 0;
+}
+
+int init_keystore(const char *dir)
+{
+    int fd;
+
+    if (!dir) mkdir(dir, 0770);
+    if (!dir || chdir(dir)) {
+        LOGE("Can not open/create the keystore directory %s\n",
+             dir ? dir : "(null)");
+        return -1;
+    }
+    gen_random_blob(iv, IV_LEN);
+    if ((fd = open(MASTER_KEY, O_RDONLY)) == -1) {
+        state = UNINITIALIZED;
+        return 0;
+    }
+    close(fd);
+    state = LOCKED;
+    return 0;
+}
diff --git a/cmds/keystore/keymgmt.h b/cmds/keystore/keymgmt.h
new file mode 100644 (file)
index 0000000..0e928db
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+** Copyright 2009, 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.
+*/
+
+#ifndef __KEYMGMT_H__
+#define __KEYMGMT_H__
+
+#define MASTER_KEY_TAG  "master_key"
+#define MASTER_KEY      ".keymaster"
+#define MAX_PATH_LEN    128
+#define SALT            "Android Keystore 0.1"
+#define NAME_DELIMITER  "_"
+#define KEYFILE_NAME    "%s"NAME_DELIMITER"%s"
+#define KEYGEN_ITER     1024
+#define AES_KEY_LEN     128
+#define USER_KEY_LEN    (AES_KEY_LEN/8)
+#define IV_LEN          USER_KEY_LEN
+#define MAX_RETRY_COUNT   6
+#define MIN_PASSWD_LENGTH 8
+
+#define gen_key(passwd, key, len) \
+                PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), \
+                                       (unsigned char*)SALT, \
+                                       strlen(SALT), KEYGEN_ITER, \
+                                       len, key)
+
+#define KEYFILE_LEN MAX_NAMESPACE_LENGTH + MAX_KEY_NAME_LENGTH + 6
+
+#define get_blob_size(blob) \
+        (((blob->value_size + sizeof(uint32_t) + MAX_KEY_NAME_LENGTH \
+        + USER_KEY_LEN - 1) / USER_KEY_LEN) * USER_KEY_LEN)
+
+#define MAX_BLOB_LEN    ((MAX_KEY_VALUE_LENGTH + MAX_KEY_NAME_LENGTH + \
+                         sizeof(uint32_t) + USER_KEY_LEN - 1) / USER_KEY_LEN)\
+                         * USER_KEY_LEN
+
+#define data_blob_size(blob) USER_KEY_LEN + sizeof(uint32_t) + blob->blob_size
+
+typedef struct {
+    unsigned char iv[USER_KEY_LEN];
+    uint32_t blob_size;
+    union {
+        unsigned char blob[1];
+        struct {
+            uint32_t value_size;
+            char keyname[MAX_KEY_NAME_LENGTH];
+            unsigned char value[MAX_KEY_VALUE_LENGTH];
+        } __attribute__((packed));
+    };
+} DATA_BLOB;
+
+typedef struct {
+    char tag[USER_KEY_LEN];
+    unsigned char master_key[USER_KEY_LEN];
+} MASTER_BLOB;
+
+int put_key(const char *namespace, const char *keyname,
+            unsigned char *data, int size);
+int get_key(const char *namespace, const char *keyname,
+            unsigned char *data, int *size);
+int remove_key(const char *namespace, const char *keyname);
+int list_keys(const char *namespace, char reply[BUFFER_MAX]);
+int passwd(char *data);
+int lock();
+int unlock(char *passwd);
+KEYSTORE_STATE get_state();
+int reset_keystore();
+int init_keystore(const char *dir);
+
+#endif
diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h
new file mode 100644 (file)
index 0000000..a7fd9a5
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+**
+** Copyright 2009, 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.
+*/
+
+#ifndef __KEYSTORE_GET_H__
+#define __KEYSTORE_GET_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "certtool.h"
+
+/* This function is provided to native components to get values from keystore.
+ * Users are required to link against libcutils. If something goes wrong, NULL
+ * is returned. Otherwise it returns the value in dynamically allocated memory
+ * and sets the size if the pointer is not NULL. One can release the memory by
+ * calling free(). */
+static char *keystore_get(char *key, int *size)
+{
+    char buffer[MAX_KEY_VALUE_LENGTH];
+    char *value;
+    int length;
+
+    if (get_cert(key, (unsigned char *)buffer, &length) != 0) {
+        return NULL;
+    }
+    value = malloc(length + 1);
+    if (!value) {
+        return NULL;
+    }
+    memcpy(value, buffer, length);
+    value[length] = 0;
+    if (size) {
+        *size = length;
+    }
+    return value;
+}
+
+#endif
diff --git a/cmds/keystore/netkeystore.c b/cmds/keystore/netkeystore.c
new file mode 100644 (file)
index 0000000..eac455e
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+** Copyright 2009, 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.
+*/
+
+#define LOG_TAG "keystore"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <private/android_filesystem_config.h>
+
+#include <cutils/sockets.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "netkeystore.h"
+#include "keymgmt.h"
+
+#define  CMD_PUT_WITH_FILE  "putfile"
+
+typedef void CMD_FUNC(LPC_MARSHAL *cmd, LPC_MARSHAL *reply);
+
+struct cmdinfo {
+    const char *name;
+    CMD_FUNC *func;
+};
+
+static CMD_FUNC do_lock;
+static CMD_FUNC do_unlock;
+static CMD_FUNC do_passwd;
+static CMD_FUNC do_get_state;;
+static CMD_FUNC do_listkeys;
+static CMD_FUNC do_get_key;
+static CMD_FUNC do_put_key;
+static CMD_FUNC do_remove_key;
+static CMD_FUNC do_reset_keystore;
+
+#define str(x)      #x
+
+struct cmdinfo cmds[] = {
+    { str(LOCK),           do_lock },
+    { str(UNLOCK),         do_unlock },
+    { str(PASSWD),         do_passwd },
+    { str(GETSTATE),       do_get_state },
+    { str(LISTKEYS),       do_listkeys },
+    { str(GET),            do_get_key },
+    { str(PUT),            do_put_key },
+    { str(REMOVE),         do_remove_key },
+    { str(RESET),          do_reset_keystore },
+};
+
+static  struct ucred cr;
+
+static int check_get_perm(int uid)
+{
+    if (uid == AID_WIFI || uid == AID_VPN) return 0;
+    return -1;
+}
+
+static int check_reset_perm(int uid)
+{
+    if (uid == AID_SYSTEM) return 0;
+    return -1;
+}
+
+static int parse_keyname(char *name, uint32_t len,
+                         char *namespace, char *keyname)
+{
+    int count = 0;
+    char *c = namespace, *p = namespace, *t = name;
+
+    if (!name || !namespace || !keyname) return -1;
+    while (t < name + len && (*t != 0)) {
+        if (*t == ' ') {
+            if (c == keyname) return -1;
+            *p = count = 0;
+            c = p = keyname;
+            t++;
+        } else {
+            if (!isalnum(*t)) return -1;
+            *p++ = *t++;
+            // also check if the keyname/namespace is too long.
+            if (count++ == MAX_KEY_NAME_LENGTH) return -1;
+        }
+    }
+    *p = 0;
+    return 0;
+}
+
+// args of passwd():
+// firstPassword - for the first time
+// oldPassword newPassword - for changing the password
+static void do_passwd(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = passwd((char*)cmd->data);
+}
+
+// args of lock():
+// no argument
+static void do_lock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = lock();
+}
+
+// args of unlock():
+// password
+static void do_unlock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = unlock((char*)cmd->data);
+}
+
+// args of get_state():
+// no argument
+static void do_get_state(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = get_state();
+}
+
+// args of listkeys():
+// namespace
+static void do_listkeys(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = list_keys((const char*)cmd->data, (char*)reply->data);
+    if (!reply->retcode) reply->len = strlen((char*)reply->data);
+}
+
+// args of get():
+// namespace keyname
+static void do_get_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    char namespace[MAX_KEY_NAME_LENGTH];
+    char keyname[MAX_KEY_NAME_LENGTH];
+
+    if (check_get_perm(cr.uid)) {
+        LOGE("uid %d doesn't have the permission to get key value\n", cr.uid);
+        reply->retcode = -1;
+        return;
+    }
+
+    if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) {
+        reply->retcode = -1;
+    } else {
+        reply->retcode = get_key(namespace, keyname, reply->data,
+                                 (int*)&reply->len);
+    }
+}
+
+static int get_value_index(LPC_MARSHAL *cmd)
+{
+    uint32_t count = 0, i;
+    for (i = 0 ; i < cmd->len ; ++i) {
+        if (cmd->data[i] == ' ') {
+            if (++count == 2) return ++i;
+        }
+    }
+    return -1;
+}
+
+// args of put():
+// namespace keyname keyvalue
+static void do_put_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    char namespace[MAX_KEY_NAME_LENGTH];
+    char keyname[MAX_KEY_NAME_LENGTH];
+
+    int p = get_value_index(cmd);
+    if (p == -1) {
+        reply->retcode = -1;
+    } else {
+        unsigned char *value;
+        if (parse_keyname((char*)cmd->data, p - 1, namespace, keyname)) {
+            reply->retcode = -1;
+            return;
+        }
+        value = &cmd->data[p];
+        int len = cmd->len - p;
+        reply->retcode = put_key(namespace, keyname, value, len);
+    }
+}
+
+// args of remove_key():
+// namespace keyname
+static void do_remove_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    char namespace[MAX_KEY_NAME_LENGTH];
+    char keyname[MAX_KEY_NAME_LENGTH];
+    if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) {
+        reply->retcode = -1;
+        return;
+    }
+    reply->retcode = remove_key(namespace, keyname);
+}
+
+// args of reset_keystore():
+// no argument
+static void do_reset_keystore(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    if (check_reset_perm(cr.uid)) {
+        LOGE("uid %d doesn't have the permission to reset the keystore\n",
+             cr.uid);
+        reply->retcode = -1;
+        return;
+    }
+    reply->retcode = reset_keystore();
+}
+
+static void execute(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    uint32_t cmd_max = sizeof(cmds)/sizeof(struct cmdinfo);
+
+    if (cmd->opcode >= cmd_max) {
+        LOGE("the opcode (%d) is not valid", cmd->opcode);
+        reply->retcode = -1;
+        return;
+    }
+    cmds[cmd->opcode].func(cmd, reply);
+}
+
+static int set_read_timeout(int socket)
+{
+    struct timeval tv;
+    tv.tv_sec = READ_TIMEOUT;
+    if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,  sizeof tv))
+    {
+        LOGE("setsockopt failed");
+        return -1;
+    }
+    return 0;
+}
+
+static int append_input_from_file(const char *filename, LPC_MARSHAL *cmd)
+{
+    int fd, len, ret = 0;
+
+    // get opcode of the function put()
+    if ((fd = open(filename, O_RDONLY)) == -1) {
+        fprintf(stderr, "Can not open file %s\n", filename);
+        return -1;
+    }
+    cmd->data[cmd->len] = ' ';
+    cmd->len++;
+    len = read(fd, cmd->data + cmd->len, BUFFER_MAX - cmd->len);
+    if (len < 0 || (len == (int)(BUFFER_MAX - cmd->len))) {
+        ret = -1;
+    } else {
+        cmd->len += len;
+    }
+    close(fd);
+    return ret;
+}
+
+static int flatten_str_args(int argc, const char **argv, LPC_MARSHAL *cmd)
+{
+    int i, len = 0;
+    char *buf = (char*)cmd->data;
+    buf[0] = 0;
+    for (i = 0 ; i < argc ; ++i) {
+        if (i == 0) {
+            len = strlcpy(buf, argv[i], BUFFER_MAX);
+        } else {
+            len += snprintf(buf + len, BUFFER_MAX - len, " %s", argv[i]);
+        }
+        if (len >= BUFFER_MAX) return -1;
+    }
+    if (len) cmd->len = len;
+    return 0;
+}
+
+static int parse_cmd(int argc, const char **argv, LPC_MARSHAL *cmd)
+{
+    uint32_t i, len = 0;
+    uint32_t cmd_max = sizeof(cmds)/sizeof(cmds[0]);
+
+    for (i = 0 ; i < cmd_max ; ++i) {
+        if (!strcasecmp(argv[0], cmds[i].name)) break;
+    }
+
+    if (i == cmd_max) {
+        // check if this is a command to put the key value with a file.
+        if (strcmp(argv[0], CMD_PUT_WITH_FILE) != 0) return -1;
+        cmd->opcode = PUT;
+        if (argc != 4) {
+            fprintf(stderr, "%s args\n\tnamespace keyname filename\n",
+                    argv[0]);
+            return -1;
+        }
+        if (flatten_str_args(argc - 2, argv + 1, cmd)) return -1;
+        return append_input_from_file(argv[3], cmd);
+    } else {
+        cmd->opcode = i;
+        return flatten_str_args(argc - 1, argv + 1, cmd);
+    }
+}
+
+static int shell_command(const int argc, const char **argv)
+{
+    int fd, i;
+    LPC_MARSHAL  cmd;
+
+    if (parse_cmd(argc, argv , &cmd)) {
+        fprintf(stderr, "Incorrect command or command line is too long.\n");
+        exit(1);
+    }
+    fd = socket_local_client(SOCKET_PATH,
+                             ANDROID_SOCKET_NAMESPACE_RESERVED,
+                             SOCK_STREAM);
+    if (fd == -1) {
+        fprintf(stderr, "Keystore service is not up and running.\n");
+        exit(1);
+    }
+
+    if (write_marshal(fd, &cmd)) {
+        fprintf(stderr, "Incorrect command or command line is too long.\n");
+        exit(1);
+    }
+    if (read_marshal(fd, &cmd)) {
+        fprintf(stderr, "Failed to read the result.\n");
+        exit(1);
+    }
+    cmd.data[cmd.len] = 0;
+    fprintf(stdout, "%s\n", (cmd.retcode == 0) ? "Succeeded!" : "Failed!");
+    if (cmd.len) fprintf(stdout, "\t%s\n", (char*)cmd.data);
+    close(fd);
+    return 0;
+}
+
+int main(const int argc, const char *argv[])
+{
+    struct sockaddr addr;
+    socklen_t alen;
+    int lsocket, s;
+    LPC_MARSHAL  cmd, reply;
+
+    if (argc > 1) {
+        return shell_command(argc - 1, argv + 1);
+    }
+
+    if (init_keystore(KEYSTORE_DIR)) {
+        LOGE("Can not initialize the keystore, the directory exist?\n");
+        exit(1);
+    }
+
+    lsocket = android_get_control_socket(SOCKET_PATH);
+    if (lsocket < 0) {
+        LOGE("Failed to get socket from environment: %s\n", strerror(errno));
+        exit(1);
+    }
+    if (listen(lsocket, 5)) {
+        LOGE("Listen on socket failed: %s\n", strerror(errno));
+        exit(1);
+    }
+    fcntl(lsocket, F_SETFD, FD_CLOEXEC);
+    memset(&reply, 0, sizeof(LPC_MARSHAL));
+
+    for (;;) {
+        socklen_t cr_size = sizeof(cr);
+        alen = sizeof(addr);
+        s = accept(lsocket, &addr, &alen);
+
+        /* retrieve the caller info here */
+        if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
+            close(s);
+            LOGE("Unable to recieve socket options\n");
+            continue;
+        }
+
+        if (s < 0) {
+            LOGE("Accept failed: %s\n", strerror(errno));
+            continue;
+        }
+        fcntl(s, F_SETFD, FD_CLOEXEC);
+        if (set_read_timeout(s)) {
+            close(s);
+            continue;
+        }
+
+        // read the command, execute and send the result back.
+        if(read_marshal(s, &cmd)) goto err;
+        LOGI("new connection\n");
+        execute(&cmd, &reply);
+        write_marshal(s, &reply);
+err:
+        memset(&reply, 0, sizeof(LPC_MARSHAL));
+        LOGI("closing connection\n");
+        close(s);
+    }
+
+    return 0;
+}
diff --git a/cmds/keystore/netkeystore.h b/cmds/keystore/netkeystore.h
new file mode 100644 (file)
index 0000000..a87a667
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+**
+** Copyright 2009, 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.
+*/
+
+#ifndef __NETKEYSTORE_H__
+#define __NETKEYSTORE_H__
+
+#include <stdio.h>
+#include <cutils/sockets.h>
+#include <cutils/log.h>
+
+#include "common.h"
+
+static inline int readx(int s, void *_buf, int count)
+{
+    char *buf = _buf;
+    int n = 0, r;
+    if (count < 0) return -1;
+    while (n < count) {
+        r = read(s, buf + n, count - n);
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            LOGE("read error: %s\n", strerror(errno));
+            return -1;
+        }
+        if (r == 0) {
+            LOGE("eof\n");
+            return -1; /* EOF */
+        }
+        n += r;
+    }
+    return 0;
+}
+
+static inline int writex(int s, const void *_buf, int count)
+{
+    const char *buf = _buf;
+    int n = 0, r;
+    if (count < 0) return -1;
+    while (n < count) {
+        r = write(s, buf + n, count - n);
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            LOGE("write error: %s\n", strerror(errno));
+            return -1;
+        }
+        n += r;
+    }
+    return 0;
+}
+
+static inline int read_marshal(int s, LPC_MARSHAL *cmd)
+{
+    if (readx(s, cmd, 2 * sizeof(uint32_t))) {
+        LOGE("failed to read header\n");
+        return -1;
+    }
+    if (cmd->len > BUFFER_MAX) {
+        LOGE("invalid size %d\n", cmd->len);
+        return -1;
+    }
+    if (readx(s, cmd->data, cmd->len)) {
+        LOGE("failed to read data\n");
+        return -1;
+    }
+    cmd->data[cmd->len] = 0;
+    return 0;
+}
+
+static inline int write_marshal(int s, LPC_MARSHAL *cmd)
+{
+    if (writex(s, cmd, 2 * sizeof(uint32_t))) {
+        LOGE("failed to write marshal header\n");
+        return -1;
+    }
+    if (writex(s, cmd->data, cmd->len)) {
+        LOGE("failed to write marshal data\n");
+        return -1;
+    }
+    return 0;
+}
+
+#endif
index 1531a9e..476f38a 100644 (file)
@@ -45,9 +45,9 @@ static const char* ZYGOTE_ARGV[] = {
     "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
     /* CAP_SYS_TTY_CONFIG & CAP_SYS_RESOURCE & CAP_NET_BROADCAST &
      * CAP_NET_ADMIN & CAP_NET_RAW & CAP_NET_BIND_SERVICE  & CAP_KILL &
-     * CAP_SYS_BOOT
+     * CAP_SYS_BOOT CAP_SYS_NICE
      */
-    "--capabilities=88161312,88161312",
+    "--capabilities=96549920,96549920",
     "--runtime-init",
     "--nice-name=system_server",
     "com.android.server.SystemServer"
index 0c7ad46..a85f275 100644 (file)
@@ -456,7 +456,7 @@ struct matrix_stack_t {
     void validate();
     matrixf_t& top() { return stack[depth]; }
     const matrixf_t& top() const { return stack[depth]; }
-    const uint32_t top_ops() const { return ops[depth]; }
+    uint32_t top_ops() const { return ops[depth]; }
     inline bool isRigidBody() const {
         return !(ops[depth] & ~(OP_TRANSLATE|OP_UNIFORM_SCALE|OP_ROTATE));
     }
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
new file mode 100644 (file)
index 0000000..21cb73b
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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 <media/AudioSystem.h>
+
+// This header defines the interface used by the Android platform
+// to access Text-To-Speech functionality in shared libraries that implement
+// speech synthesis and the management of resources associated with the
+// synthesis.
+// An example of the implementation of this interface can be found in
+// FIXME: add path+name to implementation of default TTS engine
+// Libraries implementing this interface are used in:
+//  frameworks/base/tts/jni/android_tts_SpeechSynthesis.cpp
+
+namespace android {
+
+enum tts_synth_status {
+    TTS_SYNTH_DONE              = 0,
+    TTS_SYNTH_PENDING           = 1
+};
+
+enum tts_callback_status {
+    TTS_CALLBACK_HALT           = 0,
+    TTS_CALLBACK_CONTINUE       = 1
+};
+
+// The callback is used by the implementation of this interface to notify its
+// client, the Android TTS service, that the last requested synthesis has been
+// completed. // TODO reword
+// The callback for synthesis completed takes:
+// @param [inout] void *&       - The userdata pointer set in the original
+//                                 synth call
+// @param [in]    uint32_t      - Track sampling rate in Hz
+// @param [in]    audio_format  - The AudioSystem::audio_format enum
+// @param [in]    int           - The number of channels
+// @param [inout] int8_t *&     - A buffer of audio data only valid during the
+//                                execution of the callback
+// @param [inout] size_t  &     - The size of the buffer
+// @param [in] tts_synth_status - indicate whether the synthesis is done, or
+//                                 if more data is to be synthesized.
+// @return TTS_CALLBACK_HALT to indicate the synthesis must stop,
+//         TTS_CALLBACK_CONTINUE to indicate the synthesis must continue if
+//            there is more data to produce.
+typedef tts_callback_status (synthDoneCB_t)(void *&, uint32_t,
+        AudioSystem::audio_format, int, int8_t *&, size_t&, tts_synth_status);
+
+class TtsEngine;
+extern "C" TtsEngine* getTtsEngine();
+
+enum tts_result {
+    TTS_SUCCESS                 = 0,
+    TTS_FAILURE                 = -1,
+    TTS_FEATURE_UNSUPPORTED     = -2,
+    TTS_VALUE_INVALID           = -3,
+    TTS_PROPERTY_UNSUPPORTED    = -4,
+    TTS_PROPERTY_SIZE_TOO_SMALL = -5,
+    TTS_MISSING_RESOURCES       = -6
+};
+
+enum tts_support_result {
+    TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
+    TTS_LANG_COUNTRY_AVAILABLE = 1,
+    TTS_LANG_AVAILABLE = 0,
+    TTS_LANG_MISSING_DATA = -1,
+    TTS_LANG_NOT_SUPPORTED = -2
+};
+
+class TtsEngine
+{
+public:
+    // Initialize the TTS engine and returns whether initialization succeeded.
+    // @param synthDoneCBPtr synthesis callback function pointer
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result init(synthDoneCB_t synthDoneCBPtr);
+
+    // Shut down the TTS engine and releases all associated resources.
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result shutdown();
+
+    // Interrupt synthesis and flushes any synthesized data that hasn't been
+    // output yet. This will block until callbacks underway are completed.
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result stop();
+
+    // Returns the level of support for the language, country and variant.
+    // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
+    //            and the corresponding resources are correctly installed
+    //         TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
+    //             corresponding resources are correctly installed, but there is no match for
+    //             the specified variant
+    //         TTS_LANG_AVAILABLE if the language is supported and the
+    //             corresponding resources are correctly installed, but there is no match for
+    //             the specified country and variant
+    //         TTS_LANG_MISSING_DATA if the required resources to provide any level of support
+    //             for the language are not correctly installed
+    //         TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
+    virtual tts_support_result isLanguageAvailable(const char *lang, const char *country,
+            const char *variant);
+
+    // Load the resources associated with the specified language. The loaded
+    // language will only be used once a call to setLanguage() with the same
+    // language value is issued. Language and country values are coded according to the ISO three
+    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
+    // instance. The variant value is encoded as the variant string retrieved from a
+    // java.util.Locale instance built with that variant data.
+    // @param lang pointer to the ISO three letter code for the language
+    // @param country pointer to the ISO three letter code for the country
+    // @param variant pointer to the variant code
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result loadLanguage(const char *lang, const char *country, const char *variant);
+    
+    // Load the resources associated with the specified language, country and Locale variant.
+    // The loaded language will only be used once a call to setLanguageFromLocale() with the same
+    // language value is issued. Language and country values are coded according to the ISO three
+    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
+    // instance. The variant value is encoded as the variant string retrieved from a
+    // java.util.Locale instance built with that variant data.
+    // @param lang pointer to the ISO three letter code for the language
+    // @param country pointer to the ISO three letter code for the country
+    // @param variant pointer to the variant code
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result setLanguage(const char *lang, const char *country, const char *variant);
+
+    // Retrieve the currently set language, country and variant, or empty strings if none of
+    // parameters have been set. Language and country are represented by their 3-letter ISO code
+    // @param[out]   pointer to the retrieved 3-letter code language value
+    // @param[out]   pointer to the retrieved 3-letter code country value
+    // @param[out]   pointer to the retrieved variant value
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result getLanguage(char *language, char *country, char *variant);
+
+    // Notifies the engine what audio parameters should be used for the synthesis.
+    // This is meant to be used as a hint, the engine implementation will set the output values
+    // to those of the synthesis format, based on a given hint.
+    // @param[inout] encoding in: the desired audio sample format
+    //                         out: the format used by the TTS engine
+    // @param[inout] rate in: the desired audio sample rate
+    //                         out: the sample rate used by the TTS engine
+    // @param[inout] channels in: the desired number of audio channels
+    //                         out: the number of channels used by the TTS engine
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate,
+            int& channels);
+
+    // Set a property for the the TTS engine
+    // "size" is the maximum size of "value" for properties "property"
+    // @param property pointer to the property name
+    // @param value    pointer to the property value
+    // @param size     maximum size required to store this type of property
+    // @return         TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, or TTS_FAILURE,
+    //                  or TTS_VALUE_INVALID
+    virtual tts_result setProperty(const char *property, const char *value,
+            const size_t size);
+
+    // Retrieve a property from the TTS engine
+    // @param        property pointer to the property name
+    // @param[out]   value    pointer to the retrieved language value
+    // @param[inout] iosize   in: stores the size available to store the
+    //                          property value.
+    //                        out: stores the size required to hold the language
+    //                          value if getLanguage() returned
+    //                          TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise
+    // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS,
+    //         or TTS_PROPERTY_SIZE_TOO_SMALL
+    virtual tts_result getProperty(const char *property, char *value,
+            size_t *iosize);
+
+    // Synthesize the text.
+    // As the synthesis is performed, the engine invokes the callback to notify
+    // the TTS framework that it has filled the given buffer, and indicates how
+    // many bytes it wrote. The callback is called repeatedly until the engine
+    // has generated all the audio data corresponding to the text.
+    // Note about the format of the input: the text parameter may use the
+    // following elements
+    // and their respective attributes as defined in the SSML 1.0 specification:
+    //    * lang
+    //    * say-as:
+    //          o interpret-as
+    //    * phoneme
+    //    * voice:
+    //          o gender,
+    //          o age,
+    //          o variant,
+    //          o name
+    //    * emphasis
+    //    * break:
+    //          o strength,
+    //          o time
+    //    * prosody:
+    //          o pitch,
+    //          o contour,
+    //          o range,
+    //          o rate,
+    //          o duration,
+    //          o volume
+    //    * mark
+    // Differences between this text format and SSML are:
+    //    * full SSML documents are not supported
+    //    * namespaces are not supported
+    // Text is coded in UTF-8.
+    // @param text      the UTF-8 text to synthesize
+    // @param userdata  pointer to be returned when the call is invoked
+    // @param buffer    the location where the synthesized data must be written
+    // @param bufferSize the number of bytes that can be written in buffer
+    // @return          TTS_SUCCESS or TTS_FAILURE
+    virtual tts_result synthesizeText(const char *text, int8_t *buffer,
+            size_t bufferSize, void *userdata);
+
+    // Synthesize IPA text.
+    // As the synthesis is performed, the engine invokes the callback to notify
+    // the TTS framework that it has filled the given buffer, and indicates how
+    // many bytes it wrote. The callback is called repeatedly until the engine
+    // has generated all the audio data corresponding to the IPA data.
+    // @param ipa      the IPA data to synthesize
+    // @param userdata  pointer to be returned when the call is invoked
+    // @param buffer    the location where the synthesized data must be written
+    // @param bufferSize the number of bytes that can be written in buffer
+    // @return TTS_FEATURE_UNSUPPORTED if IPA is not supported,
+    //         otherwise TTS_SUCCESS or TTS_FAILURE
+    virtual tts_result synthesizeIpa(const char *ipa, int8_t *buffer,
+            size_t bufferSize, void *userdata);
+};
+
+} // namespace android
+
index 048bdd5..e3544ab 100644 (file)
@@ -63,16 +63,12 @@ namespace android {
 #define FRAME_CALLBACK_FLAG_CAMERA                   0x05
 #define FRAME_CALLBACK_FLAG_BARCODE_SCANNER          0x07
 
-// msgType in notifyCallback function
+// msgType in notifyCallback and dataCallback functions
 enum {
-    CAMERA_MSG_ERROR,
+    CAMERA_MSG_ERROR = 0,
     CAMERA_MSG_SHUTTER,
     CAMERA_MSG_FOCUS,
-    CAMERA_MSG_ZOOM
-};
-
-// msgType in dataCallback function
-enum {
+    CAMERA_MSG_ZOOM,
     CAMERA_MSG_PREVIEW_FRAME,
     CAMERA_MSG_VIDEO_FRAME,
     CAMERA_MSG_POSTVIEW_FRAME,
@@ -80,16 +76,25 @@ enum {
     CAMERA_MSG_COMPRESSED_IMAGE
 };
 
+// camera fatal errors
+enum {
+    CAMERA_ERROR_UKNOWN  = 1,
+    CAMERA_ERROR_SERVER_DIED = 100
+};
+
 class ICameraService;
 class ICamera;
 class Surface;
 class Mutex;
 class String8;
 
-typedef void (*shutter_callback)(void *cookie);
-typedef void (*frame_callback)(const sp<IMemory>& mem, void *cookie);
-typedef void (*autofocus_callback)(bool focused, void *cookie);
-typedef void (*error_callback)(status_t err, void *cookie);
+// ref-counted object for callbacks
+class CameraListener: virtual public RefBase
+{
+public:
+    virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
+    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr) = 0;
+};
 
 class Camera : public BnCameraClient, public IBinder::DeathRecipient
 {
@@ -144,13 +149,8 @@ public:
             // get preview/capture parameters - key/value pairs
             String8     getParameters() const;
 
-            void        setShutterCallback(shutter_callback cb, void *cookie);
-            void        setRawCallback(frame_callback cb, void *cookie);
-            void        setJpegCallback(frame_callback cb, void *cookie);
-            void        setRecordingCallback(frame_callback cb, void *cookie);
-            void        setPreviewCallback(frame_callback cb, void *cookie, int preview_callback_flag = FRAME_CALLBACK_FLAG_NOOP);
-            void        setErrorCallback(error_callback cb, void *cookie);
-            void        setAutoFocusCallback(autofocus_callback cb, void *cookie);
+            void        setListener(const sp<CameraListener>& listener);
+            void        setPreviewCallbackFlags(int preview_callback_flag);
 
     // ICameraClient interface
     virtual void        notifyCallback(int32_t msgType, int32_t ext, int32_t ext2);
@@ -160,6 +160,8 @@ public:
 
 private:
                         Camera();
+                        Camera(const Camera&);
+                        Camera& operator=(const Camera);
                         virtual void binderDied(const wp<IBinder>& who);
 
             class DeathNotifier: public IBinder::DeathRecipient
@@ -179,20 +181,7 @@ private:
             sp<ICamera>         mCamera;
             status_t            mStatus;
 
-            shutter_callback    mShutterCallback;
-            void                *mShutterCallbackCookie;
-            frame_callback      mRawCallback;
-            void                *mRawCallbackCookie;
-            frame_callback      mJpegCallback;
-            void                *mJpegCallbackCookie;
-            frame_callback      mPreviewCallback;
-            void                *mPreviewCallbackCookie;
-            frame_callback      mRecordingCallback;
-            void                *mRecordingCallbackCookie;
-            error_callback      mErrorCallback;
-            void                *mErrorCallbackCookie;
-            autofocus_callback  mAutoFocusCallback;
-            void                *mAutoFocusCallbackCookie;
+            sp<CameraListener>  mListener;
 
             friend class DeathNotifier;
 
index dbbad1e..1653120 100644 (file)
@@ -31,12 +31,9 @@ public:
     // because we want the compiler generated versions
 
     // Default constructor doesn't initialize the Point
-    inline Point()
-    {
+    inline Point() {
     }
-
-    inline Point(int _x, int _y) : x(_x), y(_y)
-    {
+    inline Point(int x, int y) : x(x), y(y) {
     }
 
     inline bool operator == (const Point& rhs) const {
@@ -57,8 +54,8 @@ public:
     }
 
     inline Point& operator - () {
-        x=-x;
-        y=-y;
+        x = -x;
+        y = -y;
         return *this;
     }
     
@@ -73,11 +70,13 @@ public:
         return *this;
     }
     
-    Point operator + (const Point& rhs) const {
-        return Point(x+rhs.x, y+rhs.y);
+    const Point operator + (const Point& rhs) const {
+        const Point result(x+rhs.x, y+rhs.y);
+        return result;
     }
-    Point operator - (const Point& rhs) const {
-        return Point(x-rhs.x, y-rhs.y);
+    const Point operator - (const Point& rhs) const {
+        const Point result(x-rhs.x, y-rhs.y);
+        return result;
     }    
 };
 
index d232847..da72944 100644 (file)
@@ -33,23 +33,16 @@ public:
     // we don't provide copy-ctor and operator= on purpose
     // because we want the compiler generated versions
 
-    inline Rect()
-    {
+    inline Rect() {
     }
-
     inline Rect(int w, int h)
-        : left(0), top(0), right(w), bottom(h)
-    {
+        : left(0), top(0), right(w), bottom(h) {
     }
-
     inline Rect(int l, int t, int r, int b)
-        : left(l), top(t), right(r), bottom(b)
-    {
+        : left(l), top(t), right(r), bottom(b) {
     }
-
     inline Rect(const Point& lt, const Point& rb) 
-        : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y)
-    {
+        : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) {
     }
 
     void makeInvalid();
@@ -78,21 +71,22 @@ public:
         return bottom-top;
     }
 
-    // returns left-top Point non-const reference, can be assigned
-    inline Point& leftTop() {
-        return reinterpret_cast<Point&>(left);
+    void setLeftTop(const Point& lt) {
+        left = lt.x;
+        top  = lt.y;
     }
-    // returns right bottom non-const reference, can be assigned
-    inline Point& rightBottom() {
-        return reinterpret_cast<Point&>(right);
+
+    void setRightBottom(const Point& rb) {
+        right = rb.x;
+        bottom  = rb.y;
     }
     
     // the following 4 functions return the 4 corners of the rect as Point
-    inline const Point& leftTop() const {
-        return reinterpret_cast<const Point&>(left);
+    Point leftTop() const {
+        return Point(left, top);
     }
-    inline const Point& rightBottom() const {
-        return reinterpret_cast<const Point&>(right);
+    Point rightBottom() const {
+        return Point(right, bottom);
     }
     Point rightTop() const {
         return Point(right, top);
@@ -133,8 +127,8 @@ public:
     Rect& operator -= (const Point& rhs) {
         return offsetBy(-rhs.x, -rhs.y);
     }
-    Rect operator + (const Point& rhs) const;
-    Rect operator - (const Point& rhs) const;
+    const Rect operator + (const Point& rhs) const;
+    const Rect operator - (const Point& rhs) const;
 
     void translate(int dx, int dy) { // legacy, don't use.
         offsetBy(dx, dy);
index e94c0e8..d8994e0 100644 (file)
@@ -153,6 +153,18 @@ public:
     AssetDir* openDir(const char* dirName);
 
     /*
+     * Open a directory within a particular path of the asset manager.
+     *
+     * The contents of the directory are an amalgam of vendor-specific,
+     * locale-specific, and generic assets stored loosely or in asset
+     * packages.  Depending on the cache setting and previous accesses,
+     * this call may incur significant disk overhead.
+     *
+     * To open the top-level directory, pass in "".
+     */
+    AssetDir* openNonAssetDir(void* cookie, const char* dirName);
+
+    /*
      * Get the type of a file in the asset hierarchy.  They will either
      * be "regular" or "directory".  [Currently only works for "regular".]
      *
@@ -239,6 +251,9 @@ private:
         Asset* getResourceTableAsset();
         Asset* setResourceTableAsset(Asset* asset);
 
+        ResTable* getResourceTable();
+        ResTable* setResourceTable(ResTable* res);
+        
         bool isUpToDate();
         
     protected:
@@ -253,6 +268,7 @@ private:
         time_t mModWhen;
 
         Asset* mResourceTableAsset;
+        ResTable* mResourceTable;
 
         static Mutex gLock;
         static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
@@ -276,8 +292,11 @@ private:
          */
         ZipFileRO* getZip(const String8& path);
 
-        Asset* getZipResourceTable(const String8& path);
-        Asset* setZipResourceTable(const String8& path, Asset* asset);
+        Asset* getZipResourceTableAsset(const String8& path);
+        Asset* setZipResourceTableAsset(const String8& path, Asset* asset);
+
+        ResTable* getZipResourceTable(const String8& path);
+        ResTable* setZipResourceTable(const String8& path, ResTable* res);
 
         // generate path, e.g. "common/en-US-noogle.zip"
         static String8 getPathName(const char* path);
diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h
new file mode 100644 (file)
index 0000000..b1f5045
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef _UTILS_BACKUP_HELPERS_H
+#define _UTILS_BACKUP_HELPERS_H
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+enum {
+    BACKUP_HEADER_ENTITY_V1 = 0x61746144, // Data (little endian)
+};
+
+typedef struct {
+    int type; // BACKUP_HEADER_ENTITY_V1
+    int keyLen; // length of the key name, not including the null terminator
+    int dataSize; // size of the data, not including the padding, -1 means delete
+} entity_header_v1;
+
+struct SnapshotHeader {
+    int magic0;
+    int fileCount;
+    int magic1;
+    int totalSize;
+};
+
+struct FileState {
+    int modTime_sec;
+    int modTime_nsec;
+    int mode;
+    int size;
+    int crc32;
+    int nameLen;
+};
+
+struct FileRec {
+    String8 file;
+    bool deleted;
+    FileState s;
+};
+
+
+/**
+ * Writes the data.
+ *
+ * If an error occurs, it poisons this object and all write calls will fail
+ * with the error that occurred.
+ */
+class BackupDataWriter
+{
+public:
+    BackupDataWriter(int fd);
+    // does not close fd
+    ~BackupDataWriter();
+
+    status_t WriteEntityHeader(const String8& key, size_t dataSize);
+    status_t WriteEntityData(const void* data, size_t size);
+
+    void SetKeyPrefix(const String8& keyPrefix);
+
+private:
+    explicit BackupDataWriter();
+    status_t write_padding_for(int n);
+    
+    int m_fd;
+    status_t m_status;
+    ssize_t m_pos;
+    int m_entityCount;
+    String8 m_keyPrefix;
+};
+
+/**
+ * Reads the data.
+ *
+ * If an error occurs, it poisons this object and all write calls will fail
+ * with the error that occurred.
+ */
+class BackupDataReader
+{
+public:
+    BackupDataReader(int fd);
+    // does not close fd
+    ~BackupDataReader();
+
+    status_t Status();
+    status_t ReadNextHeader(bool* done, int* type);
+
+    bool HasEntities();
+    status_t ReadEntityHeader(String8* key, size_t* dataSize);
+    status_t SkipEntityData(); // must be called with the pointer at the begining of the data.
+    ssize_t ReadEntityData(void* data, size_t size);
+
+private:
+    explicit BackupDataReader();
+    status_t skip_padding();
+    
+    int m_fd;
+    bool m_done;
+    status_t m_status;
+    ssize_t m_pos;
+    ssize_t m_dataEndPos;
+    int m_entityCount;
+    union {
+        int type;
+        entity_header_v1 entity;
+    } m_header;
+    String8 m_key;
+};
+
+int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
+        char const* const* files, char const* const *keys, int fileCount);
+
+class RestoreHelperBase
+{
+public:
+    RestoreHelperBase();
+    ~RestoreHelperBase();
+
+    status_t WriteFile(const String8& filename, BackupDataReader* in);
+    status_t WriteSnapshot(int fd);
+
+private:
+    void* m_buf;
+    bool m_loggedUnknownMetadata;
+    KeyedVector<String8,FileRec> m_files;
+};
+
+#define TEST_BACKUP_HELPERS 1
+
+#if TEST_BACKUP_HELPERS
+int backup_helper_test_empty();
+int backup_helper_test_four();
+int backup_helper_test_files();
+int backup_helper_test_null_base();
+int backup_helper_test_missing_file();
+int backup_helper_test_data_writer();
+int backup_helper_test_data_reader();
+#endif
+
+} // namespace android
+
+#endif // _UTILS_BACKUP_HELPERS_H
index 4c06067..baa3a83 100644 (file)
  * intent is to allow us to avoid byte swapping on the device.
  */
 
+static inline uint32_t android_swap_long(uint32_t v)
+{
+    return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24);
+}
+
+static inline uint16_t android_swap_short(uint16_t v)
+{
+    return (v<<8) | (v>>8);
+}
+
 #define DEVICE_BYTE_ORDER LITTLE_ENDIAN
 
 #if BYTE_ORDER == DEVICE_BYTE_ORDER
 
 #else
 
-static inline uint32_t android_swap_long(uint32_t v)
-{
-    return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24);
-}
-
-static inline uint16_t android_swap_short(uint16_t v)
-{
-    return (v<<8) | (v>>8);
-}
-
 #define        dtohl(x)        (android_swap_long(x))
 #define        dtohs(x)        (android_swap_short(x))
 #define        htodl(x)        (android_swap_long(x))
@@ -66,4 +66,16 @@ static inline uint16_t android_swap_short(uint16_t v)
 
 #endif
 
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define fromlel(x) (x)
+#define fromles(x) (x)
+#define tolel(x) (x)
+#define toles(x) (x)
+#else
+#define fromlel(x) (android_swap_long(x))
+#define fromles(x) (android_swap_short(x))
+#define tolel(x) (android_swap_long(x))
+#define toles(x) (android_swap_short(x))
+#endif
+
 #endif // _LIBS_UTILS_BYTE_ORDER_H
index 9087c44..af1490a 100644 (file)
@@ -80,8 +80,11 @@ public:
     status_t            writeStrongBinder(const sp<IBinder>& val);
     status_t            writeWeakBinder(const wp<IBinder>& val);
 
-    // doesn't take ownership of the native_handle
-    status_t            writeNativeHandle(const native_handle& handle);
+    // Place a native_handle into the parcel (the native_handle's file-
+    // descriptors are dup'ed, so it is safe to delete the native_handle
+    // when this function returns). 
+    // Doesn't take ownership of the native_handle.
+    status_t            writeNativeHandle(const native_handle* handle);
     
     // Place a file descriptor into the parcel.  The given fd must remain
     // valid for the lifetime of the parcel.
@@ -114,12 +117,11 @@ public:
     wp<IBinder>         readWeakBinder() const;
 
     
-    // if alloc is NULL, native_handle is allocated with malloc(), otherwise
-    // alloc is used. If the function fails, the effects of alloc() must be
-    // reverted by the caller.
-    native_handle*     readNativeHandle(
-            native_handle* (*alloc)(void* cookie, int numFds, int ints),
-            void* cookie) const;
+    // Retrieve native_handle from the parcel. This returns a copy of the
+    // parcel's native_handle (the caller takes ownership). The caller
+    // must free the native_handle with native_handle_close() and 
+    // native_handle_delete().
+    native_handle*     readNativeHandle() const;
 
     
     // Retrieve a file descriptor from the parcel.  This returns the raw fd
index 9b8c302..93bca4a 100644 (file)
@@ -71,7 +71,7 @@ namespace android {
  * The relative sizes of the stretchy segments indicates the relative
  * amount of stretchiness of the regions bordered by the segments.  For
  * example, regions 3, 7 and 11 above will take up more horizontal space
- * than regions 1, 5 and 9 since the horizonal segment associated with
+ * than regions 1, 5 and 9 since the horizontal segment associated with
  * the first set of regions is larger than the other set of regions.  The
  * ratios of the amount of horizontal (or vertical) space taken by any
  * two stretchable slices is exactly the ratio of their corresponding
@@ -87,7 +87,7 @@ namespace android {
  * the leftmost slices always start at x=0 and the rightmost slices
  * always end at the end of the image. So, for example, the regions 0,
  * 4 and 8 (which are fixed along the X axis) start at x value 0 and
- * go to xDiv[0] amd slices 2, 6 and 10 start at xDiv[1] and end at
+ * go to xDiv[0] and slices 2, 6 and 10 start at xDiv[1] and end at
  * xDiv[2].
  *
  * The array pointed to by the colors field lists contains hints for
@@ -626,25 +626,25 @@ public:
     event_code_t next();
 
     // These are available for all nodes:
-    const int32_t getCommentID() const;
+    int32_t getCommentID() const;
     const uint16_t* getComment(size_t* outLen) const;
-    const uint32_t getLineNumber() const;
+    uint32_t getLineNumber() const;
     
     // This is available for TEXT:
-    const int32_t getTextID() const;
+    int32_t getTextID() const;
     const uint16_t* getText(size_t* outLen) const;
     ssize_t getTextValue(Res_value* outValue) const;
     
     // These are available for START_NAMESPACE and END_NAMESPACE:
-    const int32_t getNamespacePrefixID() const;
+    int32_t getNamespacePrefixID() const;
     const uint16_t* getNamespacePrefix(size_t* outLen) const;
-    const int32_t getNamespaceUriID() const;
+    int32_t getNamespaceUriID() const;
     const uint16_t* getNamespaceUri(size_t* outLen) const;
     
     // These are available for START_TAG and END_TAG:
-    const int32_t getElementNamespaceID() const;
+    int32_t getElementNamespaceID() const;
     const uint16_t* getElementNamespace(size_t* outLen) const;
-    const int32_t getElementNameID() const;
+    int32_t getElementNameID() const;
     const uint16_t* getElementName(size_t* outLen) const;
     
     // Remaining methods are for retrieving information about attributes
@@ -653,14 +653,14 @@ public:
     size_t getAttributeCount() const;
     
     // Returns -1 if no namespace, -2 if idx out of range.
-    const int32_t getAttributeNamespaceID(size_t idx) const;
+    int32_t getAttributeNamespaceID(size_t idx) const;
     const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const;
     
-    const int32_t getAttributeNameID(size_t idx) const;
+    int32_t getAttributeNameID(size_t idx) const;
     const uint16_t* getAttributeName(size_t idx, size_t* outLen) const;
-    const uint32_t getAttributeNameResID(size_t idx) const;
+    uint32_t getAttributeNameResID(size_t idx) const;
     
-    const int32_t getAttributeValueStringID(size_t idx) const;
+    int32_t getAttributeValueStringID(size_t idx) const;
     const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const;
     
     int32_t getAttributeDataType(size_t idx) const;
@@ -866,7 +866,7 @@ struct ResTable_config
             uint8_t keyboard;
             uint8_t navigation;
             uint8_t inputFlags;
-            uint8_t pad0;
+            uint8_t inputPad0;
         };
         uint32_t input;
     };
@@ -905,6 +905,23 @@ struct ResTable_config
         uint32_t version;
     };
     
+    enum {
+        SCREENLAYOUT_ANY  = 0x0000,
+        SCREENLAYOUT_SMALL = 0x0001,
+        SCREENLAYOUT_NORMAL = 0x0002,
+        SCREENLAYOUT_LARGE = 0x0003,
+    };
+    
+    union {
+        struct {
+            uint8_t screenLayout;
+            uint8_t screenConfigPad0;
+            uint8_t screenConfigPad1;
+            uint8_t screenConfigPad2;
+        };
+        uint32_t screenConfig;
+    };
+    
     inline void copyFromDeviceNoSwap(const ResTable_config& o) {
         const size_t size = dtohl(o.size);
         if (size >= sizeof(ResTable_config)) {
@@ -950,6 +967,8 @@ struct ResTable_config
         diff = (int32_t)(screenSize - o.screenSize);
         if (diff != 0) return diff;
         diff = (int32_t)(version - o.version);
+        if (diff != 0) return diff;
+        diff = (int32_t)(screenLayout - o.screenLayout);
         return (int)diff;
     }
     
@@ -967,7 +986,8 @@ struct ResTable_config
         CONFIG_ORIENTATION = 0x0080,
         CONFIG_DENSITY = 0x0100,
         CONFIG_SCREEN_SIZE = 0x0200,
-        CONFIG_VERSION = 0x0400
+        CONFIG_VERSION = 0x0400,
+        CONFIG_SCREEN_LAYOUT = 0x0800
     };
     
     // Compare two configuration, returning CONFIG_* flags set for each value
@@ -985,6 +1005,7 @@ struct ResTable_config
         if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION;
         if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE;
         if (version != o.version) diffs |= CONFIG_VERSION;
+        if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT;
         return diffs;
     }
     
@@ -1062,6 +1083,13 @@ struct ResTable_config
             }
         }
 
+        if (screenConfig || o.screenConfig) {
+            if (screenLayout != o.screenLayout) {
+                if (!screenLayout) return false;
+                if (!o.screenLayout) return true;
+            }
+        }
+
         if (version || o.version) {
             if (sdkVersion != o.sdkVersion) {
                 if (!sdkVersion) return false;
@@ -1191,6 +1219,12 @@ struct ResTable_config
                 }
             }
 
+            if (screenConfig || o.screenConfig) {
+                if ((screenLayout != o.screenLayout) && requested->screenLayout) {
+                    return (screenLayout);
+                }
+            }
+
             if (version || o.version) {
                 if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) {
                     return (sdkVersion);
@@ -1282,6 +1316,12 @@ struct ResTable_config
                 return false;
             }
         }
+        if (screenConfig != 0) {
+            if (settings.screenLayout != 0 && screenLayout != 0
+                && screenLayout != settings.screenLayout) {
+                return false;
+            }
+        }
         if (version != 0) {
             if (settings.sdkVersion != 0 && sdkVersion != 0
                 && sdkVersion != settings.sdkVersion) {
@@ -1310,13 +1350,13 @@ struct ResTable_config
 
     String8 toString() const {
         char buf[200];
-        sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x "
-                "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d",
+        sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d "
+                "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d layout=%d vers=%d.%d",
                 mcc, mnc,
                 language[0] ? language[0] : '-', language[1] ? language[1] : '-',
                 country[0] ? country[0] : '-', country[1] ? country[1] : '-',
                 orientation, touchscreen, density, keyboard, navigation, inputFlags,
-                screenWidth, screenHeight, sdkVersion, minorVersion);
+                screenWidth, screenHeight, screenLayout, sdkVersion, minorVersion);
         return String8(buf);
     }
 };
@@ -1401,7 +1441,7 @@ struct ResTable_type
  * This is the beginning of information about an entry in the resource
  * table.  It holds the reference to the name of this entry, and is
  * immediately followed by one of:
- *   * A Res_value structures, if FLAG_COMPLEX is -not- set.
+ *   * A Res_value structure, if FLAG_COMPLEX is -not- set.
  *   * An array of ResTable_map structures, if FLAG_COMPLEX is set.
  *     These supply a set of name/value mappings of data.
  */
@@ -1540,6 +1580,7 @@ public:
                  bool copyData=false);
     status_t add(Asset* asset, void* cookie,
                  bool copyData=false);
+    status_t add(ResTable* src);
 
     status_t getError() const;
 
@@ -1781,7 +1822,7 @@ public:
     void getLocales(Vector<String8>* locales) const;
 
 #ifndef HAVE_ANDROID_OS
-    void print() const;
+    void print(bool inclValues) const;
 #endif
 
 private:
@@ -1803,6 +1844,8 @@ private:
     status_t parsePackage(
         const ResTable_package* const pkg, const Header* const header);
 
+    void print_value(const Package* pkg, const Res_value& value) const;
+    
     mutable Mutex               mLock;
 
     status_t                    mError;
diff --git a/include/utils/TimeUtils.h b/include/utils/TimeUtils.h
deleted file mode 100644 (file)
index b19e021..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#ifndef ANDROID_TIME_H
-#define ANDROID_TIME_H
-
-#include <time.h>
-#include <cutils/tztime.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-
-namespace android {
-
-/*
- * This class is the core implementation of the android.util.Time java
- * class.  It doesn't implement some of the methods that are implemented
- * in Java.  They could be done here, but it's not expected that this class
- * will be used.  If that assumption is incorrect, feel free to update this
- * file.  The reason to do it here is to not mix the implementation of this
- * class and the jni glue code.
- */
-class Time
-{
-public:
-    struct tm t;
-
-    // this object doesn't own this string
-    const char *timezone;
-
-    enum {
-        SEC = 1,
-        MIN = 2,
-        HOUR = 3,
-        MDAY = 4,
-        MON = 5,
-        YEAR = 6,
-        WDAY = 7,
-        YDAY = 8
-    };
-
-    static int compare(Time& a, Time& b);
-
-    Time();
-
-    void switchTimezone(const char *timezone);
-    String8 format(const char *format, const struct strftime_locale *locale) const;
-    void format2445(short* buf, bool hasTime) const;
-    String8 toString() const;
-    void setToNow();
-    int64_t toMillis(bool ignoreDst);
-    void set(int64_t millis);
-
-    inline void set(int sec, int min, int hour, int mday, int mon, int year,
-            int isdst)
-    {
-        this->t.tm_sec = sec;
-        this->t.tm_min = min;
-        this->t.tm_hour = hour;
-        this->t.tm_mday = mday;
-        this->t.tm_mon = mon;
-        this->t.tm_year = year;
-        this->t.tm_isdst = isdst;
-#ifdef HAVE_TM_GMTOFF
-        this->t.tm_gmtoff = 0;
-#endif
-        this->t.tm_wday = 0;
-        this->t.tm_yday = 0;
-    }
-};
-
-}; // namespace android
-
-#endif // ANDROID_TIME_H
diff --git a/include/utils/backup_helpers.h b/include/utils/backup_helpers.h
deleted file mode 100644 (file)
index 137c5f1..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef _UTILS_BACKUP_HELPERS_H
-#define _UTILS_BACKUP_HELPERS_H
-
-int back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD,
-        char const* fileBase, char const* const* files, int fileCount);
-
-#define TEST_BACKUP_HELPERS 0
-
-#if TEST_BACKUP_HELPERS
-int backup_helper_test_empty();
-int backup_helper_test_four();
-int backup_helper_test_files();
-#endif
-
-#endif // _UTILS_BACKUP_HELPERS_H
index b6d5078..16a4f2d 100644 (file)
@@ -71,8 +71,8 @@ AudioStreamOut* A2dpAudioInterface::openOutputStream(
 }
 
 AudioStreamIn* A2dpAudioInterface::openInputStream(
-        int format, int channelCount, uint32_t sampleRate, status_t *status,
-        AudioSystem::audio_in_acoustics acoustics)
+        int inputSource, int format, int channelCount, uint32_t sampleRate,
+        status_t *status, AudioSystem::audio_in_acoustics acoustics)
 {
     if (status)
         *status = -1;
index 7901a8c..091e775 100644 (file)
@@ -55,6 +55,7 @@ public:
                                 status_t *status=0);
 
     virtual AudioStreamIn* openInputStream(
+                                int inputSource,
                                 int format,
                                 int channelCount,
                                 uint32_t sampleRate,
index 1a467c7..81c5c39 100644 (file)
@@ -36,6 +36,8 @@ public:
         };
         size_t frameCount;
     };
+
+    virtual ~AudioBufferProvider() {}
     
     virtual status_t getNextBuffer(Buffer* buffer) = 0;
     virtual void releaseBuffer(Buffer* buffer) = 0;
index 9a94102..b72c94e 100644 (file)
@@ -78,9 +78,9 @@ public:
     virtual status_t    setParameter(const char* key, const char* value)
                             {return mFinalInterface->setParameter(key, value);}
 
-    virtual AudioStreamIn* openInputStream( int format, int channelCount, uint32_t sampleRate, status_t *status,
-                                            AudioSystem::audio_in_acoustics acoustics)
-                            {return mFinalInterface->openInputStream( format, channelCount, sampleRate, status, acoustics);}
+    virtual AudioStreamIn* openInputStream(int inputSource, int format, int channelCount,
+            uint32_t sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics)
+        { return mFinalInterface->openInputStream(inputSource, format, channelCount, sampleRate, status, acoustics); }
 
     virtual status_t    dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); }
 
index b56221f..8a19fbd 100644 (file)
@@ -499,7 +499,8 @@ status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask)
     }
 
 #ifdef WITH_A2DP
-    LOGD("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), IPCThreadState::self()->getCallingPid());
+    LOGV("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(),
+            IPCThreadState::self()->getCallingPid());
     if (mode == AudioSystem::MODE_NORMAL && 
             (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) {
         AutoMutex lock(&mLock);
@@ -817,19 +818,22 @@ void AudioFlinger::handleForcedSpeakerRoute(int command)
         {
             AutoMutex lock(mHardwareLock);
             if (mForcedSpeakerCount++ == 0) {
-                mRouteRestoreTime = 0;
-                mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC);
-                if (mForcedRoute == 0 && !(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) {
-                    LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER);
-                    mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true);
-                    usleep(mHardwareMixerThread->latency()*1000);
-                    mHardwareStatus = AUDIO_HW_SET_ROUTING;
-                    mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER);
-                    mHardwareStatus = AUDIO_HW_IDLE;
-                    // delay track start so that audio hardware has time to siwtch routes
-                    usleep(kStartSleepTime);
+                if (mForcedRoute == 0) {
+                    mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC);
+                    LOGV("++mForcedSpeakerCount == 0, mMusicMuteSaved = %d, mRouteRestoreTime = %d", mMusicMuteSaved, mRouteRestoreTime);
+                    if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) {
+                        LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER);
+                        mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true);
+                        usleep(mHardwareMixerThread->latency()*1000);
+                        mHardwareStatus = AUDIO_HW_SET_ROUTING;
+                        mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER);
+                        mHardwareStatus = AUDIO_HW_IDLE;
+                        // delay track start so that audio hardware has time to siwtch routes
+                        usleep(kStartSleepTime);
+                    }
                 }
                 mForcedRoute = AudioSystem::ROUTE_SPEAKER;
+                mRouteRestoreTime = 0;
             }
             LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount);
         }
@@ -890,7 +894,7 @@ void AudioFlinger::handleRouteDisablesA2dp_l(int routes)
             }
             LOGV("mA2dpDisableCount decremented to %d", mA2dpDisableCount);
         } else {
-            LOGE("mA2dpDisableCount is already zero");
+            LOGV("mA2dpDisableCount is already zero");
         }
     }
 }
@@ -1277,7 +1281,7 @@ sp<AudioFlinger::MixerThread::Track>  AudioFlinger::MixerThread::createTrack_l(
     status_t lStatus;
     
     // Resampler implementation limits input sampling rate to 2 x output sampling rate.
-    if (sampleRate > MAX_SAMPLE_RATE || sampleRate > mSampleRate*2) {
+    if (sampleRate > mSampleRate*2) {
         LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate);
         lStatus = BAD_VALUE;
         goto Exit;
@@ -1553,7 +1557,6 @@ size_t AudioFlinger::MixerThread::getOutputFrameCount()
 AudioFlinger::MixerThread::TrackBase::TrackBase(
             const sp<MixerThread>& mixerThread,
             const sp<Client>& client,
-            int streamType,
             uint32_t sampleRate,
             int format,
             int channelCount,
@@ -1563,7 +1566,6 @@ AudioFlinger::MixerThread::TrackBase::TrackBase(
     :   RefBase(),
         mMixerThread(mixerThread),
         mClient(client),
-        mStreamType(streamType),
         mFrameCount(0),
         mState(IDLE),
         mClientTid(-1),
@@ -1594,8 +1596,8 @@ AudioFlinger::MixerThread::TrackBase::TrackBase(
                 new(mCblk) audio_track_cblk_t();
                 // clear all buffers
                 mCblk->frameCount = frameCount;
-                mCblk->sampleRate = (uint16_t)sampleRate;
-                mCblk->channels = (uint16_t)channelCount;
+                mCblk->sampleRate = sampleRate;
+                mCblk->channels = (uint8_t)channelCount;
                 if (sharedBuffer == 0) {
                     mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
                     memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
@@ -1618,8 +1620,8 @@ AudioFlinger::MixerThread::TrackBase::TrackBase(
            new(mCblk) audio_track_cblk_t();
            // clear all buffers
            mCblk->frameCount = frameCount;
-           mCblk->sampleRate = (uint16_t)sampleRate;
-           mCblk->channels = (uint16_t)channelCount;
+           mCblk->sampleRate = sampleRate;
+           mCblk->channels = (uint8_t)channelCount;
            mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
            memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
            // Force underrun condition to avoid false underrun callback until first data is
@@ -1680,7 +1682,7 @@ int AudioFlinger::MixerThread::TrackBase::sampleRate() const {
 }
 
 int AudioFlinger::MixerThread::TrackBase::channelCount() const {
-    return mCblk->channels;
+    return (int)mCblk->channels;
 }
 
 void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
@@ -1713,12 +1715,13 @@ AudioFlinger::MixerThread::Track::Track(
             int channelCount,
             int frameCount,
             const sp<IMemory>& sharedBuffer)
-    :   TrackBase(mixerThread, client, streamType, sampleRate, format, channelCount, frameCount, 0, sharedBuffer)
+    :   TrackBase(mixerThread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer)
 {
     mVolume[0] = 1.0f;
     mVolume[1] = 1.0f;
     mMute = false;
     mSharedBuffer = sharedBuffer;
+    mStreamType = streamType;
 }
 
 AudioFlinger::MixerThread::Track::~Track()
@@ -1902,15 +1905,15 @@ void AudioFlinger::MixerThread::Track::setVolume(float left, float right)
 AudioFlinger::MixerThread::RecordTrack::RecordTrack(
             const sp<MixerThread>& mixerThread,
             const sp<Client>& client,
-            int streamType,
+            int inputSource,
             uint32_t sampleRate,
             int format,
             int channelCount,
             int frameCount,
             uint32_t flags)
-    :   TrackBase(mixerThread, client, streamType, sampleRate, format,
+    :   TrackBase(mixerThread, client, sampleRate, format,
                   channelCount, frameCount, flags, 0),
-        mOverflow(false)
+        mOverflow(false), mInputSource(inputSource)
 {
 }
 
@@ -2235,7 +2238,7 @@ status_t AudioFlinger::TrackHandle::onTransact(
 
 sp<IAudioRecord> AudioFlinger::openRecord(
         pid_t pid,
-        int streamType,
+        int inputSource,
         uint32_t sampleRate,
         int format,
         int channelCount,
@@ -2258,18 +2261,12 @@ sp<IAudioRecord> AudioFlinger::openRecord(
         goto Exit;
     }
 
-    if (uint32_t(streamType) >= AudioRecord::NUM_STREAM_TYPES) {
+    if (uint32_t(inputSource) >= AudioRecord::NUM_INPUT_SOURCES) {
         LOGE("invalid stream type");
         lStatus = BAD_VALUE;
         goto Exit;
     }
 
-    if (sampleRate > MAX_SAMPLE_RATE) {
-        LOGE("Sample rate out of range");
-        lStatus = BAD_VALUE;
-        goto Exit;
-    }
-
     if (mAudioRecordThread == 0) {
         LOGE("Audio record thread not started");
         lStatus = NO_INIT;
@@ -2301,7 +2298,7 @@ sp<IAudioRecord> AudioFlinger::openRecord(
         frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount;
     
         // create new record track. The record track uses one track in mHardwareMixerThread by convention.
-        recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate,
+        recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, inputSource, sampleRate,
                                                    format, channelCount, frameCount, flags);
     }
     if (recordTrack->getCblk() == NULL) {
@@ -2407,7 +2404,9 @@ bool AudioFlinger::AudioRecordThread::threadLoop()
                
                 LOGV("AudioRecordThread: loop starting");
                 if (mRecordTrack != 0) {
-                    input = mAudioHardware->openInputStream(mRecordTrack->format(), 
+                    input = mAudioHardware->openInputStream(
+                                    mRecordTrack->inputSource(),
+                                    mRecordTrack->format(), 
                                     mRecordTrack->channelCount(), 
                                     mRecordTrack->sampleRate(), 
                                     &mStartStatus,
index c7ca9ec..8e47b29 100644 (file)
@@ -139,7 +139,7 @@ public:
     // record interface
     virtual sp<IAudioRecord> openRecord(
                                 pid_t pid,
-                                int streamType,
+                                int inputSource,
                                 uint32_t sampleRate,
                                 int format,
                                 int channelCount,
@@ -232,7 +232,6 @@ private:
 
                                 TrackBase(const sp<MixerThread>& mixerThread,
                                         const sp<Client>& client,
-                                        int streamType,
                                         uint32_t sampleRate,
                                         int format,
                                         int channelCount,
@@ -260,10 +259,6 @@ private:
                 return mCblk;
             }
 
-            int type() const {
-                return mStreamType;
-            }
-
             int format() const {
                 return mFormat;
             }
@@ -293,7 +288,6 @@ private:
             sp<Client>          mClient;
             sp<IMemory>         mCblkMemory;
             audio_track_cblk_t* mCblk;
-            int                 mStreamType;
             void*               mBuffer;
             void*               mBufferEnd;
             uint32_t            mFrameCount;
@@ -328,6 +322,11 @@ private:
                     void        mute(bool);
                     void        setVolume(float left, float right);
 
+                    int type() const {
+                        return mStreamType;
+                    }
+
+
         protected:
             friend class MixerThread;
             friend class AudioFlinger;
@@ -364,6 +363,7 @@ private:
             int8_t              mRetryCount;
             sp<IMemory>         mSharedBuffer;
             bool                mResetDone;
+            int                 mStreamType;
         };  // end of Track
 
         // record track
@@ -371,7 +371,7 @@ private:
         public:
                                 RecordTrack(const sp<MixerThread>& mixerThread,
                                         const sp<Client>& client,
-                                        int streamType,
+                                        int inputSource,
                                         uint32_t sampleRate,
                                         int format,
                                         int channelCount,
@@ -385,6 +385,8 @@ private:
                     bool        overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; }
                     bool        setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; }
 
+                    int         inputSource() const { return mInputSource; }
+
         private:
             friend class AudioFlinger;
             friend class AudioFlinger::RecordHandle;
@@ -397,6 +399,7 @@ private:
             virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
 
             bool                mOverflow;
+            int                 mInputSource;
         };
 
         // playback track
index 62beada..1e159b8 100644 (file)
@@ -30,6 +30,7 @@
 #include <utils/String8.h>
 
 #include "AudioHardwareGeneric.h"
+#include <media/AudioRecord.h>
 
 namespace android {
 
@@ -93,9 +94,15 @@ void AudioHardwareGeneric::closeOutputStream(AudioStreamOutGeneric* out) {
 }
 
 AudioStreamIn* AudioHardwareGeneric::openInputStream(
-        int format, int channelCount, uint32_t sampleRate, status_t *status,
-        AudioSystem::audio_in_acoustics acoustics)
+        int inputSource, int format, int channelCount, uint32_t sampleRate,
+        status_t *status, AudioSystem::audio_in_acoustics acoustics)
 {
+    // check for valid input source
+    if ((inputSource < AudioRecord::DEFAULT_INPUT) ||
+        (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) {
+        return 0;
+    }
+
     AutoMutex lock(mLock);
 
     // only one input stream allowed
index c949aa1..c89df87 100644 (file)
@@ -112,6 +112,7 @@ public:
             status_t *status=0);
 
     virtual AudioStreamIn* openInputStream(
+            int inputSource,
             int format,
             int channelCount,
             uint32_t sampleRate,
index b13cb1c..0ab4c60 100644 (file)
@@ -23,6 +23,7 @@
 #include <utils/String8.h>
 
 #include "AudioHardwareStub.h"
+#include <media/AudioRecord.h>
 
 namespace android {
 
@@ -56,9 +57,15 @@ AudioStreamOut* AudioHardwareStub::openOutputStream(
 }
 
 AudioStreamIn* AudioHardwareStub::openInputStream(
-        int format, int channelCount, uint32_t sampleRate,
+        int inputSource, int format, int channelCount, uint32_t sampleRate,
         status_t *status, AudioSystem::audio_in_acoustics acoustics)
 {
+    // check for valid input source
+    if ((inputSource < AudioRecord::DEFAULT_INPUT) ||
+        (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) {
+        return 0;
+    }
+
     AudioStreamInStub* in = new AudioStreamInStub();
     status_t lStatus = in->set(format, channelCount, sampleRate, acoustics);
     if (status) {
index d406424..bf63cc5 100644 (file)
@@ -78,6 +78,7 @@ public:
                                 status_t *status=0);
 
     virtual AudioStreamIn* openInputStream(
+                                int inputSource,
                                 int format,
                                 int channelCount,
                                 uint32_t sampleRate,
index 2212436..9272983 100644 (file)
@@ -6,7 +6,6 @@ LOCAL_SRC_FILES:= \
     DisplayHardware/DisplayHardware.cpp \
     DisplayHardware/DisplayHardwareBase.cpp \
     GPUHardware/GPUHardware.cpp \
-    BootAnimation.cpp \
     BlurFilter.cpp.arm \
     CPUGauge.cpp \
     Layer.cpp \
diff --git a/libs/surfaceflinger/BootAnimation.cpp b/libs/surfaceflinger/BootAnimation.cpp
deleted file mode 100644 (file)
index db40385..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#define LOG_TAG "BootAnimation"
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <math.h>
-#include <fcntl.h>
-#include <utils/misc.h>
-
-#include <utils/threads.h>
-#include <utils/Atomic.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/AssetManager.h>
-
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-#include <ui/DisplayInfo.h>
-#include <ui/ISurfaceComposer.h>
-#include <ui/ISurfaceFlingerClient.h>
-#include <ui/EGLNativeWindowSurface.h>
-
-#include <core/SkBitmap.h>
-#include <images/SkImageDecoder.h>
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <EGL/eglext.h>
-
-#include "BootAnimation.h"
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-BootAnimation::BootAnimation(const sp<ISurfaceComposer>& composer) :
-    Thread(false) {
-    mSession = SurfaceComposerClient::clientForConnection(
-            composer->createConnection()->asBinder());
-}
-
-BootAnimation::~BootAnimation() {
-}
-
-void BootAnimation::onFirstRef() {
-    run("BootAnimation", PRIORITY_DISPLAY);
-}
-
-const sp<SurfaceComposerClient>& BootAnimation::session() const {
-    return mSession;
-}
-
-status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
-        const char* name) {
-    Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
-    if (!asset)
-        return NO_INIT;
-    SkBitmap bitmap;
-    SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
-            &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode);
-    asset->close();
-    delete asset;
-
-    // ensure we can call getPixels(). No need to call unlock, since the
-    // bitmap will go out of scope when we return from this method.
-    bitmap.lockPixels();
-
-    const int w = bitmap.width();
-    const int h = bitmap.height();
-    const void* p = bitmap.getPixels();
-
-    GLint crop[4] = { 0, h, w, -h };
-    texture->w = w;
-    texture->h = h;
-
-    glGenTextures(1, &texture->name);
-    glBindTexture(GL_TEXTURE_2D, texture->name);
-
-    switch (bitmap.getConfig()) {
-        case SkBitmap::kA8_Config:
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA,
-                    GL_UNSIGNED_BYTE, p);
-            break;
-        case SkBitmap::kARGB_4444_Config:
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
-                    GL_UNSIGNED_SHORT_4_4_4_4, p);
-            break;
-        case SkBitmap::kARGB_8888_Config:
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
-                    GL_UNSIGNED_BYTE, p);
-            break;
-        case SkBitmap::kRGB_565_Config:
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
-                    GL_UNSIGNED_SHORT_5_6_5, p);
-            break;
-        default:
-            break;
-    }
-
-    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-    return NO_ERROR;
-}
-
-status_t BootAnimation::readyToRun() {
-    mAssets.addDefaultAssets();
-
-    DisplayInfo dinfo;
-    status_t status = session()->getDisplayInfo(0, &dinfo);
-    if (status)
-        return -1;
-
-    // create the native surface
-    sp<Surface> s = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h,
-            PIXEL_FORMAT_RGB_565);
-    session()->openTransaction();
-    s->setLayer(0x40000000);
-    session()->closeTransaction();
-
-    // initialize opengl and egl
-    const EGLint attribs[] = { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6,
-            EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 0, EGL_NONE };
-    EGLint w, h, dummy;
-    EGLint numConfigs;
-    EGLConfig config;
-    EGLSurface surface;
-    EGLContext context;
-    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
-
-    mNativeWindowSurface = new EGLNativeWindowSurface(s);
-    surface = eglCreateWindowSurface(display, config, 
-            mNativeWindowSurface.get(), NULL);
-
-    context = eglCreateContext(display, config, NULL, NULL);
-    eglQuerySurface(display, surface, EGL_WIDTH, &w);
-    eglQuerySurface(display, surface, EGL_HEIGHT, &h);
-    eglMakeCurrent(display, surface, surface, context);
-    mDisplay = display;
-    mContext = context;
-    mSurface = surface;
-    mWidth = w;
-    mHeight = h;
-    mFlingerSurface = s;
-
-    // initialize GL
-    glShadeModel(GL_FLAT);
-    glEnable(GL_TEXTURE_2D);
-    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-
-    return NO_ERROR;
-}
-
-void BootAnimation::requestExit() {
-    mBarrier.open();
-    Thread::requestExit();
-}
-
-bool BootAnimation::threadLoop() {
-    bool r = android();
-    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    eglDestroyContext(mDisplay, mContext);
-    eglDestroySurface(mDisplay, mSurface);
-    mNativeWindowSurface.clear();
-    return r;
-}
-
-bool BootAnimation::android() {
-    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
-    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
-
-    // clear screen
-    glDisable(GL_DITHER);
-    glDisable(GL_SCISSOR_TEST);
-    glClear(GL_COLOR_BUFFER_BIT);
-    eglSwapBuffers(mDisplay, mSurface);
-
-    const GLint xc = (mWidth  - mAndroid[0].w) / 2;
-    const GLint yc = (mHeight - mAndroid[0].h) / 2;
-    const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
-
-    // draw and update only what we need
-    mNativeWindowSurface->setSwapRectangle(updateRect.left,
-            updateRect.top, updateRect.width(), updateRect.height());
-
-    glEnable(GL_SCISSOR_TEST);
-    glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
-            updateRect.height());
-
-    // Blend state
-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-
-    const nsecs_t startTime = systemTime();
-    do {
-        nsecs_t now = systemTime();
-        double time = now - startTime;
-        float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
-        GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
-        GLint x = xc - offset;
-
-        glDisable(GL_BLEND);
-        glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
-        glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);
-        glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
-
-        glEnable(GL_BLEND);
-        glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
-        glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
-
-        eglSwapBuffers(mDisplay, mSurface);
-        
-        // 12fps: don't animate too fast to preserve CPU
-        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
-        if (sleepTime > 0)
-            usleep(sleepTime); 
-    } while (!exitPending());
-
-    glDeleteTextures(1, &mAndroid[0].name);
-    glDeleteTextures(1, &mAndroid[1].name);
-    return false;
-}
-
-// ---------------------------------------------------------------------------
-
-}
-; // namespace android
diff --git a/libs/surfaceflinger/BootAnimation.h b/libs/surfaceflinger/BootAnimation.h
deleted file mode 100644 (file)
index 3fb6670..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#ifndef ANDROID_BOOTANIMATION_H
-#define ANDROID_BOOTANIMATION_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/threads.h>
-#include <utils/AssetManager.h>
-
-#include <ui/ISurfaceComposer.h>
-#include <ui/SurfaceComposerClient.h>
-
-#include <EGL/egl.h>
-#include <GLES/gl.h>
-
-#include "Barrier.h"
-
-class SkBitmap;
-
-namespace android {
-
-class AssetManager;
-class EGLNativeWindowSurface;
-
-// ---------------------------------------------------------------------------
-
-class BootAnimation : public Thread
-{
-public:
-                BootAnimation(const sp<ISurfaceComposer>& composer);
-    virtual     ~BootAnimation();
-
-    const sp<SurfaceComposerClient>& session() const;
-    virtual void        requestExit();
-
-private:
-    virtual bool        threadLoop();
-    virtual status_t    readyToRun();
-    virtual void        onFirstRef();
-
-    struct Texture {
-        GLint   w;
-        GLint   h;
-        GLuint  name;
-    };
-
-    status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
-    bool android();
-
-    sp<SurfaceComposerClient>       mSession;
-    AssetManager mAssets;
-    Texture     mAndroid[2];
-    int         mWidth;
-    int         mHeight;
-    EGLDisplay  mDisplay;
-    EGLDisplay  mContext;
-    EGLDisplay  mSurface;
-    sp<Surface> mFlingerSurface;
-    sp<EGLNativeWindowSurface> mNativeWindowSurface;
-    Barrier mBarrier;
-};
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_BOOTANIMATION_H
index f14d7e9..eec645e 100644 (file)
@@ -189,9 +189,14 @@ void DisplayHardware::init(uint32_t dpy)
     
     
     char property[PROPERTY_VALUE_MAX];
-    if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
-        LOGW("ro.sf.lcd_density not defined, using 160 dpi by default.");
-        strcpy(property, "160");
+    /* Read density from build-specific ro.sf.lcd_density property
+     * except if it is overriden by qemu.sf.lcd_density.
+     */
+    if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) {
+        if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
+            LOGW("ro.sf.lcd_density not defined, using 160 dpi by default.");
+            strcpy(property, "160");
+        }
     }
     mDensity = atoi(property) * (1.0f/160.0f);
 
index e844350..397ddc8 100644 (file)
@@ -114,7 +114,9 @@ status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment,
     }
 
     if (mBitsMemory==0 || mSurface.data==0) {
-        LOGE("not enough memory for layer bitmap size=%u", size);
+        LOGE("not enough memory for layer bitmap "
+             "size=%u (w=%d, h=%d, stride=%d, format=%d)",
+             size, int(w), int(h), int(stride), int(format));
         allocator->dump("LayerBitmap");
         mSurface.data = 0;
         mSize = -1U;
index 167a59b..ef4a8ea 100644 (file)
 #include "GPUHardware/GPUHardware.h"
 
 
+/* ideally AID_GRAPHICS would be in a semi-public header
+ * or there would be a way to map a user/group name to its id
+ */
+#ifndef AID_GRAPHICS
+#define AID_GRAPHICS 1003
+#endif
+
 #define DISPLAY_COUNT       1
 
 namespace android {
@@ -184,7 +191,6 @@ SurfaceFlinger::SurfaceFlinger()
         mDebugCpu(0),
         mDebugFps(0),
         mDebugBackground(0),
-        mDebugNoBootAnimation(0),
         mSyncObject(),
         mDeplayedTransactionPending(0),
         mConsoleSignals(0),
@@ -207,14 +213,11 @@ void SurfaceFlinger::init()
     mDebugBackground = atoi(value);
     property_get("debug.sf.showfps", value, "0");
     mDebugFps = atoi(value);
-    property_get("debug.sf.nobootanimation", value, "0");
-    mDebugNoBootAnimation = atoi(value);
 
     LOGI_IF(mDebugRegion,           "showupdates enabled");
     LOGI_IF(mDebugCpu,              "showcpu enabled");
     LOGI_IF(mDebugBackground,       "showbackground enabled");
     LOGI_IF(mDebugFps,              "showfps enabled");
-    LOGI_IF(mDebugNoBootAnimation,  "boot animation disabled");
 }
 
 SurfaceFlinger::~SurfaceFlinger()
@@ -324,11 +327,8 @@ void SurfaceFlinger::bootFinished()
 {
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
-    LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
-    if (mBootAnimation != 0) {
-        mBootAnimation->requestExit();
-        mBootAnimation.clear();
-    }
+    LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );  
+    property_set("ctl.stop", "bootanim");
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -456,10 +456,10 @@ status_t SurfaceFlinger::readyToRun()
     if (mDebugCpu)
         mCpuGauge = new CPUGauge(this, ms2ns(500));
 
-    // the boot animation!
-    if (mDebugNoBootAnimation == false)
-        mBootAnimation = new BootAnimation(this);
-
+    
+    // start boot animation
+    property_set("ctl.start", "bootanim");
+    
     return NO_ERROR;
 }
 
@@ -771,10 +771,11 @@ void SurfaceFlinger::computeVisibleRegions(
             dirty.orSelf(layer->visibleRegionScreen);
             layer->contentDirty = false;
         } else {
-            // compute the exposed region
-            // dirty = what's visible now - what's wasn't covered before
-            //       = what's visible now & what's was covered before
-            dirty = visibleRegion.intersect(layer->coveredRegionScreen);            
+            /* compute the exposed region:
+             *    exposed = what's VISIBLE and NOT COVERED now 
+             *    but was COVERED before
+             */
+            dirty = (visibleRegion - coveredRegion) & layer->coveredRegionScreen;
         }
         dirty.subtractSelf(aboveOpaqueLayers);
 
@@ -783,7 +784,7 @@ void SurfaceFlinger::computeVisibleRegions(
 
         // updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer
         aboveOpaqueLayers.orSelf(opaqueRegion);
-        aboveCoveredLayers.orSelf(bounds);
+        aboveCoveredLayers.orSelf(visibleRegion);
         
         // Store the visible region is screen space
         layer->setVisibleRegion(visibleRegion);
@@ -1543,13 +1544,13 @@ status_t SurfaceFlinger::onTransact(
             // codes that require permission check
             IPCThreadState* ipc = IPCThreadState::self();
             const int pid = ipc->getCallingPid();
+            const int uid = ipc->getCallingUid();
             const int self_pid = getpid();
-            if (UNLIKELY(pid != self_pid)) {
+            if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) {
                 // we're called from a different process, do the real check
                 if (!checkCallingPermission(
                         String16("android.permission.ACCESS_SURFACE_FLINGER")))
                 {
-                    const int uid = ipc->getCallingUid();
                     LOGE("Permission Denial: "
                             "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
                     return PERMISSION_DENIED;
index e023182..15913f2 100644 (file)
@@ -36,7 +36,6 @@
 #include <private/ui/SurfaceFlingerSynchro.h>
 
 #include "Barrier.h"
-#include "BootAnimation.h"
 #include "CPUGauge.h"
 #include "Layer.h"
 #include "Tokenizer.h"
@@ -347,7 +346,6 @@ private:
                 sp<SurfaceHeapManager>      mSurfaceHeapManager;
                 sp<GPUHardwareInterface>    mGPU;
                 GLuint                      mWormholeTexName;
-                sp<BootAnimation>           mBootAnimation;
                 nsecs_t                     mBootTime;
                 
                 // Can only accessed from the main thread, these members
@@ -374,7 +372,6 @@ private:
                 int                         mDebugCpu;
                 int                         mDebugFps;
                 int                         mDebugBackground;
-                int                         mDebugNoBootAnimation;
 
                 // these are thread safe
     mutable     Barrier                     mReadyToRunBarrier;
index 238c602..5f633bd 100644 (file)
@@ -35,6 +35,8 @@
 #include <utils/MemoryHeapPmem.h>
 #include <utils/MemoryHeapBase.h>
 
+#include <EGL/eglnatives.h>
+
 #include "GPUHardware/GPUHardware.h"
 #include "SurfaceFlinger.h"
 #include "VRamHeap.h"
index f944357..7bbe38b 100644 (file)
@@ -20,13 +20,11 @@ LOCAL_SRC_FILES:= \
        LayerState.cpp \
        Overlay.cpp \
        PixelFormat.cpp \
-       Point.cpp \
        Rect.cpp \
        Region.cpp \
        Surface.cpp \
        SurfaceComposerClient.cpp \
-       SurfaceFlingerSynchro.cpp \
-       Time.cpp
+       SurfaceFlingerSynchro.cpp 
 
 LOCAL_SHARED_LIBRARIES := \
        libcorecg \
index 6613700..975594f 100644 (file)
@@ -85,20 +85,6 @@ sp<Camera> Camera::create(const sp<ICamera>& camera)
 void Camera::init()
 {
     mStatus = UNKNOWN_ERROR;
-    mShutterCallback = 0;
-    mShutterCallbackCookie = 0;
-    mRawCallback = 0;
-    mRawCallbackCookie = 0;
-    mJpegCallback = 0;
-    mJpegCallbackCookie = 0;
-    mPreviewCallback = 0;
-    mPreviewCallbackCookie = 0;
-    mRecordingCallback = 0;
-    mRecordingCallbackCookie = 0;
-    mErrorCallback = 0;
-    mErrorCallbackCookie = 0;
-    mAutoFocusCallback = 0;
-    mAutoFocusCallbackCookie = 0;
 }
 
 Camera::~Camera()
@@ -127,7 +113,6 @@ void Camera::disconnect()
 {
     LOGV("disconnect");
     if (mCamera != 0) {
-        mErrorCallback = 0;
         mCamera->disconnect();
         mCamera = 0;
     }
@@ -164,21 +149,21 @@ status_t Camera::unlock()
 status_t Camera::setPreviewDisplay(const sp<Surface>& surface)
 {
     LOGV("setPreviewDisplay");
-    if (surface == 0) {
-        LOGE("app passed NULL surface");
-        return NO_INIT;
-    }
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
-    return c->setPreviewDisplay(surface->getISurface());
+    if (surface != 0) {
+        return c->setPreviewDisplay(surface->getISurface());
+    } else {
+        LOGD("app passed NULL surface");
+        return c->setPreviewDisplay(0);
+    }
 }
 
 status_t Camera::setPreviewDisplay(const sp<ISurface>& surface)
 {
     LOGV("setPreviewDisplay");
     if (surface == 0) {
-        LOGE("app passed NULL surface");
-        return NO_INIT;
+        LOGD("app passed NULL surface");
     }
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
@@ -186,7 +171,7 @@ status_t Camera::setPreviewDisplay(const sp<ISurface>& surface)
 }
 
 
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
 status_t Camera::startPreview()
 {
     LOGV("startPreview");
@@ -285,125 +270,49 @@ String8 Camera::getParameters() const
     return params;
 }
 
-void Camera::setAutoFocusCallback(autofocus_callback cb, void *cookie)
+void Camera::setListener(const sp<CameraListener>& listener)
 {
-    LOGV("setAutoFocusCallback");
-    mAutoFocusCallback = cb;
-    mAutoFocusCallbackCookie = cookie;
-}
-
-void Camera::setShutterCallback(shutter_callback cb, void *cookie)
-{
-    LOGV("setShutterCallback");
-    mShutterCallback = cb;
-    mShutterCallbackCookie = cookie;
-}
-
-void Camera::setRawCallback(frame_callback cb, void *cookie)
-{
-    LOGV("setRawCallback");
-    mRawCallback = cb;
-    mRawCallbackCookie = cookie;
-}
-
-void Camera::setJpegCallback(frame_callback cb, void *cookie)
-{
-    LOGV("setJpegCallback");
-    mJpegCallback = cb;
-    mJpegCallbackCookie = cookie;
+    Mutex::Autolock _l(mLock);
+    mListener = listener;
 }
 
-void Camera::setPreviewCallback(frame_callback cb, void *cookie, int flag)
+void Camera::setPreviewCallbackFlags(int flag)
 {
-    LOGV("setPreviewCallback");
-    mPreviewCallback = cb;
-    mPreviewCallbackCookie = cookie;
+    LOGV("setPreviewCallbackFlags");
     sp <ICamera> c = mCamera;
     if (c == 0) return;
     mCamera->setPreviewCallbackFlag(flag);
 }
 
-void Camera::setRecordingCallback(frame_callback cb, void *cookie)
-{
-    LOGV("setRecordingCallback");
-    mRecordingCallback = cb;
-    mRecordingCallbackCookie = cookie;
-}
-
-void Camera::setErrorCallback(error_callback cb, void *cookie)
-{
-    LOGV("setErrorCallback");
-    mErrorCallback = cb;
-    mErrorCallbackCookie = cookie;
-}
-
 // callback from camera service
 void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
 {
-    switch(msgType) {
-    case CAMERA_MSG_ERROR:
-        LOGV("errorCallback");
-        if (mErrorCallback) {
-            mErrorCallback((status_t)ext1, mErrorCallbackCookie);
-        }
-        break;
-    case CAMERA_MSG_FOCUS:
-        LOGV("autoFocusCallback");
-        if (mAutoFocusCallback) {
-            mAutoFocusCallback((bool)ext1, mAutoFocusCallbackCookie);
-        }
-        break;
-    case CAMERA_MSG_SHUTTER:
-        LOGV("shutterCallback");
-        if (mShutterCallback) {
-            mShutterCallback(mShutterCallbackCookie);
-        }
-        break;
-    default:
-        LOGV("notifyCallback(%d, %d, %d)", msgType, ext1, ext2);
-        break;
+    sp<CameraListener> listener;
+    {
+        Mutex::Autolock _l(mLock);
+        listener = mListener;
+    }
+    if (listener != NULL) {
+        listener->notify(msgType, ext1, ext2);
     }
 }
 
 // callback from camera service when frame or image is ready
 void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr)
 {
-    switch(msgType) {
-    case CAMERA_MSG_PREVIEW_FRAME:
-        LOGV("previewCallback");
-        if (mPreviewCallback) {
-            mPreviewCallback(dataPtr, mPreviewCallbackCookie);
-        }
-        break;
-    case CAMERA_MSG_VIDEO_FRAME:
-        LOGV("recordingCallback");
-        if (mRecordingCallback) {
-            mRecordingCallback(dataPtr, mRecordingCallbackCookie);
-        }
-        break;
-    case CAMERA_MSG_RAW_IMAGE:
-        LOGV("rawCallback");
-        if (mRawCallback) {
-            mRawCallback(dataPtr, mRawCallbackCookie);
-        }
-        break;
-    case CAMERA_MSG_COMPRESSED_IMAGE:
-        LOGV("jpegCallback");
-        if (mJpegCallback) {
-            mJpegCallback(dataPtr, mJpegCallbackCookie);
-        }
-        break;
-    default:
-        LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
-        break;
+    sp<CameraListener> listener;
+    {
+        Mutex::Autolock _l(mLock);
+        listener = mListener;
+    }
+    if (listener != NULL) {
+        listener->postData(msgType, dataPtr);
     }
 }
 
 void Camera::binderDied(const wp<IBinder>& who) {
     LOGW("ICamera died");
-    if (mErrorCallback) {
-        mErrorCallback(DEAD_OBJECT, mErrorCallbackCookie);
-    }
+    notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, 0);
 }
 
 void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) {
index dd6a798..dab5f71 100644 (file)
 
 // ---------------------------------------------------------------------------
 
+/* ideally AID_GRAPHICS would be in a semi-public header
+ * or there would be a way to map a user/group name to its id
+ */
+#ifndef AID_GRAPHICS
+#define AID_GRAPHICS 1003
+#endif
+
 #define LIKELY( exp )       (__builtin_expect( (exp) != 0, true  ))
 #define UNLIKELY( exp )     (__builtin_expect( (exp) != 0, false ))
 
@@ -136,13 +143,13 @@ status_t BnSurfaceFlingerClient::onTransact(
      
      IPCThreadState* ipc = IPCThreadState::self();
      const int pid = ipc->getCallingPid();
-     const int self_pid    = getpid();
-     if (UNLIKELY(pid != self_pid)) {
+     const int uid = ipc->getCallingUid();
+     const int self_pid = getpid();
+     if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) {
          // we're called from a different process, do the real check
          if (!checkCallingPermission(
                  String16("android.permission.ACCESS_SURFACE_FLINGER")))
          {
-             const int uid = ipc->getCallingUid();
              LOGE("Permission Denial: "
                      "can't openGlobalTransaction pid=%d, uid=%d", pid, uid);
              return PERMISSION_DENIED;
index b236edc..59c6514 100644 (file)
@@ -129,12 +129,8 @@ OverlayRef::OverlayRef(overlay_handle_t handle, const sp<IOverlay>& channel,
 OverlayRef::~OverlayRef()
 {
     if (mOwnHandle) {
-        /* FIXME: handles should be promoted to "real" API and be handled by 
-         * the framework */
-        for (int i=0 ; i<mOverlayHandle->numFds ; i++) {
-            close(mOverlayHandle->data[i]);
-        }
-        free((void*)mOverlayHandle);
+        native_handle_close(mOverlayHandle);
+        native_handle_delete(const_cast<native_handle*>(mOverlayHandle));
     }
 }
 
@@ -147,7 +143,7 @@ sp<OverlayRef> OverlayRef::readFromParcel(const Parcel& data) {
         uint32_t f = data.readInt32();
         uint32_t ws = data.readInt32();
         uint32_t hs = data.readInt32();
-        native_handle* handle = data.readNativeHandle(NULL, NULL);
+        native_handle* handle = data.readNativeHandle();
 
         result = new OverlayRef();
         result->mOverlayHandle = handle;
@@ -169,7 +165,7 @@ status_t OverlayRef::writeToParcel(Parcel* reply, const sp<OverlayRef>& o) {
         reply->writeInt32(o->mFormat);
         reply->writeInt32(o->mWidthStride);
         reply->writeInt32(o->mHeightStride);
-        reply->writeNativeHandle(*(o->mOverlayHandle));
+        reply->writeNativeHandle(o->mOverlayHandle);
     } else {
         reply->writeStrongBinder(NULL);
     }
diff --git a/libs/ui/Point.cpp b/libs/ui/Point.cpp
deleted file mode 100644 (file)
index 438d49f..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- *  Point.cpp
- *  Android
- *
- *  Created on 11/16/2006.
- *  Copyright 2005 The Android Open Source Project
- *
- */
-
-#include <ui/Point.h>
-
index 99e68bb..66b9576 100644 (file)
@@ -1,21 +1,28 @@
 /*
- *  Rect.cpp
- *  Android
+ * Copyright (C) 2009 The Android Open Source Project
  *
- *  Created on 10/14/05.
- *  Copyright 2005 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 <ui/Rect.h>
 
 namespace android {
 
-inline int min(int a, int b) {
+static inline int min(int a, int b) {
     return (a<b) ? a : b;
 }
 
-inline int max(int a, int b) {
+static inline int max(int a, int b) {
     return (a>b) ? a : b;
 }
 
@@ -64,14 +71,16 @@ Rect& Rect::offsetBy(int x, int y)
     return *this;
 }
 
-Rect Rect::operator + (const Point& rhs) const
+const Rect Rect::operator + (const Point& rhs) const
 {
-    return Rect(left+rhs.x, top+rhs.y, right+rhs.x, bottom+rhs.y); 
+    const Rect result(left+rhs.x, top+rhs.y, right+rhs.x, bottom+rhs.y);
+    return result;
 }
 
-Rect Rect::operator - (const Point& rhs) const
+const Rect Rect::operator - (const Point& rhs) const
 {
-    return Rect(left-rhs.x, top-rhs.y, right-rhs.x, bottom-rhs.y); 
+    const Rect result(left-rhs.x, top-rhs.y, right-rhs.x, bottom-rhs.y);
+    return result;
 }
 
 bool Rect::intersect(const Rect& with, Rect* result) const
diff --git a/libs/ui/Time.cpp b/libs/ui/Time.cpp
deleted file mode 100644 (file)
index b553913..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-#include <utils/TimeUtils.h>
-#include <stdio.h>
-#include <cutils/tztime.h>
-
-namespace android {
-
-static void
-dump(const Time& t)
-{
-    #ifdef HAVE_TM_GMTOFF
-        long tm_gmtoff = t.t.tm_gmtoff;
-    #else
-        long tm_gmtoff = 0;
-    #endif
-    printf("%04d-%02d-%02d %02d:%02d:%02d (%d,%ld,%d,%d)\n",
-            t.t.tm_year+1900, t.t.tm_mon+1, t.t.tm_mday,
-            t.t.tm_hour, t.t.tm_min, t.t.tm_sec,
-            t.t.tm_isdst, tm_gmtoff, t.t.tm_wday, t.t.tm_yday);
-}
-
-Time::Time()
-{
-    t.tm_sec = 0;
-    t.tm_min = 0;
-    t.tm_hour = 0;
-    t.tm_mday = 0;
-    t.tm_mon = 0;
-    t.tm_year = 0;
-    t.tm_wday = 0;
-    t.tm_yday = 0;
-    t.tm_isdst = -1; // we don't know, so let the C library determine
-    #ifdef HAVE_TM_GMTOFF
-        t.tm_gmtoff = 0;
-    #endif
-}
-
-
-#define COMPARE_FIELD(field) do { \
-        int diff = a.t.field - b.t.field; \
-        if (diff != 0) return diff; \
-    } while(0)
-
-int
-Time::compare(Time& a, Time& b)
-{
-    if (0 == strcmp(a.timezone, b.timezone)) {
-        // if the timezones are the same, we can easily compare the two
-        // times.  Otherwise, convert to milliseconds and compare that.
-        // This requires that object be normalized.
-        COMPARE_FIELD(tm_year);
-        COMPARE_FIELD(tm_mon);
-        COMPARE_FIELD(tm_mday);
-        COMPARE_FIELD(tm_hour);
-        COMPARE_FIELD(tm_min);
-        COMPARE_FIELD(tm_sec);
-        return 0;
-    } else {
-        int64_t am = a.toMillis(false /* use isDst */);
-        int64_t bm = b.toMillis(false /* use isDst */);
-        int64_t diff = am-bm;
-        return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
-    }
-}
-
-static const int DAYS_PER_MONTH[] = {
-                        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-                    };
-
-static inline int days_this_month(int year, int month)
-{
-    int n = DAYS_PER_MONTH[month];
-    if (n != 28) {
-        return n;
-    } else {
-        int y = year;
-        return ((y%4)==0&&((y%100)!=0||(y%400)==0)) ? 29 : 28;
-    }
-}
-
-void 
-Time::switchTimezone(const char* timezone)
-{
-    time_t seconds = mktime_tz(&(this->t), this->timezone);
-    localtime_tz(&seconds, &(this->t), timezone);
-}
-
-String8 
-Time::format(const char *format, const struct strftime_locale *locale) const
-{
-    char buf[257];
-    int n = strftime_tz(buf, 257, format, &(this->t), locale);
-    if (n > 0) {
-        return String8(buf);
-    } else {
-        return String8();
-    }
-}
-
-static inline short
-tochar(int n)
-{
-    return (n >= 0 && n <= 9) ? ('0'+n) : ' ';
-}
-
-static inline short
-next_char(int *m, int k)
-{
-    int n = *m / k;
-    *m = *m % k;
-    return tochar(n);
-}
-
-void
-Time::format2445(short* buf, bool hasTime) const
-{
-    int n;
-
-    n = t.tm_year+1900;
-    buf[0] = next_char(&n, 1000);
-    buf[1] = next_char(&n, 100);
-    buf[2] = next_char(&n, 10);
-    buf[3] = tochar(n);
-
-    n = t.tm_mon+1;
-    buf[4] = next_char(&n, 10);
-    buf[5] = tochar(n);
-
-    n = t.tm_mday;
-    buf[6] = next_char(&n, 10);
-    buf[7] = tochar(n);
-
-    if (hasTime) {
-      buf[8] = 'T';
-
-      n = t.tm_hour;
-      buf[9] = next_char(&n, 10);
-      buf[10] = tochar(n);
-      
-      n = t.tm_min;
-      buf[11] = next_char(&n, 10);
-      buf[12] = tochar(n);
-      
-      n = t.tm_sec;
-      buf[13] = next_char(&n, 10);
-      buf[14] = tochar(n);
-      bool inUtc = strcmp("UTC", timezone) == 0;
-      if (inUtc) {
-          buf[15] = 'Z';
-      }
-    }
-}
-
-String8 
-Time::toString() const
-{
-    String8 str;
-    char* s = str.lockBuffer(150);
-    #ifdef HAVE_TM_GMTOFF
-        long tm_gmtoff = t.tm_gmtoff;
-    #else
-        long tm_gmtoff = 0;
-    #endif
-    sprintf(s, "%04d%02d%02dT%02d%02d%02d%s(%d,%d,%ld,%d,%d)", 
-            t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min,
-            t.tm_sec, timezone, t.tm_wday, t.tm_yday, tm_gmtoff, t.tm_isdst,
-            (int)(((Time*)this)->toMillis(false /* use isDst */)/1000));
-    str.unlockBuffer();
-    return str;
-}
-
-void 
-Time::setToNow()
-{
-    time_t seconds;
-    time(&seconds);
-    localtime_tz(&seconds, &(this->t), this->timezone);
-}
-
-int64_t 
-Time::toMillis(bool ignoreDst)
-{
-    if (ignoreDst) {
-        this->t.tm_isdst = -1;
-    }
-    int64_t r = mktime_tz(&(this->t), this->timezone);
-    if (r == -1)
-        return -1;
-    return r * 1000;
-}
-
-void 
-Time::set(int64_t millis)
-{
-    time_t seconds = millis / 1000;
-    localtime_tz(&seconds, &(this->t), this->timezone);
-}
-
-}; // namespace android
-
index f9fb780..9bdd64a 100644 (file)
@@ -117,7 +117,8 @@ LOCAL_SRC_FILES:= \
        IPermissionController.cpp \
        IServiceManager.cpp \
        Unicode.cpp \
-       file_backup_helper.cpp
+    BackupData.cpp \
+       BackupHelpers.cpp
 
 ifeq ($(TARGET_SIMULATOR),true)
 LOCAL_SRC_FILES += $(hostSources)
index 91203dd..23cb72d 100644 (file)
@@ -582,11 +582,14 @@ const void* _FileAsset::ensureAlignment(FileMap* map)
     if ((((size_t)data)&0x3) == 0) {
         // We can return this directly if it is aligned on a word
         // boundary.
+        LOGV("Returning aligned FileAsset %p (%s).", this,
+                getAssetSource());
         return data;
     }
     // If not aligned on a word boundary, then we need to copy it into
     // our own buffer.
-    LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength);
+    LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
+            getAssetSource(), (int)mLength);
     unsigned char* buf = new unsigned char[mLength];
     if (buf == NULL) {
         LOGE("alloc of %ld bytes failed\n", (long) mLength);
index 447b801..5a05e6a 100644 (file)
@@ -395,21 +395,41 @@ const ResTable* AssetManager::getResTable(bool required) const
     const size_t N = mAssetPaths.size();
     for (size_t i=0; i<N; i++) {
         Asset* ass = NULL;
+        ResTable* sharedRes = NULL;
         bool shared = true;
         const asset_path& ap = mAssetPaths.itemAt(i);
         LOGV("Looking for resource asset in '%s'\n", ap.path.string());
         if (ap.type != kFileTypeDirectory) {
-            ass = const_cast<AssetManager*>(this)->
-                mZipSet.getZipResourceTable(ap.path);
-            if (ass == NULL) {
-                LOGV("loading resource table %s\n", ap.path.string());
+            if (i == 0) {
+                // The first item is typically the framework resources,
+                // which we want to avoid parsing every time.
+                sharedRes = const_cast<AssetManager*>(this)->
+                    mZipSet.getZipResourceTable(ap.path);
+            }
+            if (sharedRes == NULL) {
                 ass = const_cast<AssetManager*>(this)->
-                    openNonAssetInPathLocked("resources.arsc",
-                                             Asset::ACCESS_BUFFER,
-                                             ap);
-                if (ass != NULL && ass != kExcludedAsset) {
+                    mZipSet.getZipResourceTableAsset(ap.path);
+                if (ass == NULL) {
+                    LOGV("loading resource table %s\n", ap.path.string());
                     ass = const_cast<AssetManager*>(this)->
-                        mZipSet.setZipResourceTable(ap.path, ass);
+                        openNonAssetInPathLocked("resources.arsc",
+                                                 Asset::ACCESS_BUFFER,
+                                                 ap);
+                    if (ass != NULL && ass != kExcludedAsset) {
+                        ass = const_cast<AssetManager*>(this)->
+                            mZipSet.setZipResourceTableAsset(ap.path, ass);
+                    }
+                }
+                
+                if (i == 0 && ass != NULL) {
+                    // If this is the first resource table in the asset
+                    // manager, then we are going to cache it so that we
+                    // can quickly copy it out for others.
+                    LOGV("Creating shared resources for %s", ap.path.string());
+                    sharedRes = new ResTable();
+                    sharedRes->add(ass, (void*)(i+1), false);
+                    sharedRes = const_cast<AssetManager*>(this)->
+                        mZipSet.setZipResourceTable(ap.path, sharedRes);
                 }
             }
         } else {
@@ -420,13 +440,19 @@ const ResTable* AssetManager::getResTable(bool required) const
                                          ap);
             shared = false;
         }
-        if (ass != NULL && ass != kExcludedAsset) {
+        if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
             if (rt == NULL) {
                 mResources = rt = new ResTable();
                 updateResourceParamsLocked();
             }
             LOGV("Installing resource asset %p in to table %p\n", ass, mResources);
-            rt->add(ass, (void*)(i+1), !shared);
+            if (sharedRes != NULL) {
+                LOGV("Copying existing resources for %s", ap.path.string());
+                rt->add(sharedRes);
+            } else {
+                LOGV("Parsing resources for %s", ap.path.string());
+                rt->add(ass, (void*)(i+1), !shared);
+            }
 
             if (!shared) {
                 delete ass;
@@ -901,6 +927,60 @@ AssetDir* AssetManager::openDir(const char* dirName)
 }
 
 /*
+ * Open a directory in the non-asset namespace.
+ *
+ * An "asset directory" is simply the combination of all files in all
+ * locations, with ".gz" stripped for loose files.  With app, locale, and
+ * vendor defined, we have 8 directories and 2 Zip archives to scan.
+ *
+ * Pass in "" for the root dir.
+ */
+AssetDir* AssetManager::openNonAssetDir(void* cookie, const char* dirName)
+{
+    AutoMutex _l(mLock);
+
+    AssetDir* pDir = NULL;
+    SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL;
+
+    LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
+    assert(dirName != NULL);
+
+    //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
+
+    if (mCacheMode != CACHE_OFF && !mCacheValid)
+        loadFileNameCacheLocked();
+
+    pDir = new AssetDir;
+
+    pMergedInfo = new SortedVector<AssetDir::FileInfo>;
+
+    const size_t which = ((size_t)cookie)-1;
+
+    if (which < mAssetPaths.size()) {
+        const asset_path& ap = mAssetPaths.itemAt(which);
+        if (ap.type == kFileTypeRegular) {
+            LOGV("Adding directory %s from zip %s", dirName, ap.path.string());
+            scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName);
+        } else {
+            LOGV("Adding directory %s from dir %s", dirName, ap.path.string());
+            scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName);
+        }
+    }
+
+#if 0
+    printf("FILE LIST:\n");
+    for (i = 0; i < (size_t) pMergedInfo->size(); i++) {
+        printf(" %d: (%d) '%s'\n", i,
+            pMergedInfo->itemAt(i).getFileType(),
+            (const char*) pMergedInfo->itemAt(i).getFileName());
+    }
+#endif
+
+    pDir->setFileList(pMergedInfo);
+    return pDir;
+}
+
+/*
  * Scan the contents of the specified directory and merge them into the
  * "pMergedInfo" vector, removing previous entries if we find "exclude"
  * directives.
@@ -1143,6 +1223,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg
             LOGE("ARGH: name too long?\n");
             continue;
         }
+        //printf("Comparing %s in %s?\n", nameBuf, dirName.string());
         if (dirNameLen == 0 ||
             (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 &&
              nameBuf[dirNameLen] == '/'))
@@ -1165,7 +1246,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg
                     createZipSourceNameLocked(zipName, dirName, info.getFileName()));
 
                 contents.add(info);
-                //printf("FOUND: file '%s'\n", (const char*) info.mFileName);
+                //printf("FOUND: file '%s'\n", info.getFileName().string());
             } else {
                 /* this is a subdir; add it if we don't already have it*/
                 String8 subdirName(cp, nextSlash - cp);
@@ -1181,7 +1262,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg
                     dirs.add(subdirName);
                 }
 
-                //printf("FOUND: dir '%s'\n", (const char*) subdirName);
+                //printf("FOUND: dir '%s'\n", subdirName.string());
             }
         }
     }
@@ -1455,7 +1536,8 @@ Mutex AssetManager::SharedZip::gLock;
 DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen;
 
 AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
-    : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL)
+    : mPath(path), mZipFile(NULL), mModWhen(modWhen),
+      mResourceTableAsset(NULL), mResourceTable(NULL)
 {
     //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
     mZipFile = new ZipFileRO;
@@ -1508,6 +1590,25 @@ Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset)
     return mResourceTableAsset;
 }
 
+ResTable* AssetManager::SharedZip::getResourceTable()
+{
+    LOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable);
+    return mResourceTable;
+}
+
+ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res)
+{
+    {
+        AutoMutex _l(gLock);
+        if (mResourceTable == NULL) {
+            mResourceTable = res;
+            return res;
+        }
+    }
+    delete res;
+    return mResourceTable;
+}
+
 bool AssetManager::SharedZip::isUpToDate()
 {
     time_t modWhen = getFileModDate(mPath.string());
@@ -1517,6 +1618,9 @@ bool AssetManager::SharedZip::isUpToDate()
 AssetManager::SharedZip::~SharedZip()
 {
     //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath);
+    if (mResourceTable != NULL) {
+        delete mResourceTable;
+    }
     if (mResourceTableAsset != NULL) {
         delete mResourceTableAsset;
     }
@@ -1572,7 +1676,7 @@ ZipFileRO* AssetManager::ZipSet::getZip(const String8& path)
     return zip->getZip();
 }
 
-Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path)
+Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path)
 {
     int idx = getIndex(path);
     sp<SharedZip> zip = mZipFile[idx];
@@ -1583,7 +1687,7 @@ Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path)
     return zip->getResourceTableAsset();
 }
 
-Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path,
+Asset* AssetManager::ZipSet::setZipResourceTableAsset(const String8& path,
                                                  Asset* asset)
 {
     int idx = getIndex(path);
@@ -1592,6 +1696,26 @@ Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path,
     return zip->setResourceTableAsset(asset);
 }
 
+ResTable* AssetManager::ZipSet::getZipResourceTable(const String8& path)
+{
+    int idx = getIndex(path);
+    sp<SharedZip> zip = mZipFile[idx];
+    if (zip == NULL) {
+        zip = SharedZip::get(path);
+        mZipFile.editItemAt(idx) = zip;
+    }
+    return zip->getResourceTable();
+}
+
+ResTable* AssetManager::ZipSet::setZipResourceTable(const String8& path,
+                                                    ResTable* res)
+{
+    int idx = getIndex(path);
+    sp<SharedZip> zip = mZipFile[idx];
+    // doesn't make sense to call before previously accessing.
+    return zip->setResourceTable(res);
+}
+
 /*
  * Generate the partial pathname for the specified archive.  The caller
  * gets to prepend the asset root directory.
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
new file mode 100644 (file)
index 0000000..cce754a
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#define LOG_TAG "backup_data"
+
+#include <utils/BackupHelpers.h>
+#include <utils/ByteOrder.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+
+namespace android {
+
+/*
+ * File Format (v1):
+ *
+ * All ints are stored little-endian.
+ *
+ *  - An app_header_v1 struct.
+ *  - The name of the package, utf-8, null terminated, padded to 4-byte boundary.
+ *  - A sequence of zero or more key/value paires (entities), each with
+ *      - A entity_header_v1 struct
+ *      - The key, utf-8, null terminated, padded to 4-byte boundary.
+ *      - The value, padded to 4 byte boundary
+ */
+
+const static int ROUND_UP[4] = { 0, 3, 2, 1 };
+
+static inline size_t
+round_up(size_t n)
+{
+    return n + ROUND_UP[n % 4];
+}
+
+static inline size_t
+padding_extra(size_t n)
+{
+    return ROUND_UP[n % 4];
+}
+
+BackupDataWriter::BackupDataWriter(int fd)
+    :m_fd(fd),
+     m_status(NO_ERROR),
+     m_pos(0),
+     m_entityCount(0)
+{
+}
+
+BackupDataWriter::~BackupDataWriter()
+{
+}
+
+// Pad out anything they've previously written to the next 4 byte boundary.
+status_t
+BackupDataWriter::write_padding_for(int n)
+{
+    ssize_t amt;
+    ssize_t paddingSize;
+
+    paddingSize = padding_extra(n);
+    if (paddingSize > 0) {
+        uint32_t padding = 0xbcbcbcbc;
+        amt = write(m_fd, &padding, paddingSize);
+        if (amt != paddingSize) {
+            m_status = errno;
+            return m_status;
+        }
+        m_pos += amt;
+    }
+    return NO_ERROR;
+}
+
+status_t
+BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+
+    ssize_t amt;
+
+    amt = write_padding_for(m_pos);
+    if (amt != 0) {
+        return amt;
+    }
+
+    String8 k;
+    if (m_keyPrefix.length() > 0) {
+        k = m_keyPrefix;
+        k += ":";
+        k += key;
+    } else {
+        k = key;
+    }
+    if (true) {
+        LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(),
+                dataSize);
+    }
+
+    entity_header_v1 header;
+    ssize_t keyLen;
+
+    keyLen = k.length();
+
+    header.type = tolel(BACKUP_HEADER_ENTITY_V1);
+    header.keyLen = tolel(keyLen);
+    header.dataSize = tolel(dataSize);
+
+    amt = write(m_fd, &header, sizeof(entity_header_v1));
+    if (amt != sizeof(entity_header_v1)) {
+        m_status = errno;
+        return m_status;
+    }
+    m_pos += amt;
+
+    amt = write(m_fd, k.string(), keyLen+1);
+    if (amt != keyLen+1) {
+        m_status = errno;
+        return m_status;
+    }
+    m_pos += amt;
+
+    amt = write_padding_for(keyLen+1);
+
+    m_entityCount++;
+
+    return amt;
+}
+
+status_t
+BackupDataWriter::WriteEntityData(const void* data, size_t size)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+
+    // We don't write padding here, because they're allowed to call this several
+    // times with smaller buffers.  We write it at the end of WriteEntityHeader
+    // instead.
+    ssize_t amt = write(m_fd, data, size);
+    if (amt != (ssize_t)size) {
+        m_status = errno;
+        return m_status;
+    }
+    m_pos += amt;
+    return NO_ERROR;
+}
+
+void
+BackupDataWriter::SetKeyPrefix(const String8& keyPrefix)
+{
+    m_keyPrefix = keyPrefix;
+}
+
+
+BackupDataReader::BackupDataReader(int fd)
+    :m_fd(fd),
+     m_done(false),
+     m_status(NO_ERROR),
+     m_pos(0),
+     m_entityCount(0)
+{
+    memset(&m_header, 0, sizeof(m_header));
+}
+
+BackupDataReader::~BackupDataReader()
+{
+}
+
+status_t
+BackupDataReader::Status()
+{
+    return m_status;
+}
+
+#define CHECK_SIZE(actual, expected) \
+    do { \
+        if ((actual) != (expected)) { \
+            if ((actual) == 0) { \
+                m_status = EIO; \
+            } else { \
+                m_status = errno; \
+            } \
+            return m_status; \
+        } \
+    } while(0)
+#define SKIP_PADDING() \
+    do { \
+        status_t err = skip_padding(); \
+        if (err != NO_ERROR) { \
+            m_status = err; \
+            return err; \
+        } \
+    } while(0)
+
+status_t
+BackupDataReader::ReadNextHeader(bool* done, int* type)
+{
+    *done = m_done;
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+
+    int amt;
+
+    // No error checking here, in case we're at the end of the stream.  Just let read() fail.
+    skip_padding();
+    amt = read(m_fd, &m_header, sizeof(m_header));
+    *done = m_done = (amt == 0);
+    CHECK_SIZE(amt, sizeof(m_header));
+    m_pos += sizeof(m_header);
+    if (type) {
+        *type = m_header.type;
+    }
+
+    // validate and fix up the fields.
+    m_header.type = fromlel(m_header.type);
+    switch (m_header.type)
+    {
+        case BACKUP_HEADER_ENTITY_V1:
+        {
+            m_header.entity.keyLen = fromlel(m_header.entity.keyLen);
+            if (m_header.entity.keyLen <= 0) {
+                LOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos,
+                        (int)m_header.entity.keyLen);
+                m_status = EINVAL;
+            }
+            m_header.entity.dataSize = fromlel(m_header.entity.dataSize);
+            m_entityCount++;
+
+            // read the rest of the header (filename)
+            size_t size = m_header.entity.keyLen;
+            char* buf = m_key.lockBuffer(size);
+            if (buf == NULL) {
+                m_status = ENOMEM;
+                return m_status;
+            }
+            int amt = read(m_fd, buf, size+1);
+            CHECK_SIZE(amt, (int)size+1);
+            m_key.unlockBuffer(size);
+            m_pos += size+1;
+            SKIP_PADDING();
+            m_dataEndPos = m_pos + m_header.entity.dataSize;
+
+            break;
+        }
+        default:
+            LOGD("Chunk header at %d has invalid type: 0x%08x", (int)m_pos, (int)m_header.type);
+            m_status = EINVAL;
+    }
+    
+    return m_status;
+}
+
+bool
+BackupDataReader::HasEntities()
+{
+    return m_status == NO_ERROR && m_header.type == BACKUP_HEADER_ENTITY_V1;
+}
+
+status_t
+BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+    if (m_header.type != BACKUP_HEADER_ENTITY_V1) {
+        return EINVAL;
+    }
+    *key = m_key;
+    *dataSize = m_header.entity.dataSize;
+    return NO_ERROR;
+}
+
+status_t
+BackupDataReader::SkipEntityData()
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+    if (m_header.type != BACKUP_HEADER_ENTITY_V1) {
+        return EINVAL;
+    }
+    if (m_header.entity.dataSize > 0) {
+        int pos = lseek(m_fd, m_dataEndPos, SEEK_SET);
+        return pos == -1 ? (int)errno : (int)NO_ERROR;
+    } else {
+        return NO_ERROR;
+    }
+}
+
+ssize_t
+BackupDataReader::ReadEntityData(void* data, size_t size)
+{
+    if (m_status != NO_ERROR) {
+        return -1;
+    }
+    int remaining = m_dataEndPos - m_pos;
+    //LOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n",
+    //        size, m_pos, m_dataEndPos, remaining);
+    if (remaining <= 0) {
+        return 0;
+    }
+    if (((int)size) > remaining) {
+        size = remaining;
+    }
+    //LOGD("   reading %d bytes", size);
+    int amt = read(m_fd, data, size);
+    if (amt < 0) {
+        m_status = errno;
+        return -1;
+    }
+    m_pos += amt;
+    return amt;
+}
+
+status_t
+BackupDataReader::skip_padding()
+{
+    ssize_t amt;
+    ssize_t paddingSize;
+
+    paddingSize = padding_extra(m_pos);
+    if (paddingSize > 0) {
+        uint32_t padding;
+        amt = read(m_fd, &padding, paddingSize);
+        CHECK_SIZE(amt, paddingSize);
+        m_pos += amt;
+    }
+    return NO_ERROR;
+}
+
+
+} // namespace android
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
new file mode 100644 (file)
index 0000000..4ad9b51
--- /dev/null
@@ -0,0 +1,1314 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#define LOG_TAG "file_backup_helper"
+
+#include <utils/BackupHelpers.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/ByteOrder.h>
+#include <utils/String8.h>
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/time.h>  // for utimes
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <zlib.h>
+
+#include <cutils/log.h>
+
+namespace android {
+
+#define MAGIC0 0x70616e53 // Snap
+#define MAGIC1 0x656c6946 // File
+
+/*
+ * File entity data format (v1):
+ *
+ *   - 4-byte version number of the metadata, little endian (0x00000001 for v1)
+ *   - 12 bytes of metadata
+ *   - the file data itself
+ *
+ * i.e. a 16-byte metadata header followed by the raw file data.  If the
+ * restore code does not recognize the metadata version, it can still
+ * interpret the file data itself correctly.
+ *
+ * file_metadata_v1:
+ *
+ *   - 4 byte version number === 0x00000001 (little endian)
+ *   - 4-byte access mode (little-endian)
+ *   - undefined (8 bytes)
+ */
+
+struct file_metadata_v1 {
+    int version;
+    int mode;
+    int undefined_1;
+    int undefined_2;
+};
+
+const static int CURRENT_METADATA_VERSION = 1;
+
+#if 1
+#define LOGP(f, x...)
+#else
+#if TEST_BACKUP_HELPERS
+#define LOGP(f, x...) printf(f "\n", x)
+#else
+#define LOGP(x...) LOGD(x)
+#endif
+#endif
+
+const static int ROUND_UP[4] = { 0, 3, 2, 1 };
+
+static inline int
+round_up(int n)
+{
+    return n + ROUND_UP[n % 4];
+}
+
+static int
+read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot)
+{
+    int bytesRead = 0;
+    int amt;
+    SnapshotHeader header;
+
+    amt = read(fd, &header, sizeof(header));
+    if (amt != sizeof(header)) {
+        return errno;
+    }
+    bytesRead += amt;
+
+    if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) {
+        LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1);
+        return 1;
+    }
+
+    for (int i=0; i<header.fileCount; i++) {
+        FileState file;
+        char filenameBuf[128];
+
+        amt = read(fd, &file, sizeof(FileState));
+        if (amt != sizeof(FileState)) {
+            LOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead);
+            return 1;
+        }
+        bytesRead += amt;
+
+        // filename is not NULL terminated, but it is padded
+        int nameBufSize = round_up(file.nameLen);
+        char* filename = nameBufSize <= (int)sizeof(filenameBuf)
+                ? filenameBuf
+                : (char*)malloc(nameBufSize);
+        amt = read(fd, filename, nameBufSize);
+        if (amt == nameBufSize) {
+            snapshot->add(String8(filename, file.nameLen), file);
+        }
+        bytesRead += amt;
+        if (filename != filenameBuf) {
+            free(filename);
+        }
+        if (amt != nameBufSize) {
+            LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead);
+            return 1;
+        }
+    }
+
+    if (header.totalSize != bytesRead) {
+        LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n",
+                header.totalSize, bytesRead);
+        return 1;
+    }
+
+    return 0;
+}
+
+static int
+write_snapshot_file(int fd, const KeyedVector<String8,FileRec>& snapshot)
+{
+    int fileCount = 0;
+    int bytesWritten = sizeof(SnapshotHeader);
+    // preflight size
+    const int N = snapshot.size();
+    for (int i=0; i<N; i++) {
+        const FileRec& g = snapshot.valueAt(i);
+        if (!g.deleted) {
+            const String8& name = snapshot.keyAt(i);
+            bytesWritten += sizeof(FileState) + round_up(name.length());
+            fileCount++;
+        }
+    }
+
+    LOGP("write_snapshot_file fd=%d\n", fd);
+
+    int amt;
+    SnapshotHeader header = { MAGIC0, fileCount, MAGIC1, bytesWritten };
+
+    amt = write(fd, &header, sizeof(header));
+    if (amt != sizeof(header)) {
+        LOGW("write_snapshot_file error writing header %s", strerror(errno));
+        return errno;
+    }
+
+    for (int i=0; i<N; i++) {
+        FileRec r = snapshot.valueAt(i);
+        if (!r.deleted) {
+            const String8& name = snapshot.keyAt(i);
+            int nameLen = r.s.nameLen = name.length();
+
+            amt = write(fd, &r.s, sizeof(FileState));
+            if (amt != sizeof(FileState)) {
+                LOGW("write_snapshot_file error writing header %s", strerror(errno));
+                return 1;
+            }
+
+            // filename is not NULL terminated, but it is padded
+            amt = write(fd, name.string(), nameLen);
+            if (amt != nameLen) {
+                LOGW("write_snapshot_file error writing filename %s", strerror(errno));
+                return 1;
+            }
+            int paddingLen = ROUND_UP[nameLen % 4];
+            if (paddingLen != 0) {
+                int padding = 0xabababab;
+                amt = write(fd, &padding, paddingLen);
+                if (amt != paddingLen) {
+                    LOGW("write_snapshot_file error writing %d bytes of filename padding %s",
+                            paddingLen, strerror(errno));
+                    return 1;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int
+write_delete_file(BackupDataWriter* dataStream, const String8& key)
+{
+    LOGP("write_delete_file %s\n", key.string());
+    return dataStream->WriteEntityHeader(key, -1);
+}
+
+static int
+write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key,
+        char const* realFilename)
+{
+    LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.string(), mode);
+
+    const int bufsize = 4*1024;
+    int err;
+    int amt;
+    int fileSize;
+    int bytesLeft;
+    file_metadata_v1 metadata;
+
+    char* buf = (char*)malloc(bufsize);
+    int crc = crc32(0L, Z_NULL, 0);
+
+
+    fileSize = lseek(fd, 0, SEEK_END);
+    lseek(fd, 0, SEEK_SET);
+
+    if (sizeof(metadata) != 16) {
+        LOGE("ERROR: metadata block is the wrong size!");
+    }
+
+    bytesLeft = fileSize + sizeof(metadata);
+    err = dataStream->WriteEntityHeader(key, bytesLeft);
+    if (err != 0) {
+        free(buf);
+        return err;
+    }
+
+    // store the file metadata first
+    metadata.version = tolel(CURRENT_METADATA_VERSION);
+    metadata.mode = tolel(mode);
+    metadata.undefined_1 = metadata.undefined_2 = 0;
+    err = dataStream->WriteEntityData(&metadata, sizeof(metadata));
+    if (err != 0) {
+        free(buf);
+        return err;
+    }
+    bytesLeft -= sizeof(metadata); // bytesLeft should == fileSize now
+
+    // now store the file content
+    while ((amt = read(fd, buf, bufsize)) != 0 && bytesLeft > 0) {
+        bytesLeft -= amt;
+        if (bytesLeft < 0) {
+            amt += bytesLeft; // Plus a negative is minus.  Don't write more than we promised.
+        }
+        err = dataStream->WriteEntityData(buf, amt);
+        if (err != 0) {
+            free(buf);
+            return err;
+        }
+    }
+    if (bytesLeft != 0) {
+        if (bytesLeft > 0) {
+            // Pad out the space we promised in the buffer.  We can't corrupt the buffer,
+            // even though the data we're sending is probably bad.
+            memset(buf, 0, bufsize);
+            while (bytesLeft > 0) {
+                amt = bytesLeft < bufsize ? bytesLeft : bufsize;
+                bytesLeft -= amt;
+                err = dataStream->WriteEntityData(buf, amt);
+                if (err != 0) {
+                    free(buf);
+                    return err;
+                }
+            }
+        }
+        LOGE("write_update_file size mismatch for %s. expected=%d actual=%d."
+                " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft);
+    }
+
+    free(buf);
+    return NO_ERROR;
+}
+
+static int
+write_update_file(BackupDataWriter* dataStream, const String8& key, char const* realFilename)
+{
+    int err;
+    struct stat st;
+
+    err = stat(realFilename, &st);
+    if (err < 0) {
+        return errno;
+    }
+
+    int fd = open(realFilename, O_RDONLY);
+    if (fd == -1) {
+        return errno;
+    }
+
+    err = write_update_file(dataStream, fd, st.st_mode, key, realFilename);
+    close(fd);
+    return err;
+}
+
+static int
+compute_crc32(int fd)
+{
+    const int bufsize = 4*1024;
+    int amt;
+
+    char* buf = (char*)malloc(bufsize);
+    int crc = crc32(0L, Z_NULL, 0);
+
+    lseek(fd, 0, SEEK_SET);
+
+    while ((amt = read(fd, buf, bufsize)) != 0) {
+        crc = crc32(crc, (Bytef*)buf, amt);
+    }
+
+    free(buf);
+    return crc;
+}
+
+int
+back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
+        char const* const* files, char const* const* keys, int fileCount)
+{
+    int err;
+    KeyedVector<String8,FileState> oldSnapshot;
+    KeyedVector<String8,FileRec> newSnapshot;
+
+    if (oldSnapshotFD != -1) {
+        err = read_snapshot_file(oldSnapshotFD, &oldSnapshot);
+        if (err != 0) {
+            // On an error, treat this as a full backup.
+            oldSnapshot.clear();
+        }
+    }
+
+    for (int i=0; i<fileCount; i++) {
+        String8 key(keys[i]);
+        FileRec r;
+        char const* file = files[i];
+        r.file = file;
+        struct stat st;
+
+        err = stat(file, &st);
+        if (err != 0) {
+            r.deleted = true;
+        } else {
+            r.deleted = false;
+            r.s.modTime_sec = st.st_mtime;
+            r.s.modTime_nsec = 0; // workaround sim breakage
+            //r.s.modTime_nsec = st.st_mtime_nsec;
+            r.s.mode = st.st_mode;
+            r.s.size = st.st_size;
+            // we compute the crc32 later down below, when we already have the file open.
+
+            if (newSnapshot.indexOfKey(key) >= 0) {
+                LOGP("back_up_files key already in use '%s'", key.string());
+                return -1;
+            }
+        }
+        newSnapshot.add(key, r);
+    }
+
+    int n = 0;
+    int N = oldSnapshot.size();
+    int m = 0;
+
+    while (n<N && m<fileCount) {
+        const String8& p = oldSnapshot.keyAt(n);
+        const String8& q = newSnapshot.keyAt(m);
+        FileRec& g = newSnapshot.editValueAt(m);
+        int cmp = p.compare(q);
+        if (g.deleted || cmp < 0) {
+            // file removed
+            LOGP("file removed: %s", p.string());
+            g.deleted = true; // They didn't mention the file, but we noticed that it's gone.
+            dataStream->WriteEntityHeader(p, -1);
+            n++;
+        }
+        else if (cmp > 0) {
+            // file added
+            LOGP("file added: %s", g.file.string());
+            write_update_file(dataStream, q, g.file.string());
+            m++;
+        }
+        else {
+            // both files exist, check them
+            const FileState& f = oldSnapshot.valueAt(n);
+
+            int fd = open(g.file.string(), O_RDONLY);
+            if (fd < 0) {
+                // We can't open the file.  Don't report it as a delete either.  Let the
+                // server keep the old version.  Maybe they'll be able to deal with it
+                // on restore.
+                LOGP("Unable to open file %s - skipping", g.file.string());
+            } else {
+                g.s.crc32 = compute_crc32(fd);
+
+                LOGP("%s", q.string());
+                LOGP("  new: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
+                        f.modTime_sec, f.modTime_nsec, f.mode, f.size, f.crc32);
+                LOGP("  old: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
+                        g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32);
+                if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec
+                        || f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) {
+                    write_update_file(dataStream, fd, g.s.mode, p, g.file.string());
+                }
+
+                close(fd);
+            }
+            n++;
+            m++;
+        }
+    }
+
+    // these were deleted
+    while (n<N) {
+        dataStream->WriteEntityHeader(oldSnapshot.keyAt(n), -1);
+        n++;
+    }
+
+    // these were added
+    while (m<fileCount) {
+        const String8& q = newSnapshot.keyAt(m);
+        FileRec& g = newSnapshot.editValueAt(m);
+        write_update_file(dataStream, q, g.file.string());
+        m++;
+    }
+
+    err = write_snapshot_file(newSnapshotFD, newSnapshot);
+
+    return 0;
+}
+
+#define RESTORE_BUF_SIZE (8*1024)
+
+RestoreHelperBase::RestoreHelperBase()
+{
+    m_buf = malloc(RESTORE_BUF_SIZE);
+    m_loggedUnknownMetadata = false;
+}
+
+RestoreHelperBase::~RestoreHelperBase()
+{
+    free(m_buf);
+}
+
+status_t
+RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in)
+{
+    ssize_t err;
+    size_t dataSize;
+    String8 key;
+    int fd;
+    void* buf = m_buf;
+    ssize_t amt;
+    int mode;
+    int crc;
+    struct stat st;
+    FileRec r;
+
+    err = in->ReadEntityHeader(&key, &dataSize);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    // Get the metadata block off the head of the file entity and use that to
+    // set up the output file
+    file_metadata_v1 metadata;
+    amt = in->ReadEntityData(&metadata, sizeof(metadata));
+    if (amt != sizeof(metadata)) {
+        LOGW("Could not read metadata for %s -- %ld / %s", filename.string(),
+                (long)amt, strerror(errno));
+        return EIO;
+    }
+    metadata.version = fromlel(metadata.version);
+    metadata.mode = fromlel(metadata.mode);
+    if (metadata.version > CURRENT_METADATA_VERSION) {
+        if (!m_loggedUnknownMetadata) {
+            m_loggedUnknownMetadata = true;
+            LOGW("Restoring file with unsupported metadata version %d (currently %d)",
+                    metadata.version, CURRENT_METADATA_VERSION);
+        }
+    }
+    mode = metadata.mode;
+
+    // Write the file and compute the crc
+    crc = crc32(0L, Z_NULL, 0);
+    fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode);
+    if (fd == -1) {
+        LOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
+        return errno;
+    }
+    
+    while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
+        err = write(fd, buf, amt);
+        if (err != amt) {
+            close(fd);
+            LOGW("Error '%s' writing '%s'", strerror(errno), filename.string());
+            return errno;
+        }
+        crc = crc32(crc, (Bytef*)buf, amt);
+    }
+
+    close(fd);
+
+    // Record for the snapshot
+    err = stat(filename.string(), &st);
+    if (err != 0) {
+        LOGW("Error stating file that we just created %s", filename.string());
+        return errno;
+    }
+
+    r.file = filename;
+    r.deleted = false;
+    r.s.modTime_sec = st.st_mtime;
+    r.s.modTime_nsec = 0; // workaround sim breakage
+    //r.s.modTime_nsec = st.st_mtime_nsec;
+    r.s.mode = st.st_mode;
+    r.s.size = st.st_size;
+    r.s.crc32 = crc;
+
+    m_files.add(key, r);
+
+    return NO_ERROR;
+}
+
+status_t
+RestoreHelperBase::WriteSnapshot(int fd)
+{
+    return write_snapshot_file(fd, m_files);;
+}
+
+#if TEST_BACKUP_HELPERS
+
+#define SCRATCH_DIR "/data/backup_helper_test/"
+
+static int
+write_text_file(const char* path, const char* data)
+{
+    int amt;
+    int fd;
+    int len;
+
+    fd = creat(path, 0666);
+    if (fd == -1) {
+        fprintf(stderr, "creat %s failed\n", path);
+        return errno;
+    }
+
+    len = strlen(data);
+    amt = write(fd, data, len);
+    if (amt != len) {
+        fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path);
+        return errno;
+    }
+
+    close(fd);
+
+    return 0;
+}
+
+static int
+compare_file(const char* path, const unsigned char* data, int len)
+{
+    int fd;
+    int amt;
+
+    fd = open(path, O_RDONLY);
+    if (fd == -1) {
+        fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path);
+        return errno;
+    }
+
+    unsigned char* contents = (unsigned char*)malloc(len);
+    if (contents == NULL) {
+        fprintf(stderr, "malloc(%d) failed\n", len);
+        return ENOMEM;
+    }
+
+    bool sizesMatch = true;
+    amt = lseek(fd, 0, SEEK_END);
+    if (amt != len) {
+        fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt);
+        sizesMatch = false;
+    }
+    lseek(fd, 0, SEEK_SET);
+
+    int readLen = amt < len ? amt : len;
+    amt = read(fd, contents, readLen);
+    if (amt != readLen) {
+        fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt);
+    }
+
+    bool contentsMatch = true;
+    for (int i=0; i<readLen; i++) {
+        if (data[i] != contents[i]) {
+            if (contentsMatch) {
+                fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n");
+                contentsMatch = false;
+            }
+            fprintf(stderr, "  [%-2d] %02x %02x\n", i, data[i], contents[i]);
+        }
+    }
+
+    free(contents);
+    return contentsMatch && sizesMatch ? 0 : 1;
+}
+
+int
+backup_helper_test_empty()
+{
+    int err;
+    int fd;
+    KeyedVector<String8,FileRec> snapshot;
+    const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap";
+
+    system("rm -r " SCRATCH_DIR);
+    mkdir(SCRATCH_DIR, 0777);
+
+    // write
+    fd = creat(filename, 0666);
+    if (fd == -1) {
+        fprintf(stderr, "error creating %s\n", filename);
+        return 1;
+    }
+
+    err = write_snapshot_file(fd, snapshot);
+
+    close(fd);
+
+    if (err != 0) {
+        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
+        return err;
+    }
+
+    static const unsigned char correct_data[] = {
+        0x53, 0x6e, 0x61, 0x70,  0x00, 0x00, 0x00, 0x00,
+        0x46, 0x69, 0x6c, 0x65,  0x10, 0x00, 0x00, 0x00
+    };
+
+    err = compare_file(filename, correct_data, sizeof(correct_data));
+    if (err != 0) {
+        return err;
+    }
+
+    // read
+    fd = open(filename, O_RDONLY);
+    if (fd == -1) {
+        fprintf(stderr, "error opening for read %s\n", filename);
+        return 1;
+    }
+
+    KeyedVector<String8,FileState> readSnapshot;
+    err = read_snapshot_file(fd, &readSnapshot);
+    if (err != 0) {
+        fprintf(stderr, "read_snapshot_file failed %d\n", err);
+        return err;
+    }
+
+    if (readSnapshot.size() != 0) {
+        fprintf(stderr, "readSnapshot should be length 0\n");
+        return 1;
+    }
+
+    return 0;
+}
+
+int
+backup_helper_test_four()
+{
+    int err;
+    int fd;
+    KeyedVector<String8,FileRec> snapshot;
+    const char* filename = SCRATCH_DIR "backup_helper_test_four.snap";
+
+    system("rm -r " SCRATCH_DIR);
+    mkdir(SCRATCH_DIR, 0777);
+
+    // write
+    fd = creat(filename, 0666);
+    if (fd == -1) {
+        fprintf(stderr, "error opening %s\n", filename);
+        return 1;
+    }
+
+    String8 filenames[4];
+    FileState states[4];
+    FileRec r;
+    r.deleted = false;
+
+    states[0].modTime_sec = 0xfedcba98;
+    states[0].modTime_nsec = 0xdeadbeef;
+    states[0].mode = 0777; // decimal 511, hex 0x000001ff
+    states[0].size = 0xababbcbc;
+    states[0].crc32 = 0x12345678;
+    states[0].nameLen = -12;
+    r.s = states[0];
+    filenames[0] = String8("bytes_of_padding");
+    snapshot.add(filenames[0], r);
+
+    states[1].modTime_sec = 0x93400031;
+    states[1].modTime_nsec = 0xdeadbeef;
+    states[1].mode = 0666; // decimal 438, hex 0x000001b6
+    states[1].size = 0x88557766;
+    states[1].crc32 = 0x22334422;
+    states[1].nameLen = -1;
+    r.s = states[1];
+    filenames[1] = String8("bytes_of_padding3");
+    snapshot.add(filenames[1], r);
+
+    states[2].modTime_sec = 0x33221144;
+    states[2].modTime_nsec = 0xdeadbeef;
+    states[2].mode = 0744; // decimal 484, hex 0x000001e4
+    states[2].size = 0x11223344;
+    states[2].crc32 = 0x01122334;
+    states[2].nameLen = 0;
+    r.s = states[2];
+    filenames[2] = String8("bytes_of_padding_2");
+    snapshot.add(filenames[2], r);
+
+    states[3].modTime_sec = 0x33221144;
+    states[3].modTime_nsec = 0xdeadbeef;
+    states[3].mode = 0755; // decimal 493, hex 0x000001ed
+    states[3].size = 0x11223344;
+    states[3].crc32 = 0x01122334;
+    states[3].nameLen = 0;
+    r.s = states[3];
+    filenames[3] = String8("bytes_of_padding__1");
+    snapshot.add(filenames[3], r);
+
+    err = write_snapshot_file(fd, snapshot);
+
+    close(fd);
+
+    if (err != 0) {
+        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
+        return err;
+    }
+
+    static const unsigned char correct_data[] = {
+        // header
+        0x53, 0x6e, 0x61, 0x70,  0x04, 0x00, 0x00, 0x00,
+        0x46, 0x69, 0x6c, 0x65,  0xbc, 0x00, 0x00, 0x00,
+
+        // bytes_of_padding
+        0x98, 0xba, 0xdc, 0xfe,  0xef, 0xbe, 0xad, 0xde,
+        0xff, 0x01, 0x00, 0x00,  0xbc, 0xbc, 0xab, 0xab,
+        0x78, 0x56, 0x34, 0x12,  0x10, 0x00, 0x00, 0x00,
+        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
+        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
+
+        // bytes_of_padding3
+        0x31, 0x00, 0x40, 0x93,  0xef, 0xbe, 0xad, 0xde,
+        0xb6, 0x01, 0x00, 0x00,  0x66, 0x77, 0x55, 0x88,
+        0x22, 0x44, 0x33, 0x22,  0x11, 0x00, 0x00, 0x00,
+        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
+        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
+        0x33, 0xab, 0xab, 0xab,
+
+        // bytes of padding2
+        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
+        0xe4, 0x01, 0x00, 0x00,  0x44, 0x33, 0x22, 0x11,
+        0x34, 0x23, 0x12, 0x01,  0x12, 0x00, 0x00, 0x00,
+        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
+        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
+        0x5f, 0x32, 0xab, 0xab,
+
+        // bytes of padding3
+        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
+        0xed, 0x01, 0x00, 0x00,  0x44, 0x33, 0x22, 0x11,
+        0x34, 0x23, 0x12, 0x01,  0x13, 0x00, 0x00, 0x00,
+        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
+        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
+        0x5f, 0x5f, 0x31, 0xab
+    };
+
+    err = compare_file(filename, correct_data, sizeof(correct_data));
+    if (err != 0) {
+        return err;
+    }
+
+    // read
+    fd = open(filename, O_RDONLY);
+    if (fd == -1) {
+        fprintf(stderr, "error opening for read %s\n", filename);
+        return 1;
+    }
+
+
+    KeyedVector<String8,FileState> readSnapshot;
+    err = read_snapshot_file(fd, &readSnapshot);
+    if (err != 0) {
+        fprintf(stderr, "read_snapshot_file failed %d\n", err);
+        return err;
+    }
+
+    if (readSnapshot.size() != 4) {
+        fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size());
+        return 1;
+    }
+
+    bool matched = true;
+    for (size_t i=0; i<readSnapshot.size(); i++) {
+        const String8& name = readSnapshot.keyAt(i);
+        const FileState state = readSnapshot.valueAt(i);
+
+        if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec
+                || states[i].modTime_nsec != state.modTime_nsec || states[i].mode != state.mode
+                || states[i].size != state.size || states[i].crc32 != states[i].crc32) {
+            fprintf(stderr, "state %d expected={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n"
+                            "          actual={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n", i,
+                    states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size,
+                    states[i].crc32, name.length(), filenames[i].string(),
+                    state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32,
+                    state.nameLen, name.string());
+            matched = false;
+        }
+    }
+
+    return matched ? 0 : 1;
+}
+
+// hexdump -v -e '"    " 8/1 " 0x%02x," "\n"' data_writer.data
+const unsigned char DATA_GOLDEN_FILE[] = {
+     0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00,
+     0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70,
+     0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
+     0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69,
+     0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61,
+     0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
+     0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
+     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
+     0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
+     0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00,
+     0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
+     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
+     0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
+     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
+     0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61,
+     0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
+     0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64,
+     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00
+
+};
+const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE);
+
+static int
+test_write_header_and_entity(BackupDataWriter& writer, const char* str)
+{
+    int err;
+    String8 text(str);
+
+    err = writer.WriteEntityHeader(text, text.length()+1);
+    if (err != 0) {
+        fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err));
+        return err;
+    }
+
+    err = writer.WriteEntityData(text.string(), text.length()+1);
+    if (err != 0) {
+        fprintf(stderr, "write failed for data '%s'\n", text.string());
+        return errno;
+    }
+
+    return err;
+}
+
+int
+backup_helper_test_data_writer()
+{
+    int err;
+    int fd;
+    const char* filename = SCRATCH_DIR "data_writer.data";
+
+    system("rm -r " SCRATCH_DIR);
+    mkdir(SCRATCH_DIR, 0777);
+    mkdir(SCRATCH_DIR "data", 0777);
+
+    fd = creat(filename, 0666);
+    if (fd == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    BackupDataWriter writer(fd);
+
+    err = 0;
+    err |= test_write_header_and_entity(writer, "no_padding_");
+    err |= test_write_header_and_entity(writer, "padded_to__3");
+    err |= test_write_header_and_entity(writer, "padded_to_2__");
+    err |= test_write_header_and_entity(writer, "padded_to1");
+
+    close(fd);
+
+    err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
+    if (err != 0) {
+        return err;
+    }
+
+    return err;
+}
+
+int
+test_read_header_and_entity(BackupDataReader& reader, const char* str)
+{
+    int err;
+    int bufSize = strlen(str)+1;
+    char* buf = (char*)malloc(bufSize);
+    String8 string;
+    int cookie = 0x11111111;
+    size_t actualSize;
+    bool done;
+    int type;
+    ssize_t nRead;
+
+    // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str);
+
+    err = reader.ReadNextHeader(&done, &type);
+    if (done) {
+        fprintf(stderr, "should not be done yet\n");
+        goto finished;
+    }
+    if (err != 0) {
+        fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
+        goto finished;
+    }
+    if (type != BACKUP_HEADER_ENTITY_V1) {
+        err = EINVAL;
+        fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1);
+    }
+
+    err = reader.ReadEntityHeader(&string, &actualSize);
+    if (err != 0) {
+        fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err));
+        goto finished;
+    }
+    if (string != str) {
+        fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string());
+        err = EINVAL;
+        goto finished;
+    }
+    if ((int)actualSize != bufSize) {
+        fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize,
+                actualSize);
+        err = EINVAL;
+        goto finished;
+    }
+
+    nRead = reader.ReadEntityData(buf, bufSize);
+    if (nRead < 0) {
+        err = reader.Status();
+        fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err));
+        goto finished;
+    }
+
+    if (0 != memcmp(buf, str, bufSize)) {
+        fprintf(stderr, "ReadEntityData expected '%s' but got something starting with "
+                "%02x %02x %02x %02x  '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3],
+                buf[0], buf[1], buf[2], buf[3]);
+        err = EINVAL;
+        goto finished;
+    }
+
+    // The next read will confirm whether it got the right amount of data.
+
+finished:
+    if (err != NO_ERROR) {
+        fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err));
+    }
+    free(buf);
+    return err;
+}
+
+int
+backup_helper_test_data_reader()
+{
+    int err;
+    int fd;
+    const char* filename = SCRATCH_DIR "data_reader.data";
+
+    system("rm -r " SCRATCH_DIR);
+    mkdir(SCRATCH_DIR, 0777);
+    mkdir(SCRATCH_DIR "data", 0777);
+
+    fd = creat(filename, 0666);
+    if (fd == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
+    if (err != DATA_GOLDEN_FILE_SIZE) {
+        fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename);
+        return errno;
+    }
+
+    close(fd);
+
+    fd = open(filename, O_RDONLY);
+    if (fd == -1) {
+        fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno),
+                filename);
+        return errno;
+    }
+
+    {
+        BackupDataReader reader(fd);
+
+        err = 0;
+
+        if (err == NO_ERROR) {
+            err = test_read_header_and_entity(reader, "no_padding_");
+        }
+
+        if (err == NO_ERROR) {
+            err = test_read_header_and_entity(reader, "padded_to__3");
+        }
+
+        if (err == NO_ERROR) {
+            err = test_read_header_and_entity(reader, "padded_to_2__");
+        }
+
+        if (err == NO_ERROR) {
+            err = test_read_header_and_entity(reader, "padded_to1");
+        }
+    }
+
+    close(fd);
+
+    return err;
+}
+
+static int
+get_mod_time(const char* filename, struct timeval times[2])
+{
+    int err;
+    struct stat64 st;
+    err = stat64(filename, &st);
+    if (err != 0) {
+        fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
+        return errno;
+    }
+    times[0].tv_sec = st.st_atime;
+    times[1].tv_sec = st.st_mtime;
+
+    // If st_atime is a macro then struct stat64 uses struct timespec
+    // to store the access and modif time values and typically
+    // st_*time_nsec is not defined. In glibc, this is controlled by
+    // __USE_MISC.
+#ifdef __USE_MISC
+#if !defined(st_atime) || defined(st_atime_nsec)
+#error "Check if this __USE_MISC conditional is still needed."
+#endif
+    times[0].tv_usec = st.st_atim.tv_nsec / 1000;
+    times[1].tv_usec = st.st_mtim.tv_nsec / 1000;
+#else
+    times[0].tv_usec = st.st_atime_nsec / 1000;
+    times[1].tv_usec = st.st_mtime_nsec / 1000;
+#endif
+
+    return 0;
+}
+
+int
+backup_helper_test_files()
+{
+    int err;
+    int oldSnapshotFD;
+    int dataStreamFD;
+    int newSnapshotFD;
+
+    system("rm -r " SCRATCH_DIR);
+    mkdir(SCRATCH_DIR, 0777);
+    mkdir(SCRATCH_DIR "data", 0777);
+
+    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
+    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
+    write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
+    write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
+    write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
+    write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
+
+    char const* files_before[] = {
+        SCRATCH_DIR "data/b",
+        SCRATCH_DIR "data/c",
+        SCRATCH_DIR "data/d",
+        SCRATCH_DIR "data/e",
+        SCRATCH_DIR "data/f"
+    };
+
+    char const* keys_before[] = {
+        "data/b",
+        "data/c",
+        "data/d",
+        "data/e",
+        "data/f"
+    };
+
+    dataStreamFD = creat(SCRATCH_DIR "1.data", 0666);
+    if (dataStreamFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
+    if (newSnapshotFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    {
+        BackupDataWriter dataStream(dataStreamFD);
+
+        err = back_up_files(-1, &dataStream, newSnapshotFD, files_before, keys_before, 5);
+        if (err != 0) {
+            return err;
+        }
+    }
+
+    close(dataStreamFD);
+    close(newSnapshotFD);
+
+    sleep(3);
+
+    struct timeval d_times[2];
+    struct timeval e_times[2];
+
+    err = get_mod_time(SCRATCH_DIR "data/d", d_times);
+    err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
+    if (err != 0) {
+        return err;
+    }
+
+    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
+    unlink(SCRATCH_DIR "data/c");
+    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
+    write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
+    utimes(SCRATCH_DIR "data/d", d_times);
+    write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
+    utimes(SCRATCH_DIR "data/e", e_times);
+    write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
+    unlink(SCRATCH_DIR "data/f");
+
+    char const* files_after[] = {
+        SCRATCH_DIR "data/a", // added
+        SCRATCH_DIR "data/b", // same
+        SCRATCH_DIR "data/c", // different mod time
+        SCRATCH_DIR "data/d", // different size (same mod time)
+        SCRATCH_DIR "data/e", // different contents (same mod time, same size)
+        SCRATCH_DIR "data/g"  // added
+    };
+
+    char const* keys_after[] = {
+        "data/a", // added
+        "data/b", // same
+        "data/c", // different mod time
+        "data/d", // different size (same mod time)
+        "data/e", // different contents (same mod time, same size)
+        "data/g"  // added
+    };
+
+    oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
+    if (oldSnapshotFD == -1) {
+        fprintf(stderr, "error opening: %s\n", strerror(errno));
+        return errno;
+    }
+
+    dataStreamFD = creat(SCRATCH_DIR "2.data", 0666);
+    if (dataStreamFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
+    if (newSnapshotFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    {
+        BackupDataWriter dataStream(dataStreamFD);
+
+        err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, files_after, keys_after, 6);
+        if (err != 0) {
+            return err;
+        }
+}
+
+    close(oldSnapshotFD);
+    close(dataStreamFD);
+    close(newSnapshotFD);
+
+    return 0;
+}
+
+int
+backup_helper_test_null_base()
+{
+    int err;
+    int oldSnapshotFD;
+    int dataStreamFD;
+    int newSnapshotFD;
+
+    system("rm -r " SCRATCH_DIR);
+    mkdir(SCRATCH_DIR, 0777);
+    mkdir(SCRATCH_DIR "data", 0777);
+
+    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
+
+    char const* files[] = {
+        SCRATCH_DIR "data/a",
+    };
+
+    char const* keys[] = {
+        "a",
+    };
+
+    dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
+    if (dataStreamFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
+    if (newSnapshotFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    {
+        BackupDataWriter dataStream(dataStreamFD);
+
+        err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
+        if (err != 0) {
+            return err;
+        }
+    }
+
+    close(dataStreamFD);
+    close(newSnapshotFD);
+
+    return 0;
+}
+
+int
+backup_helper_test_missing_file()
+{
+    int err;
+    int oldSnapshotFD;
+    int dataStreamFD;
+    int newSnapshotFD;
+
+    system("rm -r " SCRATCH_DIR);
+    mkdir(SCRATCH_DIR, 0777);
+    mkdir(SCRATCH_DIR "data", 0777);
+
+    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
+
+    char const* files[] = {
+        SCRATCH_DIR "data/a",
+        SCRATCH_DIR "data/b",
+        SCRATCH_DIR "data/c",
+    };
+
+    char const* keys[] = {
+        "a",
+        "b",
+        "c",
+    };
+
+    dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
+    if (dataStreamFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
+    if (newSnapshotFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    {
+        BackupDataWriter dataStream(dataStreamFD);
+
+        err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
+        if (err != 0) {
+            return err;
+        }
+    }
+
+    close(dataStreamFD);
+    close(newSnapshotFD);
+
+    return 0;
+}
+
+
+#endif // TEST_BACKUP_HELPERS
+
+}
index 0f4b647..b0e3750 100644 (file)
@@ -409,12 +409,16 @@ status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len)
             mObjects[idx++] = off;
             mObjectsSize++;
 
-            const flat_binder_object* flat
+            flat_binder_object* flat
                 = reinterpret_cast<flat_binder_object*>(mData + off);
             acquire_object(proc, *flat, this);
 
-            // take note if the object is a file descriptor
             if (flat->type == BINDER_TYPE_FD) {
+                // If this is a file descriptor, we need to dup it so the
+                // new Parcel now owns its own fd, and can declare that we
+                // officially know we have fds.
+                flat->handle = dup(flat->handle);
+                flat->cookie = (void*)1;
                 mHasFds = mFdsKnown = true;
             }
         }
@@ -650,28 +654,26 @@ status_t Parcel::writeWeakBinder(const wp<IBinder>& val)
     return flatten_binder(ProcessState::self(), val, this);
 }
 
-status_t Parcel::writeNativeHandle(const native_handle& handle)
+status_t Parcel::writeNativeHandle(const native_handle* handle)
 {
-    if (handle.version != sizeof(native_handle))
+    if (handle->version != sizeof(native_handle))
         return BAD_TYPE;
 
     status_t err;
-    err = writeInt32(handle.numFds);
+    err = writeInt32(handle->numFds);
     if (err != NO_ERROR) return err;
 
-    err = writeInt32(handle.numInts);
+    err = writeInt32(handle->numInts);
     if (err != NO_ERROR) return err;
 
-    for (int i=0 ; err==NO_ERROR && i<handle.numFds ; i++)
-        err = writeDupFileDescriptor(handle.data[i]);
+    for (int i=0 ; err==NO_ERROR && i<handle->numFds ; i++)
+        err = writeDupFileDescriptor(handle->data[i]);
 
     if (err != NO_ERROR) {
         LOGD("write native handle, write dup fd failed");
         return err;
     }
-
-    err = write(handle.data + handle.numFds, sizeof(int)*handle.numInts);
-
+    err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts);
     return err;
 }
 
@@ -928,7 +930,7 @@ wp<IBinder> Parcel::readWeakBinder() const
 }
 
 
-native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int), void* cookie) const
+native_handle* Parcel::readNativeHandle() const
 {
     int numFds, numInts;
     status_t err;
@@ -937,31 +939,15 @@ native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int)
     err = readInt32(&numInts);
     if (err != NO_ERROR) return 0;
 
-    native_handle* h;
-    if (alloc == 0) {
-        size_t size = sizeof(native_handle) + sizeof(int)*(numFds + numInts);
-        h = (native_handle*)malloc(size); 
-        h->version = sizeof(native_handle);
-        h->numFds = numFds;
-        h->numInts = numInts;
-    } else {
-        h = alloc(cookie, numFds, numInts);
-        if (h->version != sizeof(native_handle)) {
-            return 0;
-        }
-    }
-    
+    native_handle* h = native_handle_create(numFds, numInts);
     for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {
         h->data[i] = dup(readFileDescriptor());
         if (h->data[i] < 0) err = BAD_VALUE;
     }
-    
     err = read(h->data + numFds, sizeof(int)*numInts);
-    
     if (err != NO_ERROR) {
-        if (alloc == 0) {
-            free(h);
-        }
+        native_handle_close(h);
+        native_handle_delete(h);
         h = 0;
     }
     return h;
index 3d12dca..109f28d 100644 (file)
@@ -544,7 +544,7 @@ ResXMLParser::event_code_t ResXMLParser::next()
     return mEventCode;
 }
 
-const int32_t ResXMLParser::getCommentID() const
+int32_t ResXMLParser::getCommentID() const
 {
     return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1;
 }
@@ -555,12 +555,12 @@ const uint16_t* ResXMLParser::getComment(size_t* outLen) const
     return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
 }
 
-const uint32_t ResXMLParser::getLineNumber() const
+uint32_t ResXMLParser::getLineNumber() const
 {
     return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1;
 }
 
-const int32_t ResXMLParser::getTextID() const
+int32_t ResXMLParser::getTextID() const
 {
     if (mEventCode == TEXT) {
         return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index);
@@ -583,7 +583,7 @@ ssize_t ResXMLParser::getTextValue(Res_value* outValue) const
     return BAD_TYPE;
 }
 
-const int32_t ResXMLParser::getNamespacePrefixID() const
+int32_t ResXMLParser::getNamespacePrefixID() const
 {
     if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
         return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index);
@@ -598,7 +598,7 @@ const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const
     return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
 }
 
-const int32_t ResXMLParser::getNamespaceUriID() const
+int32_t ResXMLParser::getNamespaceUriID() const
 {
     if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
         return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index);
@@ -613,7 +613,7 @@ const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const
     return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
 }
 
-const int32_t ResXMLParser::getElementNamespaceID() const
+int32_t ResXMLParser::getElementNamespaceID() const
 {
     if (mEventCode == START_TAG) {
         return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index);
@@ -630,7 +630,7 @@ const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const
     return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
 }
 
-const int32_t ResXMLParser::getElementNameID() const
+int32_t ResXMLParser::getElementNameID() const
 {
     if (mEventCode == START_TAG) {
         return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index);
@@ -655,7 +655,7 @@ size_t ResXMLParser::getAttributeCount() const
     return 0;
 }
 
-const int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const
+int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const
 {
     if (mEventCode == START_TAG) {
         const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
@@ -678,7 +678,7 @@ const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen)
     return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
 }
 
-const int32_t ResXMLParser::getAttributeNameID(size_t idx) const
+int32_t ResXMLParser::getAttributeNameID(size_t idx) const
 {
     if (mEventCode == START_TAG) {
         const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
@@ -701,7 +701,7 @@ const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const
     return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
 }
 
-const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
+uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
 {
     int32_t id = getAttributeNameID(idx);
     if (id >= 0 && (size_t)id < mTree.mNumResIds) {
@@ -710,7 +710,7 @@ const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
     return 0;
 }
 
-const int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const
+int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const
 {
     if (mEventCode == START_TAG) {
         const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
@@ -1136,8 +1136,9 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const
 
 struct ResTable::Header
 {
-    Header() : ownedData(NULL), header(NULL) { }
+    Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL) { }
 
+    ResTable* const                 owner;
     void*                           ownedData;
     const ResTable_header*          header;
     size_t                          size;
@@ -1163,8 +1164,8 @@ struct ResTable::Type
 
 struct ResTable::Package
 {
-    Package(const Header* _header, const ResTable_package* _package)
-        : header(_header), package(_package) { }
+    Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
+        : owner(_owner), header(_header), package(_package) { }
     ~Package()
     {
         size_t i = types.size();
@@ -1174,10 +1175,14 @@ struct ResTable::Package
         }
     }
     
+    ResTable* const                 owner;
     const Header* const             header;
     const ResTable_package* const   package;
     Vector<Type*>                   types;
 
+    ResStringPool                   typeStrings;
+    ResStringPool                   keyStrings;
+    
     const Type* getType(size_t idx) const {
         return idx < types.size() ? types[idx] : NULL;
     }
@@ -1188,13 +1193,16 @@ struct ResTable::Package
 // table that defined the package); the ones after are skins on top of it.
 struct ResTable::PackageGroup
 {
-    PackageGroup(const String16& _name, uint32_t _id)
-        : name(_name), id(_id), typeCount(0), bags(NULL) { }
+    PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id)
+        : owner(_owner), name(_name), id(_id), typeCount(0), bags(NULL) { }
     ~PackageGroup() {
         clearBagCache();
         const size_t N = packages.size();
         for (size_t i=0; i<N; i++) {
-            delete packages[i];
+            Package* pkg = packages[i];
+            if (pkg->owner == owner) {
+                delete pkg;
+            }
         }
     }
 
@@ -1225,15 +1233,17 @@ struct ResTable::PackageGroup
         }
     }
     
+    ResTable* const                 owner;
     String16 const                  name;
     uint32_t const                  id;
     Vector<Package*>                packages;
+    
+    // This is for finding typeStrings and other common package stuff.
+    Package*                        basePackage;
 
-    // Taken from the root package.
-    ResStringPool                   typeStrings;
-    ResStringPool                   keyStrings;
+    // For quick access.
     size_t                          typeCount;
-
+    
     // Computed attribute bags, first indexed by the type and second
     // by the entry in that type.
     bag_set***                      bags;
@@ -1560,11 +1570,36 @@ status_t ResTable::add(Asset* asset, void* cookie, bool copyData)
     return add(data, size, cookie, asset, copyData);
 }
 
+status_t ResTable::add(ResTable* src)
+{
+    mError = src->mError;
+    mParams = src->mParams;
+    
+    for (size_t i=0; i<src->mHeaders.size(); i++) {
+        mHeaders.add(src->mHeaders[i]);
+    }
+    
+    for (size_t i=0; i<src->mPackageGroups.size(); i++) {
+        PackageGroup* srcPg = src->mPackageGroups[i];
+        PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
+        for (size_t j=0; j<srcPg->packages.size(); j++) {
+            pg->packages.add(srcPg->packages[j]);
+        }
+        pg->basePackage = srcPg->basePackage;
+        pg->typeCount = srcPg->typeCount;
+        mPackageGroups.add(pg);
+    }
+    
+    memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
+    
+    return mError;
+}
+
 status_t ResTable::add(const void* data, size_t size, void* cookie,
                        Asset* asset, bool copyData)
 {
     if (!data) return NO_ERROR;
-    Header* header = new Header;
+    Header* header = new Header(this);
     header->index = mHeaders.size();
     header->cookie = cookie;
     mHeaders.add(header);
@@ -1682,10 +1717,12 @@ void ResTable::uninit()
     N = mHeaders.size();
     for (size_t i=0; i<N; i++) {
         Header* header = mHeaders[i];
-        if (header->ownedData) {
-            free(header->ownedData);
+        if (header->owner == this) {
+            if (header->ownedData) {
+                free(header->ownedData);
+            }
+            delete header;
         }
-        delete header;
     }
 
     mPackageGroups.clear();
@@ -1728,8 +1765,8 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const
 
         outName->package = grp->name.string();
         outName->packageLen = grp->name.size();
-        outName->type = grp->typeStrings.stringAt(t, &outName->typeLen);
-        outName->name = grp->keyStrings.stringAt(
+        outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
+        outName->name = grp->basePackage->keyStrings.stringAt(
             dtohl(entry->key.index), &outName->nameLen);
         return true;
     }
@@ -2331,13 +2368,13 @@ nope:
             continue;
         }
 
-        const ssize_t ti = group->typeStrings.indexOfString(type, typeLen);
+        const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen);
         if (ti < 0) {
             TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string()));
             continue;
         }
 
-        const ssize_t ei = group->keyStrings.indexOfString(name, nameLen);
+        const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen);
         if (ei < 0) {
             TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string()));
             continue;
@@ -3630,25 +3667,36 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
     PackageGroup* group = NULL;
     uint32_t id = dtohl(pkg->id);
     if (id != 0 && id < 256) {
+    
+        package = new Package(this, header, pkg);
+        if (package == NULL) {
+            return (mError=NO_MEMORY);
+        }
+        
         size_t idx = mPackageMap[id];
         if (idx == 0) {
             idx = mPackageGroups.size()+1;
 
             char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
             strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
-            group = new PackageGroup(String16(tmpName), id);
+            group = new PackageGroup(this, String16(tmpName), id);
             if (group == NULL) {
+                delete package;
                 return (mError=NO_MEMORY);
             }
 
-            err = group->typeStrings.setTo(base+dtohl(pkg->typeStrings),
+            err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
                                            header->dataEnd-(base+dtohl(pkg->typeStrings)));
             if (err != NO_ERROR) {
+                delete group;
+                delete package;
                 return (mError=err);
             }
-            err = group->keyStrings.setTo(base+dtohl(pkg->keyStrings),
+            err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
                                           header->dataEnd-(base+dtohl(pkg->keyStrings)));
             if (err != NO_ERROR) {
+                delete group;
+                delete package;
                 return (mError=err);
             }
 
@@ -3657,6 +3705,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
             if (err < NO_ERROR) {
                 return (mError=err);
             }
+            group->basePackage = package;
+            
             mPackageMap[id] = (uint8_t)idx;
         } else {
             group = mPackageGroups.itemAt(idx-1);
@@ -3664,10 +3714,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
                 return (mError=UNKNOWN_ERROR);
             }
         }
-        package = new Package(header, pkg);
-        if (package == NULL) {
-            return (mError=NO_MEMORY);
-        }
         err = group->packages.add(package);
         if (err < NO_ERROR) {
             return (mError=err);
@@ -3830,9 +3876,88 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
 #define CHAR16_ARRAY_EQ(constant, var, len) \
         ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len))))
 
-void ResTable::print() const
+void print_complex(uint32_t complex, bool isFraction)
+{
+    const float MANTISSA_MULT =
+        1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT);
+    const float RADIX_MULTS[] = {
+        1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT,
+        1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT
+    };
+
+    float value = (complex&(Res_value::COMPLEX_MANTISSA_MASK
+                   <<Res_value::COMPLEX_MANTISSA_SHIFT))
+            * RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT)
+                            & Res_value::COMPLEX_RADIX_MASK];
+    printf("%f", value);
+    
+    if (!isFraction) {
+        switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
+            case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
+            case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break;
+            case Res_value::COMPLEX_UNIT_SP: printf("sp"); break;
+            case Res_value::COMPLEX_UNIT_PT: printf("pt"); break;
+            case Res_value::COMPLEX_UNIT_IN: printf("in"); break;
+            case Res_value::COMPLEX_UNIT_MM: printf("mm"); break;
+            default: printf(" (unknown unit)"); break;
+        }
+    } else {
+        switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
+            case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break;
+            case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break;
+            default: printf(" (unknown unit)"); break;
+        }
+    }
+}
+
+void ResTable::print_value(const Package* pkg, const Res_value& value) const
+{
+    if (value.dataType == Res_value::TYPE_NULL) {
+        printf("(null)\n");
+    } else if (value.dataType == Res_value::TYPE_REFERENCE) {
+        printf("(reference) 0x%08x\n", value.data);
+    } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
+        printf("(attribute) 0x%08x\n", value.data);
+    } else if (value.dataType == Res_value::TYPE_STRING) {
+        size_t len;
+        const char16_t* str = pkg->header->values.stringAt(
+                value.data, &len);
+        if (str == NULL) {
+            printf("(string) null\n");
+        } else {
+            printf("(string) \"%s\"\n",
+                    String8(str, len).string());
+        } 
+    } else if (value.dataType == Res_value::TYPE_FLOAT) {
+        printf("(float) %g\n", *(const float*)&value.data);
+    } else if (value.dataType == Res_value::TYPE_DIMENSION) {
+        printf("(dimension) ");
+        print_complex(value.data, false);
+        printf("\n");
+    } else if (value.dataType == Res_value::TYPE_FRACTION) {
+        printf("(fraction) ");
+        print_complex(value.data, true);
+        printf("\n");
+    } else if (value.dataType >= Res_value::TYPE_FIRST_COLOR_INT
+            || value.dataType <= Res_value::TYPE_LAST_COLOR_INT) {
+        printf("(color) #%08x\n", value.data);
+    } else if (value.dataType == Res_value::TYPE_INT_BOOLEAN) {
+        printf("(boolean) %s\n", value.data ? "true" : "false");
+    } else if (value.dataType >= Res_value::TYPE_FIRST_INT
+            || value.dataType <= Res_value::TYPE_LAST_INT) {
+        printf("(int) 0x%08x or %d\n", value.data, value.data);
+    } else {
+        printf("(unknown type) t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)\n",
+               (int)value.dataType, (int)value.data,
+               (int)value.size, (int)value.res0);
+    }
+}
+
+void ResTable::print(bool inclValues) const
 {
-    printf("mError=0x%x (%s)\n", mError, strerror(mError));
+    if (mError != 0) {
+        printf("mError=0x%x (%s)\n", mError, strerror(mError));
+    }
 #if 0
     printf("mParams=%c%c-%c%c,\n",
             mParams.language[0], mParams.language[1],
@@ -3883,7 +4008,7 @@ void ResTable::print() const
                         printf("      NON-INTEGER ResTable_type ADDRESS: %p\n", type);
                         continue;
                     }
-                    printf("      config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n",
+                    printf("      config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n",
                            (int)configIndex,
                            type->config.language[0] ? type->config.language[0] : '-',
                            type->config.language[1] ? type->config.language[1] : '-',
@@ -3896,7 +4021,8 @@ void ResTable::print() const
                            type->config.inputFlags,
                            type->config.navigation,
                            dtohs(type->config.screenWidth),
-                           dtohs(type->config.screenHeight));
+                           dtohs(type->config.screenHeight),
+                           type->config.screenLayout);
                     size_t entryCount = dtohl(type->entryCount);
                     uint32_t entriesStart = dtohl(type->entriesStart);
                     if ((entriesStart&0x3) != 0) {
@@ -3947,32 +4073,60 @@ void ResTable::print() const
                                  (void*)(entriesStart + thisOffset));
                             continue;
                         }
+                        
+                        uint16_t esize = dtohs(ent->size);
+                        if ((esize&0x3) != 0) {
+                            printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
+                            continue;
+                        }
+                        if ((thisOffset+esize) > typeSize) {
+                            printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
+                                   (void*)entriesStart, (void*)thisOffset,
+                                   (void*)esize, (void*)typeSize);
+                            continue;
+                        }
+                            
+                        const Res_value* valuePtr = NULL;
+                        const ResTable_map_entry* bagPtr = NULL;
+                        Res_value value;
                         if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
                             printf("<bag>");
+                            bagPtr = (const ResTable_map_entry*)ent;
                         } else {
-                            uint16_t esize = dtohs(ent->size);
-                            if ((esize&0x3) != 0) {
-                                printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
-                                continue;
-                            }
-                            if ((thisOffset+esize) > typeSize) {
-                                printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
-                                       (void*)entriesStart, (void*)thisOffset,
-                                       (void*)esize, (void*)typeSize);
-                                continue;
-                            }
-                            
-                            const Res_value* value = (const Res_value*)
+                            valuePtr = (const Res_value*)
                                 (((const uint8_t*)ent) + esize);
+                            value.copyFrom_dtoh(*valuePtr);
                             printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
-                                   (int)value->dataType, (int)dtohl(value->data),
-                                   (int)dtohs(value->size), (int)value->res0);
+                                   (int)value.dataType, (int)value.data,
+                                   (int)value.size, (int)value.res0);
                         }
                         
                         if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
                             printf(" (PUBLIC)");
                         }
                         printf("\n");
+                        
+                        if (inclValues) {
+                            if (valuePtr != NULL) {
+                                printf("          ");
+                                print_value(pkg, value);
+                            } else if (bagPtr != NULL) {
+                                const int N = dtohl(bagPtr->count);
+                                const ResTable_map* mapPtr = (const ResTable_map*)
+                                        (((const uint8_t*)ent) + esize);
+                                printf("          Parent=0x%08x, Count=%d\n",
+                                    dtohl(bagPtr->parent.ident), N);
+                                for (int i=0; i<N; i++) {
+                                    printf("          #%i (Key=0x%08x): ",
+                                        i, dtohl(mapPtr->name.ident));
+                                    value.copyFrom_dtoh(mapPtr->value);
+                                    print_value(pkg, value);
+                                    const size_t size = dtohs(mapPtr->value.size);
+                                    mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)
+                                            + size + sizeof(*mapPtr)-sizeof(mapPtr->value));
+                                }
+                            }
+                        }
                     }
                 }
             }
index 33f535f..f92703e 100644 (file)
  * limitations under the License.
  */
 
-#include "utils/AndroidUnicode.h"
-#include "characterData.h"
+#include <utils/AndroidUnicode.h>
+#include "CharacterData.h"
 
 #define LOG_TAG "Unicode"
-#include "utils/Log.h"
+#include <utils/Log.h>
 
 // ICU headers for using macros
 #include <unicode/utf16.h>
index fbc9e67..96f9fc4 100644 (file)
@@ -20,8 +20,8 @@
 
 #define LOG_TAG "zip"
 
-#include "utils/ZipEntry.h"
-#include "utils/Log.h"
+#include <utils/ZipEntry.h>
+#include <utils/Log.h>
 
 #include <stdio.h>
 #include <string.h>
index 89aa874..6f27d17 100644 (file)
@@ -20,9 +20,9 @@
 
 #define LOG_TAG "zip"
 
-#include "utils/ZipFile.h"
-#include "utils/ZipUtils.h"
-#include "utils/Log.h"
+#include <utils/ZipFile.h>
+#include <utils/ZipUtils.h>
+#include <utils/Log.h>
 
 #include <zlib.h>
 #define DEF_MEM_LEVEL 8                // normally in zutil.h?
index d312daf..45f6c8b 100644 (file)
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "utils/ZipFileCRO.h"
-#include "utils/ZipFileRO.h"
+#include <utils/ZipFileCRO.h>
+#include <utils/ZipFileRO.h>
 
 using namespace android;
 
index ae8c719..6c701dd 100644 (file)
@@ -19,9 +19,9 @@
 //
 #define LOG_TAG "zipro"
 //#define LOG_NDEBUG 0
-#include "utils/ZipFileRO.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
 
 #include <zlib.h>
 
index bfbacfe..5df94cb 100644 (file)
@@ -20,9 +20,9 @@
 
 #define LOG_TAG "ziputil"
 
-#include "utils/ZipUtils.h"
-#include "utils/ZipFileRO.h"
-#include "utils/Log.h"
+#include <utils/ZipUtils.h>
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
 
 #include <stdlib.h>
 #include <string.h>
diff --git a/libs/utils/file_backup_helper.cpp b/libs/utils/file_backup_helper.cpp
deleted file mode 100644 (file)
index 453084a..0000000
+++ /dev/null
@@ -1,685 +0,0 @@
-#define LOG_TAG "file_backup_helper"
-
-#include <utils/backup_helpers.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/ByteOrder.h>
-#include <utils/String8.h>
-
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <utime.h>
-#include <fcntl.h>
-#include <zlib.h>
-
-#include <cutils/log.h>
-
-using namespace android;
-
-#define MAGIC0 0x70616e53 // Snap
-#define MAGIC1 0x656c6946 // File
-
-#define LOGP(x...) LOGD(x)
-//#define LOGP(x...) printf(x)
-
-struct SnapshotHeader {
-    int magic0;
-    int fileCount;
-    int magic1;
-    int totalSize;
-};
-
-struct FileState {
-    int modTime_sec;
-    int modTime_nsec;
-    int size;
-    int crc32;
-    int nameLen;
-};
-
-const static int ROUND_UP[4] = { 0, 3, 2, 1 };
-
-static inline int
-round_up(int n)
-{
-    return n + ROUND_UP[n % 4];
-}
-
-static int
-read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot)
-{
-    int bytesRead = 0;
-    int amt;
-    SnapshotHeader header;
-
-    amt = read(fd, &header, sizeof(header));
-    if (amt != sizeof(header)) {
-        return errno;
-    }
-    bytesRead += amt;
-
-    if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) {
-        LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1);
-        return 1;
-    }
-
-    for (int i=0; i<header.fileCount; i++) {
-        FileState file;
-        char filenameBuf[128];
-
-        amt = read(fd, &file, sizeof(file));
-        if (amt != sizeof(file)) {
-            LOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead);
-            return 1;
-        }
-        bytesRead += amt;
-
-        // filename is not NULL terminated, but it is padded
-        int nameBufSize = round_up(file.nameLen);
-        char* filename = nameBufSize <= (int)sizeof(filenameBuf)
-                ? filenameBuf
-                : (char*)malloc(nameBufSize);
-        amt = read(fd, filename, nameBufSize);
-        if (amt == nameBufSize) {
-            snapshot->add(String8(filename, file.nameLen), file);
-        }
-        bytesRead += amt;
-        if (filename != filenameBuf) {
-            free(filename);
-        }
-        if (amt != nameBufSize) {
-            LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead);
-            return 1;
-        }
-    }
-
-    if (header.totalSize != bytesRead) {
-        LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n",
-                header.totalSize, bytesRead);
-        return 1;
-    }
-
-    return 0;
-}
-
-static int
-write_snapshot_file(int fd, const KeyedVector<String8,FileState>& snapshot)
-{
-    int bytesWritten = sizeof(SnapshotHeader);
-    // preflight size
-    const int N = snapshot.size();
-    for (int i=0; i<N; i++) {
-        const String8& name = snapshot.keyAt(i);
-        bytesWritten += sizeof(FileState) + round_up(name.length());
-    }
-
-    int amt;
-    SnapshotHeader header = { MAGIC0, N, MAGIC1, bytesWritten };
-
-    amt = write(fd, &header, sizeof(header));
-    if (amt != sizeof(header)) {
-        LOGW("write_snapshot_file error writing header %s", strerror(errno));
-        return errno;
-    }
-
-    for (int i=0; i<header.fileCount; i++) {
-        const String8& name = snapshot.keyAt(i);
-        FileState file = snapshot.valueAt(i);
-        int nameLen = file.nameLen = name.length();
-
-        amt = write(fd, &file, sizeof(file));
-        if (amt != sizeof(file)) {
-            LOGW("write_snapshot_file error writing header %s", strerror(errno));
-            return 1;
-        }
-
-        // filename is not NULL terminated, but it is padded
-        amt = write(fd, name.string(), nameLen);
-        if (amt != nameLen) {
-            LOGW("write_snapshot_file error writing filename %s", strerror(errno));
-            return 1;
-        }
-        int paddingLen = ROUND_UP[nameLen % 4];
-        if (paddingLen != 0) {
-            int padding = 0xabababab;
-            amt = write(fd, &padding, paddingLen);
-            if (amt != paddingLen) {
-                LOGW("write_snapshot_file error writing %d bytes of filename padding %s",
-                        paddingLen, strerror(errno));
-                return 1;
-            }
-        }
-    }
-
-    return 0;
-}
-
-static int
-write_delete_file(const String8& key)
-{
-    LOGP("write_delete_file %s\n", key.string());
-    return 0;
-}
-
-static int
-write_update_file(const String8& realFilename, const String8& key)
-{
-    LOGP("write_update_file %s (%s)\n", realFilename.string(), key.string());
-    return 0;
-}
-
-static int
-compute_crc32(const String8& filename)
-{
-    const int bufsize = 4*1024;
-    int amt;
-
-    int fd = open(filename.string(), O_RDONLY);
-    if (fd == -1) {
-        return -1;
-    }
-
-    char* buf = (char*)malloc(bufsize);
-    int crc = crc32(0L, Z_NULL, 0);
-
-    while ((amt = read(fd, buf, bufsize)) != 0) {
-        crc = crc32(crc, (Bytef*)buf, amt);
-    }
-
-    close(fd);
-    free(buf);
-
-    return crc;
-}
-
-int
-back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD,
-        char const* fileBase, char const* const* files, int fileCount)
-{
-    int err;
-    const String8 base(fileBase);
-    KeyedVector<String8,FileState> oldSnapshot;
-    KeyedVector<String8,FileState> newSnapshot;
-
-    if (oldSnapshotFD != -1) {
-        err = read_snapshot_file(oldSnapshotFD, &oldSnapshot);
-        if (err != 0) {
-            // On an error, treat this as a full backup.
-            oldSnapshot.clear();
-        }
-    }
-
-    for (int i=0; i<fileCount; i++) {
-        String8 name(files[i]);
-        FileState s;
-        struct stat st;
-        String8 realFilename(base);
-        realFilename.appendPath(name);
-
-        err = stat(realFilename.string(), &st);
-        if (err != 0) {
-            LOGW("Error stating file %s", realFilename.string());
-            continue;
-        }
-
-        s.modTime_sec = st.st_mtime;
-        s.modTime_nsec = 0; // workaround sim breakage
-        //s.modTime_nsec = st.st_mtime_nsec;
-        s.size = st.st_size;
-        s.crc32 = compute_crc32(realFilename);
-
-        newSnapshot.add(name, s);
-    }
-
-    int n = 0;
-    int N = oldSnapshot.size();
-    int m = 0;
-
-    while (n<N && m<fileCount) {
-        const String8& p = oldSnapshot.keyAt(n);
-        const String8& q = newSnapshot.keyAt(m);
-        int cmp = p.compare(q);
-        if (cmp > 0) {
-            // file added
-            String8 realFilename(base);
-            realFilename.appendPath(q);
-            write_update_file(realFilename, q);
-            m++;
-        }
-        else if (cmp < 0) {
-            // file removed
-            write_delete_file(p);
-            n++;
-        }
-        else {
-            // both files exist, check them
-            String8 realFilename(base);
-            realFilename.appendPath(q);
-            const FileState& f = oldSnapshot.valueAt(n);
-            const FileState& g = newSnapshot.valueAt(m);
-
-            LOGP("%s\n", q.string());
-            LOGP("  new: modTime=%d,%d size=%-3d crc32=0x%08x\n",
-                    f.modTime_sec, f.modTime_nsec, f.size, f.crc32);
-            LOGP("  old: modTime=%d,%d size=%-3d crc32=0x%08x\n",
-                    g.modTime_sec, g.modTime_nsec, g.size, g.crc32);
-            if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec
-                    || f.size != g.size || f.crc32 != g.crc32) {
-                write_update_file(realFilename, p);
-            }
-            n++;
-            m++;
-        }
-    }
-
-    // these were deleted
-    while (n<N) {
-        write_delete_file(oldSnapshot.keyAt(n));
-        n++;
-    }
-
-    // these were added
-    while (m<fileCount) {
-        const String8& q = newSnapshot.keyAt(m);
-        String8 realFilename(base);
-        realFilename.appendPath(q);
-        write_update_file(realFilename, q);
-        m++;
-    }
-
-    err = write_snapshot_file(newSnapshotFD, newSnapshot);
-
-    return 0;
-}
-
-#if TEST_BACKUP_HELPERS
-
-#define SCRATCH_DIR "/data/backup_helper_test/"
-
-static int
-write_text_file(const char* path, const char* data)
-{
-    int amt;
-    int fd;
-    int len;
-
-    fd = creat(path, 0666);
-    if (fd == -1) {
-        fprintf(stderr, "creat %s failed\n", path);
-        return errno;
-    }
-
-    len = strlen(data);
-    amt = write(fd, data, len);
-    if (amt != len) {
-        fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path);
-        return errno;
-    }
-
-    close(fd);
-
-    return 0;
-}
-
-static int
-compare_file(const char* path, const unsigned char* data, int len)
-{
-    int fd;
-    int amt;
-
-    fd = open(path, O_RDONLY);
-    if (fd == -1) {
-        fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path);
-        return errno;
-    }
-
-    unsigned char* contents = (unsigned char*)malloc(len);
-    if (contents == NULL) {
-        fprintf(stderr, "malloc(%d) failed\n", len);
-        return ENOMEM;
-    }
-
-    bool sizesMatch = true;
-    amt = lseek(fd, 0, SEEK_END);
-    if (amt != len) {
-        fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt);
-        sizesMatch = false;
-    }
-    lseek(fd, 0, SEEK_SET);
-
-    int readLen = amt < len ? amt : len;
-    amt = read(fd, contents, readLen);
-    if (amt != readLen) {
-        fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt);
-    }
-
-    bool contentsMatch = true;
-    for (int i=0; i<readLen; i++) {
-        if (data[i] != contents[i]) {
-            if (contentsMatch) {
-                fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n");
-                contentsMatch = false;
-            }
-            fprintf(stderr, "  [%-2d] %02x %02x\n", i, data[i], contents[i]);
-        }
-    }
-
-    return contentsMatch && sizesMatch ? 0 : 1;
-}
-
-int
-backup_helper_test_empty()
-{
-    int err;
-    int fd;
-    KeyedVector<String8,FileState> snapshot;
-    const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap";
-
-    system("rm -r " SCRATCH_DIR);
-    mkdir(SCRATCH_DIR, 0777);
-
-    // write
-    fd = creat(filename, 0666);
-    if (fd == -1) {
-        fprintf(stderr, "error creating %s\n", filename);
-        return 1;
-    }
-
-    err = write_snapshot_file(fd, snapshot);
-
-    close(fd);
-
-    if (err != 0) {
-        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
-        return err;
-    }
-
-    static const unsigned char correct_data[] = {
-        0x53, 0x6e, 0x61, 0x70,  0x00, 0x00, 0x00, 0x00,
-        0x46, 0x69, 0x6c, 0x65,  0x10, 0x00, 0x00, 0x00
-    };
-
-    err = compare_file(filename, correct_data, sizeof(correct_data));
-    if (err != 0) {
-        return err;
-    }
-
-    // read
-    fd = open(filename, O_RDONLY);
-    if (fd == -1) {
-        fprintf(stderr, "error opening for read %s\n", filename);
-        return 1;
-    }
-
-    KeyedVector<String8,FileState> readSnapshot;
-    err = read_snapshot_file(fd, &readSnapshot);
-    if (err != 0) {
-        fprintf(stderr, "read_snapshot_file failed %d\n", err);
-        return err;
-    }
-
-    if (readSnapshot.size() != 0) {
-        fprintf(stderr, "readSnapshot should be length 0\n");
-        return 1;
-    }
-
-    return 0;
-}
-
-int
-backup_helper_test_four()
-{
-    int err;
-    int fd;
-    KeyedVector<String8,FileState> snapshot;
-    const char* filename = SCRATCH_DIR "backup_helper_test_four.snap";
-
-    system("rm -r " SCRATCH_DIR);
-    mkdir(SCRATCH_DIR, 0777);
-
-    // write
-    fd = creat(filename, 0666);
-    if (fd == -1) {
-        fprintf(stderr, "error opening %s\n", filename);
-        return 1;
-    }
-
-    String8 filenames[4];
-    FileState states[4];
-
-    states[0].modTime_sec = 0xfedcba98;
-    states[0].modTime_nsec = 0xdeadbeef;
-    states[0].size = 0xababbcbc;
-    states[0].crc32 = 0x12345678;
-    states[0].nameLen = -12;
-    filenames[0] = String8("bytes_of_padding");
-    snapshot.add(filenames[0], states[0]);
-
-    states[1].modTime_sec = 0x93400031;
-    states[1].modTime_nsec = 0xdeadbeef;
-    states[1].size = 0x88557766;
-    states[1].crc32 = 0x22334422;
-    states[1].nameLen = -1;
-    filenames[1] = String8("bytes_of_padding3");
-    snapshot.add(filenames[1], states[1]);
-
-    states[2].modTime_sec = 0x33221144;
-    states[2].modTime_nsec = 0xdeadbeef;
-    states[2].size = 0x11223344;
-    states[2].crc32 = 0x01122334;
-    states[2].nameLen = 0;
-    filenames[2] = String8("bytes_of_padding_2");
-    snapshot.add(filenames[2], states[2]);
-
-    states[3].modTime_sec = 0x33221144;
-    states[3].modTime_nsec = 0xdeadbeef;
-    states[3].size = 0x11223344;
-    states[3].crc32 = 0x01122334;
-    states[3].nameLen = 0;
-    filenames[3] = String8("bytes_of_padding__1");
-    snapshot.add(filenames[3], states[3]);
-
-    err = write_snapshot_file(fd, snapshot);
-
-    close(fd);
-
-    if (err != 0) {
-        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
-        return err;
-    }
-
-    static const unsigned char correct_data[] = {
-        // header
-        0x53, 0x6e, 0x61, 0x70,  0x04, 0x00, 0x00, 0x00,
-        0x46, 0x69, 0x6c, 0x65,  0xac, 0x00, 0x00, 0x00,
-
-        // bytes_of_padding
-        0x98, 0xba, 0xdc, 0xfe,  0xef, 0xbe, 0xad, 0xde,
-        0xbc, 0xbc, 0xab, 0xab,  0x78, 0x56, 0x34, 0x12,
-        0x10, 0x00, 0x00, 0x00,  0x62, 0x79, 0x74, 0x65,
-        0x73, 0x5f, 0x6f, 0x66,  0x5f, 0x70, 0x61, 0x64,
-        0x64, 0x69, 0x6e, 0x67,
-
-        // bytes_of_padding3
-        0x31, 0x00, 0x40, 0x93,  0xef, 0xbe, 0xad, 0xde,
-        0x66, 0x77, 0x55, 0x88,  0x22, 0x44, 0x33, 0x22,
-        0x11, 0x00, 0x00, 0x00,  0x62, 0x79, 0x74, 0x65,
-        0x73, 0x5f, 0x6f, 0x66,  0x5f, 0x70, 0x61, 0x64,
-        0x64, 0x69, 0x6e, 0x67,  0x33, 0xab, 0xab, 0xab,
-        
-        // bytes of padding2
-        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
-        0x44, 0x33, 0x22, 0x11,  0x34, 0x23, 0x12, 0x01,
-        0x12, 0x00, 0x00, 0x00,  0x62, 0x79, 0x74, 0x65,
-        0x73, 0x5f, 0x6f, 0x66,  0x5f, 0x70, 0x61, 0x64,
-        0x64, 0x69, 0x6e, 0x67,  0x5f, 0x32, 0xab, 0xab,
-        
-        // bytes of padding3
-        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
-        0x44, 0x33, 0x22, 0x11,  0x34, 0x23, 0x12, 0x01,
-        0x13, 0x00, 0x00, 0x00,  0x62, 0x79, 0x74, 0x65,
-        0x73, 0x5f, 0x6f, 0x66,  0x5f, 0x70, 0x61, 0x64,
-        0x64, 0x69, 0x6e, 0x67,  0x5f, 0x5f, 0x31, 0xab
-    };
-
-    err = compare_file(filename, correct_data, sizeof(correct_data));
-    if (err != 0) {
-        return err;
-    }
-    
-    // read
-    fd = open(filename, O_RDONLY);
-    if (fd == -1) {
-        fprintf(stderr, "error opening for read %s\n", filename);
-        return 1;
-    }
-
-
-    KeyedVector<String8,FileState> readSnapshot;
-    err = read_snapshot_file(fd, &readSnapshot);
-    if (err != 0) {
-        fprintf(stderr, "read_snapshot_file failed %d\n", err);
-        return err;
-    }
-
-    if (readSnapshot.size() != 4) {
-        fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size());
-        return 1;
-    }
-
-    bool matched = true;
-    for (size_t i=0; i<readSnapshot.size(); i++) {
-        const String8& name = readSnapshot.keyAt(i);
-        const FileState state = readSnapshot.valueAt(i);
-
-        if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec
-                || states[i].modTime_nsec != state.modTime_nsec
-                || states[i].size != state.size || states[i].crc32 != states[i].crc32) {
-            fprintf(stderr, "state %d expected={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n"
-                            "          actual={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n", i,
-                    states[i].modTime_sec, states[i].modTime_nsec, states[i].size, states[i].crc32,
-                    name.length(), filenames[i].string(),
-                    state.modTime_sec, state.modTime_nsec, state.size, state.crc32, state.nameLen,
-                    name.string());
-            matched = false;
-        }
-    }
-    
-    return matched ? 0 : 1;
-}
-
-static int
-get_mod_time(const char* filename, struct timeval times[2])
-{
-    int err;
-    struct stat64 st;
-    err = stat64(filename, &st);
-    if (err != 0) {
-        fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
-        return errno;
-    }
-    times[0].tv_sec = st.st_atime;
-    times[0].tv_usec = st.st_atime_nsec / 1000;
-    times[1].tv_sec = st.st_mtime;
-    times[1].tv_usec = st.st_mtime_nsec / 1000;
-    return 0;
-}
-
-int
-backup_helper_test_files()
-{
-    int err;
-    int newSnapshotFD;
-    int oldSnapshotFD;
-
-    system("rm -r " SCRATCH_DIR);
-    mkdir(SCRATCH_DIR, 0777);
-    mkdir(SCRATCH_DIR "data", 0777);
-
-    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
-    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
-    write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
-    write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
-    write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
-    write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
-
-    char const* files_before[] = {
-        "data/b",
-        "data/c",
-        "data/d",
-        "data/e",
-        "data/f"
-    };
-
-    newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
-    if (newSnapshotFD == -1) {
-        fprintf(stderr, "error creating: %s\n", strerror(errno));
-        return errno;
-    }
-
-    err = back_up_files(-1, newSnapshotFD, 0, SCRATCH_DIR, files_before, 5);
-    if (err != 0) {
-        return err;
-    }
-
-    close(newSnapshotFD);
-
-    sleep(3);
-
-    struct timeval d_times[2];
-    struct timeval e_times[2];
-
-    err = get_mod_time(SCRATCH_DIR "data/d", d_times);
-    err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
-    if (err != 0) {
-        return err;
-    }
-
-    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
-    unlink(SCRATCH_DIR "data/c");
-    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
-    write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
-    utimes(SCRATCH_DIR "data/d", d_times);
-    write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
-    utimes(SCRATCH_DIR "data/e", e_times);
-    write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
-    unlink(SCRATCH_DIR "data/f");
-    
-    char const* files_after[] = {
-        "data/a", // added
-        "data/b", // same
-        "data/c", // different mod time
-        "data/d", // different size (same mod time)
-        "data/e", // different contents (same mod time, same size)
-        "data/g"  // added
-    };
-
-    oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
-    if (oldSnapshotFD == -1) {
-        fprintf(stderr, "error opening: %s\n", strerror(errno));
-        return errno;
-    }
-
-    newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
-    if (newSnapshotFD == -1) {
-        fprintf(stderr, "error creating: %s\n", strerror(errno));
-        return errno;
-    }
-
-    err = back_up_files(oldSnapshotFD, newSnapshotFD, 0, SCRATCH_DIR, files_after, 6);
-    if (err != 0) {
-        return err;
-    }
-
-    close(oldSnapshotFD);
-    close(newSnapshotFD);
-    
-    return 0;
-}
-
-#endif // TEST_BACKUP_HELPERS
index ba19520..ab48c69 100644 (file)
@@ -28,6 +28,7 @@
 // This futex glue code is need on desktop linux, but is already part of bionic.
 #if !defined(HAVE_FUTEX_WRAPPERS)
 
+#include <unistd.h>
 #include <sys/syscall.h>
 typedef unsigned int u32;
 #define asmlinkage
index 8fa7566..3e9c6a5 100644 (file)
@@ -951,6 +951,8 @@ void compileElement__generic(ogles_context_t* c,
     v->index = first;
     first &= vertex_cache_t::INDEX_MASK;
     const GLubyte* vp = c->arrays.vertex.element(first);
+    v->obj.z = 0;
+    v->obj.w = 0x10000;
     c->arrays.vertex.fetch(c, v->obj.v, vp);
     c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj);
     c->arrays.perspective(c, v);
@@ -966,6 +968,8 @@ void compileElements__generic(ogles_context_t* c,
     do {
         v->flags = 0;
         v->index = first++;
+        v->obj.z = 0;
+        v->obj.w = 0x10000;
         c->arrays.vertex.fetch(c, v->obj.v, vp);
         c->arrays.mvp_transform(mvp, &v->clip, &v->obj);
         c->arrays.perspective(c, v);
index 25c41d0..8ae32cc 100644 (file)
@@ -38,13 +38,14 @@ static void lightVertex(ogles_context_t* c, vertex_t* v);
 static void lightVertexMaterial(ogles_context_t* c, vertex_t* v);
 
 static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s);
-static inline void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b);
 
 static __attribute__((noinline))
 void vnorm3(GLfixed* d, const GLfixed* a);
 
 static inline void vsa3(GLfixed* d,
     const GLfixed* m, GLfixed s, const GLfixed* a);
+static inline void vss3(GLfixed* d,
+    const GLfixed* m, GLfixed s, const GLfixed* a);
 static inline void vmla3(GLfixed* d,
     const GLfixed* m0, const GLfixed* m1, const GLfixed* a);
 static inline void vmul3(GLfixed* d,
@@ -151,18 +152,10 @@ void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
 }
 
 static inline
-void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b) {
-    const GLfixed wa = a[3];
-    const GLfixed wb = b[3];
-    if (ggl_likely(wa == wb)) {
-        d[0] = a[0] - b[0];
-        d[1] = a[1] - b[1];
-        d[2] = a[2] - b[2];
-    } else {
-        d[0] = gglMulSubx(a[0], wb, gglMulx(b[0], wa));
-        d[1] = gglMulSubx(a[1], wb, gglMulx(b[1], wa));
-        d[2] = gglMulSubx(a[2], wb, gglMulx(b[2], wa));
-    }
+void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
+    d[0] = gglMulSubx(m[0], s, a[0]);
+    d[1] = gglMulSubx(m[1], s, a[1]);
+    d[2] = gglMulSubx(m[2], s, a[2]);
 }
 
 static inline
@@ -227,7 +220,7 @@ static inline void validate_light_mvi(ogles_context_t* c)
         const int i = 31 - gglClz(en);
         en &= ~(1<<i);
         light_t& l = c->lighting.lights[i];
-        c->transforms.mvui.point3(&c->transforms.mvui,
+        c->transforms.mvui.point4(&c->transforms.mvui,
                 &l.objPosition, &l.position);
         vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
     }
@@ -318,6 +311,11 @@ void lightVertexMaterial(ogles_context_t* c, vertex_t* v)
         vmul3(l.implicitAmbient.v,  material.ambient.v,  l.ambient.v);
         vmul3(l.implicitDiffuse.v,  material.diffuse.v,  l.diffuse.v);
         vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
+        // this is just a flag to tell if we have a specular component
+        l.implicitSpecular.v[3] =
+                l.implicitSpecular.r |
+                l.implicitSpecular.g |
+                l.implicitSpecular.b;
     }
     // emission and ambient for the whole scene
     vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
@@ -343,7 +341,11 @@ void lightVertex(ogles_context_t* c, vertex_t* v)
         vec4_t n;
         c->arrays.normal.fetch(c, n.v,
             c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK));
-        if (c->transforms.rescaleNormals == GL_NORMALIZE)
+
+        // TODO: right now we handle GL_RESCALE_NORMALS as if ti were
+        // GL_NORMALIZE. We could optimize this by  scaling mvui 
+        // appropriately instead.
+        if (c->transforms.rescaleNormals)
             vnorm3(n.v, n.v);
 
         const material_t& material = c->lighting.front;
@@ -360,7 +362,8 @@ void lightVertex(ogles_context_t* c, vertex_t* v)
 
             // compute vertex-to-light vector
             if (ggl_unlikely(l.position.w)) {
-                vsub3w(d.v, l.objPosition.v, v->obj.v);
+                // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex
+                vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v);
                 sqDist = dot3(d.v, d.v);
                 vscale3(d.v, d.v, gglSqrtRecipx(sqDist));
             } else {
index f175cda..0b68dc0 100644 (file)
@@ -55,7 +55,7 @@ static void normal__nop(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
-static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o);
+static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
 
 // ----------------------------------------------------------------------------
 #if 0
@@ -209,7 +209,8 @@ void mvui_transform_t::picker()
 {
     flags = 0;
     ops = OP_ALL;
-    point3 = normal__generic;
+    point3 = point4__mvui;
+    point4 = point4__mvui;
 }
 
 void transform_t::dump(const char* what)
@@ -596,66 +597,19 @@ void transform_state_t::update_mvit()
 
 void transform_state_t::update_mvui()
 {
+    GLfloat r[16];
     const GLfloat* const mv = modelview.top().elements();
-
-    /*
-    When transforming normals, we can use the upper 3x3 matrix, see:
-    http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html
-    */
     
-    // Also note that:
-    //      l(obj) =  tr(M).l(eye) for infinite light
-    //      l(obj) = inv(M).l(eye) for local light
-
-    const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE;
-    if (ggl_likely((!(ops & ~OP_ROTATE)) ||
-        (rescaleNormals && modelview.isRigidBody()))) {
-        // if the modelview matrix is a rigid body transformation
-        // (translation, rotation, uniform scaling), then we can bypass
-        // the inverse by transposing the matrix.
-        GLfloat rescale = 1.0f;
-        if (rescaleNormals == GL_RESCALE_NORMAL) {
-            if (!(ops & ~OP_UNIFORM_SCALE)) {
-                rescale = reciprocalf(mv[I(0,0)]);
-            } else {
-                rescale = rsqrtf(
-                        sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)]));
-            }
-        }
-        GLfixed* const x = mvui.matrix.m;
-        for (int i=0 ; i<3 ; i++) {
-            x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale);
-            x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale);
-            x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale);
-        }
-        mvui.picker();
-        return;
-    }
-
-    GLfloat r[3][3];
-    r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]);
-    r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]);
-    r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]);
-    r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]);
-    r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]);
-    r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]);
-    r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]);
-    r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]);
-    r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]);        
-
-    GLfloat rdet;
-    if (rescaleNormals == GL_RESCALE_NORMAL) {
-        rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2]));
-    } else {
-        rdet = reciprocalf( 
-            r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]);
-    }
+    // TODO: we need a faster invert, especially for when the modelview
+    // is a rigid-body matrix
+    invert(r, mv);
 
     GLfixed* const x = mvui.matrix.m;
-    for (int i=0 ; i<3 ; i++) {
-        x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet);
-        x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet);
-        x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet);
+    for (int i=0 ; i<4 ; i++) {
+        x[I(i,0)] = gglFloatToFixed(r[I(i,0)]);
+        x[I(i,1)] = gglFloatToFixed(r[I(i,1)]);
+        x[I(i,2)] = gglFloatToFixed(r[I(i,2)]);
+        x[I(i,4)] = gglFloatToFixed(r[I(i,3)]);
     }
     mvui.picker();
 }
@@ -783,14 +737,19 @@ void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
     lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]);
 }
 
-void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+    // this used for transforming light positions back to object space.
+    // Lights have 3 components positions, so w is always 1.
+    // however, it is used as a switch for directional lights, so we need
+    // to preserve it.
     const GLfixed* const m = mx->matrix.m;
     const GLfixed rx = rhs->x;
     const GLfixed ry = rhs->y;
     const GLfixed rz = rhs->z;
-    lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); 
-    lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]);
-    lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]);
+    lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); 
+    lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
+    lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
+    lhs->w = rhs->w;
 }
 
 
index b6f534b..14a910c 100644 (file)
@@ -900,7 +900,7 @@ void glTexParameteriv(
         memcpy(textureObject->crop_rect, params, 4*sizeof(GLint));
         break;
     default:
-        ogles_error(c, GL_INVALID_ENUM);
+        texParameterx(target, pname, GLfixed(params[0]), c);
         return;
     }
 }
@@ -919,6 +919,13 @@ void glTexParameterx(
     texParameterx(target, pname, param, c);
 }
 
+void glTexParameteri(
+        GLenum target, GLenum pname, GLint param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    texParameterx(target, pname, GLfixed(param), c);
+}
+
 // ----------------------------------------------------------------------------
 #if 0
 #pragma mark -
index 5ba6b76..23304d5 100644 (file)
@@ -39,7 +39,7 @@ LOCAL_SRC_FILES:=     \
        GLES_CM/gl.cpp.arm              \
 #
 
-LOCAL_SHARED_LIBRARIES += libcutils libutils libui libEGL
+LOCAL_SHARED_LIBRARIES += libcutils libEGL
 LOCAL_LDLIBS := -lpthread -ldl
 LOCAL_MODULE:= libGLESv1_CM
 
index 25e31ee..de323b3 100644 (file)
@@ -1052,23 +1052,25 @@ EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
     if (!validate_display_context(dpy, ctx))
         return EGL_FALSE;    
     
+    EGLSurface impl_draw = EGL_NO_SURFACE;
+    EGLSurface impl_read = EGL_NO_SURFACE;
     egl_context_t * const c = get_context(ctx);
     if (draw != EGL_NO_SURFACE) {
         egl_surface_t const * d = get_surface(draw);
         if (!d) return setError(EGL_BAD_SURFACE, EGL_FALSE);
         if (d->impl != c->impl)
             return setError(EGL_BAD_MATCH, EGL_FALSE);
-        draw = d->surface;
+        impl_draw = d->surface;
     }
     if (read != EGL_NO_SURFACE) {
         egl_surface_t const * r = get_surface(read);
         if (!r) return setError(EGL_BAD_SURFACE, EGL_FALSE);
         if (r->impl != c->impl)
             return setError(EGL_BAD_MATCH, EGL_FALSE);
-        read = r->surface;
+        impl_read = r->surface;
     }
     EGLBoolean result = c->cnx->hooks->egl.eglMakeCurrent(
-            dp->dpys[c->impl], draw, read, c->context);
+            dp->dpys[c->impl], impl_draw, impl_read, c->context);
 
     if (result == EGL_TRUE) {
         setGlThreadSpecific(c->cnx->hooks);
index f9dc5f1..4c902c8 100644 (file)
@@ -118,6 +118,11 @@ request_gpu_t* gpu_acquire(void* user)
         return 0;
     }
 
+    if (info.regs == 0) {
+        LOGD("requestGPU() failed");
+        return 0;
+    }
+
     bool failed = false;
     request_gpu_t* gpu = &gRegions;
     memset(gpu, 0, sizeof(*gpu));
index 46958d3..e193483 100644 (file)
@@ -5,7 +5,7 @@ include $(CLEAR_VARS)
 LOCAL_SRC_FILES:= app-linux.c demo.c.arm
 LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM libui
 LOCAL_MODULE:= angeles
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
 
 
@@ -13,5 +13,5 @@ include $(CLEAR_VARS)
 LOCAL_SRC_FILES:= gpustate.c
 LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM
 LOCAL_MODULE:= gpustate
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
index a448f0d..31b7d9a 100644 (file)
@@ -12,6 +12,6 @@ LOCAL_SHARED_LIBRARIES := \
 
 LOCAL_MODULE:= test-opengl-filter
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
index 26836c1..8b46cd7 100644 (file)
@@ -12,6 +12,6 @@ LOCAL_SHARED_LIBRARIES := \
 
 LOCAL_MODULE:= test-opengl-finish
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/lighting1709/Android.mk b/opengl/tests/lighting1709/Android.mk
new file mode 100644 (file)
index 0000000..9563e61
--- /dev/null
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LightingTest
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..6c23d42
--- /dev/null
@@ -0,0 +1,13 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.lightingtest">
+
+    <application>
+        <activity android:name="ClearActivity" android:label="LightingTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
new file mode 100644 (file)
index 0000000..3ae8c5c
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.android.lightingtest;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.MotionEvent;
+
+public class ClearActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mGLView = new ClearGLSurfaceView(this);
+        setContentView(mGLView);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mGLView.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mGLView.onResume();
+    }
+    private GLSurfaceView mGLView;
+}
+
+class ClearGLSurfaceView extends GLSurfaceView {
+    public ClearGLSurfaceView(Context context) {
+        super(context);
+        mRenderer = new ClearRenderer();
+        setRenderer(mRenderer);
+    }
+
+    ClearRenderer mRenderer;
+}
+
+class ClearRenderer implements GLSurfaceView.Renderer {
+    public ClearRenderer() {
+    }
+
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        // Do nothing special.
+    }
+
+    public void onSurfaceChanged(GL10 gl, int w, int h) {
+        // Compute the projection matrix
+        gl.glMatrixMode(GL10.GL_PROJECTION);
+        gl.glLoadIdentity();
+
+        // Compute the boundaries of the frustum
+        float fl = (float) (-(w / 2)) / 288;
+        float fr = (float) (w / 2) / 288;
+        float ft = (float) (h / 2) / 288;
+        float fb = (float) (-(h / 2)) / 288;
+
+        // Set the view frustum
+        gl.glFrustumf(fl, fr, fb, ft, 1.0f, 2000.0f);
+
+        // Set the viewport dimensions
+        gl.glMatrixMode(GL10.GL_MODELVIEW);
+        gl.glLoadIdentity();
+        gl.glViewport(0, 0, w, h);
+    }
+
+    public void onDrawFrame(GL10 gl) {
+        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+        final float lightOff[]        = {0.0f, 0.0f,  0.0f, 1.0f};
+        final float lightAmbient[]    = {5.0f, 0.0f,  0.0f, 1.0f};
+        final float lightDiffuse[]    = {0.0f, 2.0f,  0.0f, 0.0f};
+        final float lightPosSpot[]    = {0.0f, 0.0f, -8.0f, 1.0f};
+
+        final float pos[] = {
+                    -5.0f, -1.5f, 0.0f,
+                     0.0f, -1.5f, 0.0f,
+                     5.0f, -1.5f, 0.0f,
+                };
+        
+        final float v[] = new float[9];
+        ByteBuffer vbb = ByteBuffer.allocateDirect(v.length*4);
+        vbb.order(ByteOrder.nativeOrder());
+        FloatBuffer vb = vbb.asFloatBuffer();
+
+        gl.glDisable(GL10.GL_DITHER);
+
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightOff, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosSpot, 0);
+        gl.glEnable(GL10.GL_LIGHT0);
+        
+        gl.glEnable(GL10.GL_LIGHTING);
+
+
+        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+        gl.glNormal3f(0, 0, 1);
+        
+
+        // draw first 3 triangles, without using transforms
+        for (int i=0 ; i<3 ; i++) {
+            v[0] = -1; v[1] =-1; v[2] = -10;
+            v[3] =  0; v[4] = 1; v[5] = -10;
+            v[6] =  1; v[7] =-1; v[8] = -10;
+            for (int j=0 ; j<3 ; j++) {
+                v[j*3+0] -= pos[i*3+0];
+                v[j*3+1] -= pos[i*3+1];
+                v[j*3+2] -= pos[i*3+2];
+            }
+            vb.put(v).position(0);
+            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        }
+        
+        // draw the 2nd batch this time with transforms
+        v[0] = -1; v[1] =-1; v[2] = -10;
+        v[3] =  0; v[4] = 1; v[5] = -10;
+        v[6] =  1; v[7] =-1; v[8] = -10;
+        vb.put(v).position(0);
+        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+
+        // draw lower left triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[0], pos[1], pos[2]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower middle triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[3], pos[4], pos[5]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower right triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[6], pos[7], pos[8]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();      
+    }
+
+    public int[] getConfigSpec() {
+        int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
+        return configSpec;      
+    }
+}
+
index a8c6220..8d5f56d 100644 (file)
@@ -12,6 +12,6 @@ LOCAL_SHARED_LIBRARIES := \
 
 LOCAL_MODULE:= test-opengl-textures
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
index 5cd1f04..76fd8dd 100644 (file)
@@ -12,6 +12,6 @@ LOCAL_SHARED_LIBRARIES := \
 
 LOCAL_MODULE:= test-opengl-tritex
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
index 011a6ed..9bff0b2 100755 (executable)
@@ -4,14 +4,31 @@ set -e
 rm -rf out generated
 
 mkdir out
+
+# Create dummy Java files for Android APIs that are used by the code we generate.
+# This allows us to test the generated code without building the rest of Android.
+
 mkdir -p out/javax/microedition/khronos/opengles
 mkdir -p out/com/google/android/gles_jni
+mkdir -p out/android/app
 mkdir -p out/android/graphics
 mkdir -p out/android/opengl
+mkdir -p out/android/content
+mkdir -p out/android/content/pm
+mkdir -p out/android/os
+mkdir -p out/android/util
 
 echo "package android.graphics;" > out/android/graphics/Canvas.java
 echo "public interface Canvas {}" >> out/android/graphics/Canvas.java
 
+echo "package android.app; import android.content.pm.IPackageManager; public class ActivityThread { public static final ActivityThread currentActivityThread() { return null; } public static final String currentPackageName(){ return null; } public static IPackageManager getPackageManager() { return null;} }" > out/android/app/ActivityThread.java
+# echo "package android.content; import android.content.pm.PackageManager; public interface Context { public PackageManager getPackageManager(); }" > out/android/content/Context.java
+echo "package android.content.pm; public class ApplicationInfo {public int targetSdkVersion;}" > out/android/content/pm/ApplicationInfo.java
+echo "package android.content.pm; public interface IPackageManager {ApplicationInfo getApplicationInfo(java.lang.String packageName, int flags) throws android.os.RemoteException;}" > out/android/content/pm/IPackageManager.java
+echo "package android.os; public class Build {public static class VERSION_CODES { public static final int CUPCAKE = 3;};       }" > out/android/os/Build.java
+echo "package android.os; public class RemoteException extends Exception {}" > out/android/os/RemoteException.java
+echo "package android.util; public class Log {public static void w(String a, String b) {} public static void e(String a, String b) {}}" > out/android/util/Log.java
+
 GLFILE=out/javax/microedition/khronos/opengles/GL.java
 cp stubs/jsr239/GLHeader.java-if $GLFILE
 
index b0997b1..7340357 100644 (file)
@@ -890,16 +890,10 @@ public class JniCodeEmitter {
                                 cname +
                                 " = (" +
                                 cfunc.getArgType(cIndex).getDeclaration() +
-                                ") _env->GetDirectBufferAddress(" +
-                                (mUseCPlusPlus ? "" : "_env, ") +
+                                ") getDirectBufferPointer(_env, " +
                                 cname + "_buf);");
                         String iii = "    ";
-                        out.println(iii + indent + "if ( ! " + cname + " ) {");
-                        out.println(iii + iii + indent +
-                                (mUseCPlusPlus ? "_env" : "(*_env)") +
-                                "->ThrowNew(" +
-                                (mUseCPlusPlus ? "" : "_env, ") +
-                                "IAEClass, \"Must use a native order direct Buffer\");");
+                        out.println(iii + indent + "if ( ! " + cname + " ) {");        
                         out.println(iii + iii + indent + "return;");
                         out.println(iii + indent + "}");
                     } else {
index 3948fd3..e1c09f4 100644 (file)
@@ -132,6 +132,19 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
                                           commit ? 0 : JNI_ABORT);
 }
 
+static void *
+getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
+    char* buf = (char*) _env->GetDirectBufferAddress(buffer);
+    if (buf) {
+        jint position = _env->GetIntField(buffer, positionID);
+        jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+        buf += position << elementSizeShift;
+    } else {
+        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+    }
+    return (void*) buf;
+}
+
 static int
 getNumCompressedTextureFormats() {
     int numCompressedTextureFormats = 0;
index 11c6087..4494643 100644 (file)
@@ -44,9 +44,11 @@ static jclass OOMEClass;
 static jclass UOEClass;
 static jclass IAEClass;
 static jclass AIOOBEClass;
+static jclass G11ImplClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
+static jmethodID allowIndirectBuffersID;
 static jfieldID positionID;
 static jfieldID limitID;
 static jfieldID elementSizeShiftID;
@@ -62,13 +64,17 @@ nativeClassInitBuffer(JNIEnv *_env)
     jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
     bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
 
+    jclass g11impClassLocal = _env->FindClass("com/google/android/gles_jni/GLImpl");
+    G11ImplClass = (jclass) _env->NewGlobalRef(g11impClassLocal);
+
     getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
             "getBasePointer", "(Ljava/nio/Buffer;)J");
     getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
             "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
     getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
             "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
-
+    allowIndirectBuffersID = _env->GetStaticMethodID(g11impClassLocal,
+            "allowIndirectBuffers", "(Ljava/lang/String;)Z");
     positionID = _env->GetFieldID(bufferClass, "position", "I");
     limitID = _env->GetFieldID(bufferClass, "limit", "I");
     elementSizeShiftID =
@@ -118,6 +124,9 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
     
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
+    if (*array == NULL) {
+        return (void*) NULL;
+    }
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
@@ -132,6 +141,45 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
                                           commit ? 0 : JNI_ABORT);
 }
 
+extern "C" {
+extern char*  __progname;
+}
+
+static bool
+allowIndirectBuffers(JNIEnv *_env) {
+    static jint sIndirectBufferCompatability;
+    if (sIndirectBufferCompatability == 0) {
+        jobject appName = _env->NewStringUTF(::__progname);
+        sIndirectBufferCompatability = _env->CallStaticBooleanMethod(G11ImplClass, allowIndirectBuffersID, appName) ? 2 : 1;
+    }
+    return sIndirectBufferCompatability == 2;
+}
+
+static void *
+getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
+    if (!buffer) {
+        return NULL;
+    }
+    void* buf = _env->GetDirectBufferAddress(buffer);
+    if (buf) {
+        jint position = _env->GetIntField(buffer, positionID);
+        jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+        buf = ((char*) buf) + (position << elementSizeShift);
+    } else {
+        if (allowIndirectBuffers(_env)) {
+            jarray array = 0;
+            jint remaining;
+            buf = getPointer(_env, buffer, &array, &remaining);
+            if (array) {
+                releasePointer(_env, array, buf, 0);
+            }
+        } else {
+            _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        }
+    }
+    return buf;
+}
+
 static int
 getNumCompressedTextureFormats() {
     int numCompressedTextureFormats = 0;
index db3a41c..fe60c5d 100644 (file)
 
 package com.google.android.gles_jni;
 
+import android.app.ActivityThread;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.os.Build;
+import android.util.Log;
+
 import java.nio.Buffer;
 import javax.microedition.khronos.opengles.GL10;
 import javax.microedition.khronos.opengles.GL10Ext;
@@ -42,7 +48,28 @@ public class GLImpl implements GL10, GL10Ext, GL11, GL11Ext, GL11ExtensionPack {
     public GLImpl() {
     }
 
-     public void glGetPointerv(int pname, java.nio.Buffer[] params) {
-         throw new UnsupportedOperationException("glGetPointerv");
-     }
+    public void glGetPointerv(int pname, java.nio.Buffer[] params) {
+        throw new UnsupportedOperationException("glGetPointerv");
+    }
+
+    private static boolean allowIndirectBuffers(String appName) {
+        boolean result = false;
+        int version = 0;
+        IPackageManager pm = ActivityThread.getPackageManager();
+        try {
+            ApplicationInfo applicationInfo = pm.getApplicationInfo(appName, 0);
+            if (applicationInfo != null) {
+                version = applicationInfo.targetSdkVersion;
+            }
+        } catch (android.os.RemoteException e) {
+            // ignore
+        }
+        Log.e("OpenGLES", String.format(
+            "Application %s (SDK target %d) called a GL11 Pointer method with an indirect Buffer.",
+            appName, version));
+        if (version <= Build.VERSION_CODES.CUPCAKE) {
+            result = true;
+        }
+        return result;
+    }
 
diff --git a/vpn/java/android/net/vpn/IVpnService.aidl b/vpn/java/android/net/vpn/IVpnService.aidl
new file mode 100644 (file)
index 0000000..fedccb0
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+import android.net.vpn.VpnProfile;
+
+/**
+ * Interface to access a VPN service.
+ * {@hide}
+ */
+interface IVpnService {
+    /**
+     * Sets up the VPN connection.
+     * @param profile the profile object
+     * @param username the username for authentication
+     * @param password the corresponding password for authentication
+     */
+    boolean connect(in VpnProfile profile, String username, String password);
+
+    /**
+     * Tears down the VPN connection.
+     */
+    void disconnect();
+
+    /**
+     * Makes the service broadcast the connectivity state.
+     */
+    void checkStatus(in VpnProfile profile);
+}
diff --git a/vpn/java/android/net/vpn/L2tpIpsecProfile.java b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
new file mode 100644 (file)
index 0000000..4ae2dec
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+import android.os.Parcel;
+
+/**
+ * The profile for certificate-based L2TP-over-IPSec type of VPN.
+ * {@hide}
+ */
+public class L2tpIpsecProfile extends L2tpProfile {
+    private static final long serialVersionUID = 1L;
+
+    private String mUserCertificate;
+    private String mCaCertificate;
+
+    @Override
+    public VpnType getType() {
+        return VpnType.L2TP_IPSEC;
+    }
+
+    public void setCaCertificate(String name) {
+        mCaCertificate = name;
+    }
+
+    public String getCaCertificate() {
+        return mCaCertificate;
+    }
+
+    public void setUserCertificate(String name) {
+        mUserCertificate = name;
+    }
+
+    public String getUserCertificate() {
+        return mUserCertificate;
+    }
+
+    @Override
+    protected void readFromParcel(Parcel in) {
+        super.readFromParcel(in);
+        mCaCertificate = in.readString();
+        mUserCertificate = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        super.writeToParcel(parcel, flags);
+        parcel.writeString(mCaCertificate);
+        parcel.writeString(mUserCertificate);
+    }
+}
diff --git a/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java
new file mode 100644 (file)
index 0000000..7a03018
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+import android.os.Parcel;
+
+/**
+ * The profile for pre-shared-key-based L2TP-over-IPSec type of VPN.
+ * {@hide}
+ */
+public class L2tpIpsecPskProfile extends L2tpProfile {
+    private static final long serialVersionUID = 1L;
+
+    private String mPresharedKey;
+
+    @Override
+    public VpnType getType() {
+        return VpnType.L2TP_IPSEC_PSK;
+    }
+
+    public void setPresharedKey(String key) {
+        mPresharedKey = key;
+    }
+
+    public String getPresharedKey() {
+        return mPresharedKey;
+    }
+
+    @Override
+    protected void readFromParcel(Parcel in) {
+        super.readFromParcel(in);
+        mPresharedKey = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        super.writeToParcel(parcel, flags);
+        parcel.writeString(mPresharedKey);
+    }
+}
diff --git a/vpn/java/android/net/vpn/L2tpProfile.java b/vpn/java/android/net/vpn/L2tpProfile.java
new file mode 100644 (file)
index 0000000..dbba0c5
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+import android.os.Parcel;
+
+/**
+ * The profile for L2TP type of VPN.
+ * {@hide}
+ */
+public class L2tpProfile extends VpnProfile {
+    private static final long serialVersionUID = 1L;
+
+    private boolean mSecret;
+    private String mSecretString;
+
+    @Override
+    public VpnType getType() {
+        return VpnType.L2TP;
+    }
+
+    /**
+     * Enables/disables the secret for authenticating tunnel connection.
+     */
+    public void setSecretEnabled(boolean enabled) {
+        mSecret = enabled;
+    }
+
+    public boolean isSecretEnabled() {
+        return mSecret;
+    }
+
+    public void setSecretString(String secret) {
+        mSecretString = secret;
+    }
+
+    public String getSecretString() {
+        return mSecretString;
+    }
+
+    @Override
+    protected void readFromParcel(Parcel in) {
+        super.readFromParcel(in);
+        mSecret = in.readInt() > 0;
+        mSecretString = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        super.writeToParcel(parcel, flags);
+        parcel.writeInt(mSecret ? 1 : 0);
+        parcel.writeString(mSecretString);
+    }
+}
diff --git a/vpn/java/android/net/vpn/PptpProfile.java b/vpn/java/android/net/vpn/PptpProfile.java
new file mode 100644 (file)
index 0000000..c68bb71
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+/**
+ * The profile for PPTP type of VPN.
+ * {@hide}
+ */
+public class PptpProfile extends VpnProfile {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public VpnType getType() {
+        return VpnType.PPTP;
+    }
+}
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
new file mode 100644 (file)
index 0000000..dc70b26
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.util.Log;
+
+/**
+ * The class provides interface to manage all VPN-related tasks, including:
+ * <ul>
+ * <li>The list of supported VPN types.
+ * <li>API's to start/stop the service of a particular type.
+ * <li>API's to start the settings activity.
+ * <li>API's to create a profile.
+ * <li>API's to register/unregister a connectivity receiver and the keys to
+ *      access the fields in a connectivity broadcast event.
+ * </ul>
+ * {@hide}
+ */
+public class VpnManager {
+    // Action for broadcasting a connectivity state.
+    private static final String ACTION_VPN_CONNECTIVITY = "vpn.connectivity";
+    /** Key to the profile name of a connectivity broadcast event. */
+    public static final String BROADCAST_PROFILE_NAME = "profile_name";
+    /** Key to the connectivity state of a connectivity broadcast event. */
+    public static final String BROADCAST_CONNECTION_STATE = "connection_state";
+
+    public static final String PROFILES_PATH = "/data/misc/vpn/profiles";
+
+    private static final String PACKAGE_PREFIX =
+            VpnManager.class.getPackage().getName() + ".";
+
+    // Action to start VPN service
+    private static final String ACTION_VPN_SERVICE = PACKAGE_PREFIX + "SERVICE";
+
+    // Action to start VPN settings
+    private static final String ACTION_VPN_SETTINGS = PACKAGE_PREFIX + "SETTINGS";
+
+    private static final String TAG = VpnManager.class.getSimpleName();
+
+    /**
+     * Returns all supported VPN types.
+     */
+    public static VpnType[] getSupportedVpnTypes() {
+        return VpnType.values();
+    }
+
+    private Context mContext;
+
+    /**
+     * Creates a manager object with the specified context.
+     */
+    public VpnManager(Context c) {
+        mContext = c;
+    }
+
+    /**
+     * Creates a VPN profile of the specified type.
+     *
+     * @param type the VPN type
+     * @return the profile object
+     */
+    public VpnProfile createVpnProfile(VpnType type) {
+        return createVpnProfile(type, false);
+    }
+
+    /**
+     * Creates a VPN profile of the specified type.
+     *
+     * @param type the VPN type
+     * @param customized true if the profile is custom made
+     * @return the profile object
+     */
+    public VpnProfile createVpnProfile(VpnType type, boolean customized) {
+        try {
+            VpnProfile p = (VpnProfile) type.getProfileClass().newInstance();
+            p.setCustomized(customized);
+            return p;
+        } catch (InstantiationException e) {
+            return null;
+        } catch (IllegalAccessException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Starts the VPN service to establish VPN connection.
+     */
+    public void startVpnService() {
+        mContext.startService(new Intent(ACTION_VPN_SERVICE));
+    }
+
+    /**
+     * Stops the VPN service.
+     */
+    public void stopVpnService() {
+        mContext.stopService(new Intent(ACTION_VPN_SERVICE));
+    }
+
+    /**
+     * Binds the specified ServiceConnection with the VPN service.
+     */
+    public boolean bindVpnService(ServiceConnection c) {
+        if (!mContext.bindService(new Intent(ACTION_VPN_SERVICE), c, 0)) {
+            Log.w(TAG, "failed to connect to VPN service");
+            return false;
+        } else {
+            Log.d(TAG, "succeeded to connect to VPN service");
+            return true;
+        }
+    }
+
+    /** Broadcasts the connectivity state of the specified profile. */
+    public void broadcastConnectivity(String profileName, VpnState s) {
+        Intent intent = new Intent(ACTION_VPN_CONNECTIVITY);
+        intent.putExtra(BROADCAST_PROFILE_NAME, profileName);
+        intent.putExtra(BROADCAST_CONNECTION_STATE, s);
+        mContext.sendBroadcast(intent);
+    }
+
+    public void registerConnectivityReceiver(BroadcastReceiver r) {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(VpnManager.ACTION_VPN_CONNECTIVITY);
+        mContext.registerReceiver(r, filter);
+    }
+
+    public void unregisterConnectivityReceiver(BroadcastReceiver r) {
+        mContext.unregisterReceiver(r);
+    }
+
+    /** Starts the VPN settings activity. */
+    public void startSettingsActivity() {
+        Intent intent = new Intent(ACTION_VPN_SETTINGS);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+
+    /** Creates an intent to start the VPN settings activity. */
+    public Intent createSettingsActivityIntent() {
+        Intent intent = new Intent(ACTION_VPN_SETTINGS);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
+}
diff --git a/vpn/java/android/net/vpn/VpnProfile.aidl b/vpn/java/android/net/vpn/VpnProfile.aidl
new file mode 100644 (file)
index 0000000..edeaef0
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+parcelable VpnProfile;
diff --git a/vpn/java/android/net/vpn/VpnProfile.java b/vpn/java/android/net/vpn/VpnProfile.java
new file mode 100644 (file)
index 0000000..bd6c809
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * A VPN profile.
+ * {@hide}
+ */
+public abstract class VpnProfile implements Parcelable, Serializable {
+    private static final long serialVersionUID = 1L;
+    private String mName; // unique display name
+    private String mId; // unique identifier
+    private String mServerName; // VPN server name
+    private String mDomainSuffices; // space separated list
+    private String mRouteList; // space separated list
+    private String mSavedUsername;
+    private boolean mIsCustomized;
+    private transient VpnState mState = VpnState.IDLE;
+
+    /** Sets a user-friendly name for this profile. */
+    public void setName(String name) {
+        mName = name;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Sets an ID for this profile.  The caller should make sure the
+     * uniqueness of the ID.
+     */
+    public void setId(String id) {
+        mId = id;
+    }
+
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Sets the name of the VPN server. Used for DNS lookup.
+     */
+    public void setServerName(String name) {
+        mServerName = name;
+    }
+
+    public String getServerName() {
+        return mServerName;
+    }
+
+    /**
+     * Sets the domain suffices for DNS resolution.
+     *
+     * @param entries a comma-separated list of domain suffices
+     */
+    public void setDomainSuffices(String entries) {
+        mDomainSuffices = entries;
+    }
+
+    public String getDomainSuffices() {
+        return mDomainSuffices;
+    }
+
+    /**
+     * Sets the routing info for this VPN connection.
+     *
+     * @param entries a comma-separated list of routes; each entry is in the
+     *      format of "(network address)/(network mask)"
+     */
+    public void setRouteList(String entries) {
+        mRouteList = entries;
+    }
+
+    public String getRouteList() {
+        return mRouteList;
+    }
+
+    public void setSavedUsername(String name) {
+        mSavedUsername = name;
+    }
+
+    public String getSavedUsername() {
+        return mSavedUsername;
+    }
+
+    public void setState(VpnState state) {
+        mState = state;
+    }
+
+    public VpnState getState() {
+        return ((mState == null) ? VpnState.IDLE : mState);
+    }
+
+    public boolean isIdle() {
+        return (mState == VpnState.IDLE);
+    }
+
+    /**
+     * Returns whether this profile is custom made (as opposed to being
+     * created by provided user interface).
+     */
+    public boolean isCustomized() {
+        return mIsCustomized;
+    }
+
+    /**
+     * Returns the VPN type of the profile.
+     */
+    public abstract VpnType getType();
+
+    void setCustomized(boolean customized) {
+        mIsCustomized = customized;
+    }
+
+    protected void readFromParcel(Parcel in) {
+        mName = in.readString();
+        mId = in.readString();
+        mServerName = in.readString();
+        mDomainSuffices = in.readString();
+        mRouteList = in.readString();
+        mSavedUsername = in.readString();
+    }
+
+    public static final Parcelable.Creator<VpnProfile> CREATOR =
+            new Parcelable.Creator<VpnProfile>() {
+                public VpnProfile createFromParcel(Parcel in) {
+                    VpnType type = Enum.valueOf(VpnType.class, in.readString());
+                    boolean customized = in.readInt() > 0;
+                    VpnProfile p = new VpnManager(null).createVpnProfile(type,
+                            customized);
+                    if (p == null) return null;
+                    p.readFromParcel(in);
+                    return p;
+                }
+
+                public VpnProfile[] newArray(int size) {
+                    return new VpnProfile[size];
+                }
+            };
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(getType().toString());
+        parcel.writeInt(mIsCustomized ? 1 : 0);
+        parcel.writeString(mName);
+        parcel.writeString(mId);
+        parcel.writeString(mServerName);
+        parcel.writeString(mDomainSuffices);
+        parcel.writeString(mRouteList);
+        parcel.writeString(mSavedUsername);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/vpn/java/android/net/vpn/VpnState.java b/vpn/java/android/net/vpn/VpnState.java
new file mode 100644 (file)
index 0000000..ebd9364
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+/**
+ * Enumeration of all VPN states.
+ *
+ * A normal VPN connection lifetime starts in {@link IDLE}. When a new
+ * connection is about to be set up, it goes to {@link CONNECTING} and then
+ * {@link CONNECTED} if successful; back to {@link IDLE} if failed.
+ * When the connection is about to be torn down, it goes to
+ * {@link DISCONNECTING} and then {@link IDLE}.
+ * {@link CANCELLED} is a state when a VPN connection attempt is aborted, and
+ * is in transition to {@link IDLE}.
+ * {@hide}
+ */
+public enum VpnState {
+    CONNECTING, DISCONNECTING, CANCELLED, CONNECTED, IDLE
+}
diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java
new file mode 100644 (file)
index 0000000..c7df943
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009, 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.
+ */
+
+package android.net.vpn;
+
+/**
+ * Enumeration of all supported VPN types.
+ * {@hide}
+ */
+public enum VpnType {
+    PPTP("PPTP", "", PptpProfile.class),
+    L2TP("L2TP", "", L2tpProfile.class),
+    L2TP_IPSEC_PSK("L2TP/IPSec PSK", "Pre-shared key based L2TP/IPSec VPN",
+            L2tpIpsecPskProfile.class),
+    L2TP_IPSEC("L2TP/IPSec CRT", "Certificate based L2TP/IPSec VPN",
+            L2tpIpsecProfile.class);
+
+    private String mDisplayName;
+    private String mDescription;
+    private Class<? extends VpnProfile> mClass;
+
+    VpnType(String displayName, String description,
+            Class<? extends VpnProfile> klass) {
+        mDisplayName = displayName;
+        mDescription = description;
+        mClass = klass;
+    }
+
+    public String getDisplayName() {
+        return mDisplayName;
+    }
+
+    public String getDescription() {
+        return mDescription;
+    }
+
+    public Class<? extends VpnProfile> getProfileClass() {
+        return mClass;
+    }
+}