Merge "Fix the test breakage in vCard tests." into gingerbread
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f7ccc12..ed5af91 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3898,7 +3898,14 @@
}
final void performPause() {
+ mCalled = false;
onPause();
+ if (!mCalled && getApplicationInfo().targetSdkVersion
+ >= android.os.Build.VERSION_CODES.GINGERBREAD) {
+ throw new SuperNotCalledException(
+ "Activity " + mComponent.toShortString() +
+ " did not call through to super.onPause()");
+ }
}
final void performUserLeaving() {
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index 1eb178e..d016dfa 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -54,11 +54,6 @@
// ----------------------------------------------------------------------------
-// 4 * (11 + 7 + (1 + 2*NUM_RECT_MAX) * NUM_BUFFER_MAX) * NUM_LAYERS_MAX
-// 4 * (11 + 7 + (1 + 2*7)*16) * 31
-// 1032 * 31
-// = ~27 KiB (31992)
-
class SharedBufferStack
{
friend class SharedClient;
@@ -85,7 +80,7 @@
};
struct FlatRegion { // 52 bytes = 4 * (1 + 2*N)
- static const unsigned int NUM_RECT_MAX = 6;
+ static const unsigned int NUM_RECT_MAX = 5;
uint32_t count;
SmallRect rects[NUM_RECT_MAX];
};
@@ -93,13 +88,18 @@
struct BufferData {
FlatRegion dirtyRegion;
SmallRect crop;
+ uint8_t transform;
+ uint8_t reserved[3];
};
SharedBufferStack();
void init(int32_t identity);
status_t setDirtyRegion(int buffer, const Region& reg);
status_t setCrop(int buffer, const Rect& reg);
+ status_t setTransform(int buffer, uint8_t transform);
Region getDirtyRegion(int buffer) const;
+ Rect getCrop(int buffer) const;
+ uint32_t getTransform(int buffer) const;
// these attributes are part of the conditions/updates
volatile int32_t head; // server's current front buffer
@@ -117,7 +117,7 @@
int32_t reserved32[1];
Statistics stats;
int32_t reserved;
- BufferData buffers[NUM_BUFFER_MAX]; // 960 bytes
+ BufferData buffers[NUM_BUFFER_MAX]; // 1024 bytes
};
// ----------------------------------------------------------------------------
@@ -206,7 +206,7 @@
bool needNewBuffer(int buffer) const;
status_t setDirtyRegion(int buffer, const Region& reg);
status_t setCrop(int buffer, const Rect& reg);
-
+ status_t setTransform(int buffer, uint32_t transform);
class SetBufferCountCallback {
friend class SharedBufferClient;
@@ -275,6 +275,8 @@
status_t reallocateAllExcept(int buffer);
int32_t getQueuedCount() const;
Region getDirtyRegion(int buffer) const;
+ Rect getCrop(int buffer) const;
+ uint32_t getTransform(int buffer) const;
status_t resize(int newNumBuffers);
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 294c867..7c5a39b 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -216,6 +216,7 @@
int dispatch_crop(va_list args);
int dispatch_set_buffer_count(va_list args);
int dispatch_set_buffers_geometry(va_list args);
+ int dispatch_set_buffers_transform(va_list args);
void setUsage(uint32_t reqUsage);
int connect(int api);
@@ -223,6 +224,7 @@
int crop(Rect const* rect);
int setBufferCount(int bufferCount);
int setBuffersGeometry(int w, int h, int format);
+ int setBuffersTransform(int transform);
/*
* private stuff...
@@ -278,6 +280,7 @@
Rect mSwapRectangle;
int mConnected;
Rect mNextBufferCrop;
+ uint32_t mNextBufferTransform;
BufferInfo mBufferInfo;
// protected by mSurfaceLock. These are also used from lock/unlock
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index ca89b06..d59d72b 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -85,6 +85,7 @@
NATIVE_WINDOW_SET_CROP,
NATIVE_WINDOW_SET_BUFFER_COUNT,
NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
+ NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
};
/* parameter for NATIVE_WINDOW_[DIS]CONNECT */
@@ -92,6 +93,20 @@
NATIVE_WINDOW_API_EGL = 1
};
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
+enum {
+ /* flip source image horizontally */
+ NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
+ /* flip source image vertically */
+ NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
+ /* rotate source image 90 degrees clock-wise */
+ NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
+ /* rotate source image 180 degrees */
+ NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
+ /* rotate source image 270 degrees clock-wise */
+ NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
+};
+
struct ANativeWindow
{
#ifdef __cplusplus
@@ -196,6 +211,7 @@
* NATIVE_WINDOW_SET_CROP
* NATIVE_WINDOW_SET_BUFFER_COUNT
* NATIVE_WINDOW_SET_BUFFERS_GEOMETRY
+ * NATIVE_WINDOW_SET_BUFFERS_TRANSFORM
*
*/
@@ -298,6 +314,19 @@
w, h, format);
}
+/*
+ * native_window_set_buffers_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified.
+ */
+static inline int native_window_set_buffers_transform(
+ ANativeWindow* window,
+ int transform)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+ transform);
+}
+
// ---------------------------------------------------------------------------
/* FIXME: this is legacy for pixmaps */
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 156a7db..4ad9f86 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -75,6 +75,14 @@
return NO_ERROR;
}
+status_t SharedBufferStack::setTransform(int buffer, uint8_t transform)
+{
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return BAD_INDEX;
+ buffers[buffer].transform = transform;
+ return NO_ERROR;
+}
+
status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
{
if (uint32_t(buffer) >= NUM_BUFFER_MAX)
@@ -137,6 +145,26 @@
return res;
}
+Rect SharedBufferStack::getCrop(int buffer) const
+{
+ Rect res(-1, -1);
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return res;
+ res.left = buffers[buffer].crop.l;
+ res.top = buffers[buffer].crop.t;
+ res.right = buffers[buffer].crop.r;
+ res.bottom = buffers[buffer].crop.b;
+ return res;
+}
+
+uint32_t SharedBufferStack::getTransform(int buffer) const
+{
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return 0;
+ return buffers[buffer].transform;
+}
+
+
// ----------------------------------------------------------------------------
SharedBufferBase::SharedBufferBase(SharedClient* sharedClient,
@@ -433,6 +461,12 @@
return stack.setCrop(buf, crop);
}
+status_t SharedBufferClient::setTransform(int buf, uint32_t transform)
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.setTransform(buf, uint8_t(transform));
+}
+
status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg)
{
SharedBufferStack& stack( *mSharedStack );
@@ -549,6 +583,18 @@
return stack.getDirtyRegion(buf);
}
+Rect SharedBufferServer::getCrop(int buf) const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.getCrop(buf);
+}
+
+uint32_t SharedBufferServer::getTransform(int buf) const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.getTransform(buf);
+}
+
/*
* NOTE: this is not thread-safe on the server-side, meaning
* 'head' cannot move during this operation. The client-side
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 5ab72cd..cb76091 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -422,8 +422,10 @@
const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
const_cast<uint32_t&>(ANativeWindow::flags) = 0;
+ mNextBufferTransform = 0;
mConnected = 0;
mSwapRectangle.makeInvalid();
+ mNextBufferCrop = Rect(0,0);
// two buffers by default
mBuffers.setCapacity(2);
mBuffers.insertAt(0, 2);
@@ -631,6 +633,7 @@
}
int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+ mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform);
mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop);
mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
err = mSharedBufferClient->queue(bufIdx);
@@ -685,6 +688,9 @@
case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
res = dispatch_set_buffers_geometry( args );
break;
+ case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+ res = dispatch_set_buffers_transform( args );
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -719,6 +725,11 @@
return setBuffersGeometry(w, h, f);
}
+int Surface::dispatch_set_buffers_transform(va_list args) {
+ int transform = va_arg(args, int);
+ return setBuffersTransform(transform);
+}
+
void Surface::setUsage(uint32_t reqUsage)
{
Mutex::Autolock _l(mSurfaceLock);
@@ -765,6 +776,10 @@
int Surface::crop(Rect const* rect)
{
+ // empty/invalid rects are not allowed
+ if (rect->isEmpty())
+ return BAD_VALUE;
+
Mutex::Autolock _l(mSurfaceLock);
// TODO: validate rect size
mNextBufferCrop = *rect;
@@ -804,6 +819,13 @@
return NO_ERROR;
}
+int Surface::setBuffersTransform(int transform)
+{
+ Mutex::Autolock _l(mSurfaceLock);
+ mNextBufferTransform = transform;
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
int Surface::getConnectedApi() const
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index f2653cf..11fdf56 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -196,6 +196,7 @@
mExtractorFlags(0),
mLastVideoBuffer(NULL),
mVideoBuffer(NULL),
+ mRTSPTimeOffset(0),
mSuspensionState(NULL) {
CHECK_EQ(mClient.connect(), OK);
@@ -393,7 +394,11 @@
mVideoBuffer = NULL;
}
- mRTSPController.clear();
+ if (mRTSPController != NULL) {
+ mRTSPController->disconnect();
+ mRTSPController.clear();
+ }
+
mRTPPusher.clear();
mRTCPPusher.clear();
mRTPSession.clear();
@@ -738,6 +743,10 @@
*positionUs = 0;
}
+ if (mRTSPController != NULL) {
+ *positionUs += mRTSPTimeOffset;
+ }
+
return OK;
}
@@ -753,6 +762,17 @@
}
status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
+ if (mRTSPController != NULL) {
+ pause_l();
+ mRTSPController->seek(timeUs);
+ play_l();
+
+ notifyListener_l(MEDIA_SEEK_COMPLETE);
+ mSeekNotificationSent = true;
+ mRTSPTimeOffset = timeUs;
+ return OK;
+ }
+
if (mFlags & CACHE_UNDERRUN) {
mFlags &= ~CACHE_UNDERRUN;
play_l();
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index 2542e4e..7020564 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -33,6 +33,8 @@
status_t connect(const char *url);
void disconnect();
+ void seek(int64_t timeUs);
+
virtual size_t countTracks();
virtual sp<MediaSource> getTrack(size_t index);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 55e2c36..49b5c78 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -180,6 +180,7 @@
sp<ALooper> mLooper;
sp<ARTSPController> mRTSPController;
+ int64_t mRTSPTimeOffset;
sp<ARTPSession> mRTPSession;
sp<UDPPusher> mRTPPusher, mRTCPPusher;
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 8c56cb7..b930184 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -406,9 +406,7 @@
const sp<ASessionDescription> &sessionDesc, size_t index)
: mInitCheck(NO_INIT),
mFormat(new MetaData),
- mEOSResult(OK),
- mFirstAccessUnit(true),
- mFirstAccessUnitNTP(0) {
+ mEOSResult(OK) {
unsigned long PT;
AString desc;
AString params;
@@ -550,9 +548,6 @@
}
status_t APacketSource::start(MetaData *params) {
- mFirstAccessUnit = true;
- mFirstAccessUnitNTP = 0;
-
return OK;
}
@@ -600,25 +595,6 @@
return;
}
- uint64_t ntpTime;
- CHECK(buffer->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
- if (mFirstAccessUnit) {
- mFirstAccessUnit = false;
- mFirstAccessUnitNTP = ntpTime;
- }
-
- if (ntpTime > mFirstAccessUnitNTP) {
- ntpTime -= mFirstAccessUnitNTP;
- } else {
- ntpTime = 0;
- }
-
- int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
-
- buffer->meta()->setInt64("timeUs", timeUs);
-
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
mCondition.signal();
@@ -632,31 +608,9 @@
mCondition.signal();
}
-int64_t APacketSource::getQueuedDuration(bool *eos) {
+void APacketSource::flushQueue() {
Mutex::Autolock autoLock(mLock);
-
- *eos = (mEOSResult != OK);
-
- if (mBuffers.empty()) {
- return 0;
- }
-
- sp<ABuffer> buffer = *mBuffers.begin();
-
- uint64_t ntpTime;
- CHECK(buffer->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
- int64_t firstTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
-
- buffer = *--mBuffers.end();
-
- CHECK(buffer->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
- int64_t lastTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
-
- return lastTimeUs - firstTimeUs;
+ mBuffers.clear();
}
} // namespace android
diff --git a/media/libstagefright/rtsp/APacketSource.h b/media/libstagefright/rtsp/APacketSource.h
index 647da6e..197af3e 100644
--- a/media/libstagefright/rtsp/APacketSource.h
+++ b/media/libstagefright/rtsp/APacketSource.h
@@ -43,7 +43,7 @@
void queueAccessUnit(const sp<ABuffer> &buffer);
void signalEOS(status_t result);
- int64_t getQueuedDuration(bool *eos);
+ void flushQueue();
protected:
virtual ~APacketSource();
@@ -58,9 +58,6 @@
List<sp<ABuffer> > mBuffers;
status_t mEOSResult;
- bool mFirstAccessUnit;
- uint64_t mFirstAccessUnitNTP;
-
DISALLOW_EVIL_CONSTRUCTORS(APacketSource);
};
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index ceae3a6..9df17cba 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -33,7 +33,7 @@
}
ARTSPController::~ARTSPController() {
- disconnect();
+ CHECK_EQ((int)mState, (int)DISCONNECTED);
mLooper->unregisterHandler(mReflector->id());
}
@@ -80,6 +80,16 @@
mHandler.clear();
}
+void ARTSPController::seek(int64_t timeUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mState != CONNECTED) {
+ return;
+ }
+
+ mHandler->seek(timeUs);
+}
+
size_t ARTSPController::countTracks() {
if (mHandler == NULL) {
return 0;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 90070c9..e248463 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -38,7 +38,10 @@
mConn(new ARTSPConnection),
mRTPConn(new ARTPConnection),
mSessionURL(url),
- mSetupTracksSuccessful(false) {
+ mSetupTracksSuccessful(false),
+ mSeekPending(false),
+ mFirstAccessUnit(true),
+ mFirstAccessUnitNTP(0) {
mNetLooper->start(false /* runOnCallingThread */,
false /* canCallJava */,
@@ -62,6 +65,12 @@
(new AMessage('abor', id()))->post();
}
+ void seek(int64_t timeUs) {
+ sp<AMessage> msg = new AMessage('seek', id());
+ msg->setInt64("time", timeUs);
+ msg->post();
+ }
+
virtual void onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case 'conn':
@@ -88,8 +97,6 @@
case 'disc':
{
- LOG(INFO) << "disconnect completed";
-
(new AMessage('quit', id()))->post();
break;
}
@@ -337,7 +344,20 @@
CHECK(accessUnit->meta()->findInt64(
"ntp-time", (int64_t *)&ntpTime));
- accessUnit->meta()->setInt64("ntp-time", ntpTime);
+ if (mFirstAccessUnit) {
+ mFirstAccessUnit = false;
+ mFirstAccessUnitNTP = ntpTime;
+ }
+
+ if (ntpTime >= mFirstAccessUnitNTP) {
+ ntpTime -= mFirstAccessUnitNTP;
+ } else {
+ ntpTime = 0;
+ }
+
+ int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+
+ accessUnit->meta()->setInt64("timeUs", timeUs);
#if 0
int32_t damaged;
@@ -353,6 +373,84 @@
break;
}
+ case 'seek':
+ {
+ if (mSeekPending) {
+ break;
+ }
+
+ int64_t timeUs;
+ CHECK(msg->findInt64("time", &timeUs));
+
+ mSeekPending = true;
+
+ AString request = "PAUSE ";
+ request.append(mSessionURL);
+ request.append(" RTSP/1.0\r\n");
+
+ request.append("Session: ");
+ request.append(mSessionID);
+ request.append("\r\n");
+
+ request.append("\r\n");
+
+ sp<AMessage> reply = new AMessage('see1', id());
+ reply->setInt64("time", timeUs);
+ mConn->sendRequest(request.c_str(), reply);
+ break;
+ }
+
+ case 'see1':
+ {
+ int64_t timeUs;
+ CHECK(msg->findInt64("time", &timeUs));
+
+ AString request = "PLAY ";
+ request.append(mSessionURL);
+ request.append(" RTSP/1.0\r\n");
+
+ request.append("Session: ");
+ request.append(mSessionID);
+ request.append("\r\n");
+
+ request.append(
+ StringPrintf(
+ "Range: npt=%lld-\r\n", timeUs / 1000000ll));
+
+ request.append("\r\n");
+
+ sp<AMessage> reply = new AMessage('see2', id());
+ mConn->sendRequest(request.c_str(), reply);
+ break;
+ }
+
+ case 'see2':
+ {
+ CHECK(mSeekPending);
+
+ LOG(INFO) << "seek completed.";
+ mSeekPending = false;
+
+ int32_t result;
+ CHECK(msg->findInt32("result", &result));
+ if (result != OK) {
+ LOG(ERROR) << "seek FAILED";
+ break;
+ }
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("response", &obj));
+ sp<ARTSPResponse> response =
+ static_cast<ARTSPResponse *>(obj.get());
+
+ CHECK_EQ(response->mStatusCode, 200u);
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ mTracks.editItemAt(i).mPacketSource->flushQueue();
+ }
+ break;
+ }
+
default:
TRESPASS();
break;
@@ -380,6 +478,9 @@
AString mBaseURL;
AString mSessionID;
bool mSetupTracksSuccessful;
+ bool mSeekPending;
+ bool mFirstAccessUnit;
+ uint64_t mFirstAccessUnitNTP;
struct TrackInfo {
int mRTPSocket;
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 185d72a9..8349fe6 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -47,7 +47,8 @@
<bool name="def_networks_available_notification_on">true</bool>
<bool name="def_backup_enabled">false</bool>
- <string name="def_backup_transport" translatable="false"></string>
+ <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string>
+
<!-- Default value for whether or not to pulse the notification LED when there is a
pending notification -->
<bool name="def_notification_pulse">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2e95932..81d82de 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -22,6 +22,8 @@
import java.security.SecureRandom;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import android.app.backup.BackupManager;
import android.content.ContentProvider;
@@ -37,6 +39,7 @@
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
+import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.provider.DrmStore;
@@ -56,9 +59,15 @@
// Cache for settings, access-ordered for acting as LRU.
// Guarded by themselves.
- private static final int MAX_CACHE_ENTRIES = 50;
- private static final SettingsCache sSystemCache = new SettingsCache();
- private static final SettingsCache sSecureCache = new SettingsCache();
+ private static final int MAX_CACHE_ENTRIES = 200;
+ private static final SettingsCache sSystemCache = new SettingsCache("system");
+ private static final SettingsCache sSecureCache = new SettingsCache("secure");
+
+ // The count of how many known (handled by SettingsProvider)
+ // database mutations are currently being handled. Used by
+ // sFileObserver to not reload the database when it's ourselves
+ // modifying it.
+ private static final AtomicInteger sKnownMutationsInFlight = new AtomicInteger(0);
// Over this size we don't reject loading or saving settings but
// we do consider them broken/malicious and don't keep them in
@@ -67,6 +76,10 @@
private static final Bundle NULL_SETTING = Bundle.forPair("value", null);
+ // Used as a sentinel value in an instance equality test when we
+ // want to cache the existence of a key, but not store its value.
+ private static final Bundle TOO_LARGE_TO_CACHE_MARKER = Bundle.forPair("_dummy", null);
+
protected DatabaseHelper mOpenHelper;
private BackupManager mBackupManager;
@@ -201,6 +214,43 @@
}
}
+ // FileObserver for external modifications to the database file.
+ // Note that this is for platform developers only with
+ // userdebug/eng builds who should be able to tinker with the
+ // sqlite database out from under the SettingsProvider, which is
+ // normally the exclusive owner of the database. But we keep this
+ // enabled all the time to minimize development-vs-user
+ // differences in testing.
+ private static SettingsFileObserver sObserverInstance;
+ private class SettingsFileObserver extends FileObserver {
+ private final AtomicBoolean mIsDirty = new AtomicBoolean(false);
+ private final String mPath;
+
+ public SettingsFileObserver(String path) {
+ super(path, FileObserver.CLOSE_WRITE |
+ FileObserver.CREATE | FileObserver.DELETE |
+ FileObserver.MOVED_TO | FileObserver.MODIFY);
+ mPath = path;
+ }
+
+ public void onEvent(int event, String path) {
+ int modsInFlight = sKnownMutationsInFlight.get();
+ if (modsInFlight > 0) {
+ // our own modification.
+ return;
+ }
+ Log.d(TAG, "external modification to " + mPath + "; event=" + event);
+ if (!mIsDirty.compareAndSet(false, true)) {
+ // already handled. (we get a few update events
+ // during an sqlite write)
+ return;
+ }
+ Log.d(TAG, "updating our caches for " + mPath);
+ fullyPopulateCaches();
+ mIsDirty.set(false);
+ }
+ }
+
@Override
public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext());
@@ -210,9 +260,65 @@
return false;
}
+ // Watch for external modifications to the database file,
+ // keeping our cache in sync.
+ // It's kinda lame to call mOpenHelper.getReadableDatabase()
+ // during onCreate(), but since ensureAndroidIdIsSet has
+ // already done it above and initialized/upgraded the
+ // database, might as well just use it...
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ sObserverInstance = new SettingsFileObserver(db.getPath());
+ sObserverInstance.startWatching();
+ startAsyncCachePopulation();
return true;
}
+ private void startAsyncCachePopulation() {
+ new Thread("populate-settings-caches") {
+ public void run() {
+ fullyPopulateCaches();
+ }
+ }.start();
+ }
+
+ private void fullyPopulateCaches() {
+ fullyPopulateCache("secure", sSecureCache);
+ fullyPopulateCache("system", sSystemCache);
+ }
+
+ // Slurp all values (if sane in number & size) into cache.
+ private void fullyPopulateCache(String table, SettingsCache cache) {
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ Cursor c = db.query(
+ table,
+ new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE },
+ null, null, null, null, null,
+ "" + (MAX_CACHE_ENTRIES + 1) /* limit */);
+ try {
+ synchronized (cache) {
+ cache.clear();
+ cache.setFullyMatchesDisk(true); // optimistic
+ int rows = 0;
+ while (c.moveToNext()) {
+ rows++;
+ String name = c.getString(0);
+ String value = c.getString(1);
+ cache.populate(name, value);
+ }
+ if (rows > MAX_CACHE_ENTRIES) {
+ // Somewhat redundant, as removeEldestEntry() will
+ // have already done this, but to be explicit:
+ cache.setFullyMatchesDisk(false);
+ Log.d(TAG, "row count exceeds max cache entries for table " + table);
+ }
+ Log.d(TAG, "cache for settings table '" + table + "' fullycached=" +
+ cache.fullyMatchesDisk());
+ }
+ } finally {
+ c.close();
+ }
+ }
+
private boolean ensureAndroidIdIsSet() {
final Cursor c = query(Settings.Secure.CONTENT_URI,
new String[] { Settings.NameValueTable.VALUE },
@@ -262,7 +368,19 @@
private Bundle lookupValue(String table, SettingsCache cache, String key) {
synchronized (cache) {
if (cache.containsKey(key)) {
- return cache.get(key);
+ Bundle value = cache.get(key);
+ if (value != TOO_LARGE_TO_CACHE_MARKER) {
+ return value;
+ }
+ // else we fall through and read the value from disk
+ } else if (cache.fullyMatchesDisk()) {
+ // Fast path (very common). Don't even try touch disk
+ // if we know we've slurped it all in. Trying to
+ // touch the disk would mean waiting for yaffs2 to
+ // give us access, which could takes hundreds of
+ // milliseconds. And we're very likely being called
+ // from somebody's UI thread...
+ return NULL_SETTING;
}
}
@@ -338,6 +456,7 @@
checkWritePermissions(args);
SettingsCache cache = SettingsCache.forTable(args.table);
+ sKnownMutationsInFlight.incrementAndGet();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
@@ -350,6 +469,7 @@
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ sKnownMutationsInFlight.decrementAndGet();
}
sendNotify(uri);
@@ -449,8 +569,10 @@
return Uri.withAppendedPath(url, name);
}
+ sKnownMutationsInFlight.incrementAndGet();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final long rowId = db.insert(args.table, null, initialValues);
+ sKnownMutationsInFlight.decrementAndGet();
if (rowId <= 0) return null;
SettingsCache.populate(cache, initialValues); // before we notify
@@ -471,12 +593,15 @@
}
checkWritePermissions(args);
+ sKnownMutationsInFlight.incrementAndGet();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count = db.delete(args.table, args.where, args.args);
+ sKnownMutationsInFlight.decrementAndGet();
if (count > 0) {
SettingsCache.wipe(args.table); // before we notify
sendNotify(url);
}
+ startAsyncCachePopulation();
if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) deleted");
return count;
}
@@ -489,12 +614,15 @@
}
checkWritePermissions(args);
+ sKnownMutationsInFlight.incrementAndGet();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ sKnownMutationsInFlight.decrementAndGet();
int count = db.update(args.table, initialValues, args.where, args.args);
if (count > 0) {
SettingsCache.wipe(args.table); // before we notify
sendNotify(url);
}
+ startAsyncCachePopulation();
if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) <- " + initialValues);
return count;
}
@@ -506,12 +634,12 @@
* When a client attempts to openFile the default ringtone or
* notification setting Uri, we will proxy the call to the current
* default ringtone's Uri (if it is in the DRM or media provider).
- */
+ */
int ringtoneType = RingtoneManager.getDefaultType(uri);
// Above call returns -1 if the Uri doesn't match a default type
if (ringtoneType != -1) {
Context context = getContext();
-
+
// Get the current value for the default sound
Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
@@ -531,7 +659,7 @@
throw new FileNotFoundException(e.getMessage());
}
}
-
+
return context.getContentResolver().openFileDescriptor(soundUri, mode);
}
}
@@ -607,13 +735,38 @@
*/
private static final class SettingsCache extends LinkedHashMap<String, Bundle> {
- public SettingsCache() {
+ private final String mCacheName;
+ private boolean mCacheFullyMatchesDisk = false; // has the whole database slurped.
+
+ public SettingsCache(String name) {
super(MAX_CACHE_ENTRIES, 0.75f /* load factor */, true /* access ordered */);
+ mCacheName = name;
+ }
+
+ /**
+ * Is the whole database table slurped into this cache?
+ */
+ public boolean fullyMatchesDisk() {
+ synchronized (this) {
+ return mCacheFullyMatchesDisk;
+ }
+ }
+
+ public void setFullyMatchesDisk(boolean value) {
+ synchronized (this) {
+ mCacheFullyMatchesDisk = value;
+ }
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
- return size() > MAX_CACHE_ENTRIES;
+ if (size() <= MAX_CACHE_ENTRIES) {
+ return false;
+ }
+ synchronized (this) {
+ mCacheFullyMatchesDisk = false;
+ }
+ return true;
}
/**
@@ -658,11 +811,15 @@
return;
}
String value = contentValues.getAsString(Settings.NameValueTable.VALUE);
- synchronized (cache) {
+ cache.populate(name, value);
+ }
+
+ public void populate(String name, String value) {
+ synchronized (this) {
if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
- cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
+ put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
} else {
- cache.remove(name);
+ put(name, TOO_LARGE_TO_CACHE_MARKER);
}
}
}
@@ -678,6 +835,7 @@
}
synchronized (cache) {
cache.clear();
+ cache.mCacheFullyMatchesDisk = true;
}
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 629d993..6f3c66d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -473,9 +473,9 @@
return;
}
- // get the dirty region
sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
if (newFrontBuffer != NULL) {
+ // get the dirty region
// compute the posted region
const Region dirty(lcblk->getDirtyRegion(buf));
mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );
@@ -511,6 +511,13 @@
// we now have the correct size, unfreeze the screen
mFreezeLock.clear();
}
+
+ // get the crop region
+ setBufferCrop( lcblk->getCrop(buf) );
+
+ // get the transformation
+ setBufferTransform( lcblk->getTransform(buf) );
+
} else {
// this should not happen unless we ran out of memory while
// allocating the buffer. we're hoping that things will get back
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 91ac915..6fc5010 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -54,6 +54,8 @@
{
const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
mFlags = hw.getFlags();
+ mBufferCrop.makeInvalid();
+ mBufferTransform = 0;
}
LayerBase::~LayerBase()
@@ -345,6 +347,14 @@
clearWithOpenGL(clip,0,0,0,0);
}
+template <typename T>
+static inline
+void swap(T& a, T& b) {
+ T t(a);
+ a = b;
+ b = t;
+}
+
void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
{
const DisplayHardware& hw(graphicPlane(0).displayHardware());
@@ -378,37 +388,72 @@
}
}
- Region::const_iterator it = clip.begin();
- Region::const_iterator const end = clip.end();
- const GLfloat texCoords[4][2] = {
- { 0, 0 },
- { 0, 1 },
- { 1, 1 },
- { 1, 0 }
+ /*
+ * compute texture coordinates
+ * here, we handle NPOT, cropping and buffer transformations
+ */
+
+ GLfloat cl, ct, cr, cb;
+ if (!mBufferCrop.isEmpty()) {
+ // source is cropped
+ const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width;
+ const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height;
+ cl = mBufferCrop.left * us;
+ ct = mBufferCrop.top * vs;
+ cr = mBufferCrop.right * us;
+ cb = mBufferCrop.bottom * vs;
+ } else {
+ cl = 0;
+ ct = 0;
+ cr = (texture.NPOTAdjust ? texture.wScale : 1.0f);
+ cb = (texture.NPOTAdjust ? texture.hScale : 1.0f);
+ }
+
+ struct TexCoords {
+ GLfloat u;
+ GLfloat v;
};
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
+ enum {
+ // name of the corners in the texture map
+ LB = 0, // left-bottom
+ LT = 1, // left-top
+ RT = 2, // right-top
+ RB = 3 // right-bottom
+ };
+
+ // vertices in screen space
+ int vLT = LB;
+ int vLB = LT;
+ int vRB = RT;
+ int vRT = RB;
// the texture's source is rotated
- switch (texture.transform) {
- case HAL_TRANSFORM_ROT_90:
- glTranslatef(0, 1, 0);
- glRotatef(-90, 0, 0, 1);
- break;
- case HAL_TRANSFORM_ROT_180:
- glTranslatef(1, 1, 0);
- glRotatef(-180, 0, 0, 1);
- break;
- case HAL_TRANSFORM_ROT_270:
- glTranslatef(1, 0, 0);
- glRotatef(-270, 0, 0, 1);
- break;
+ uint32_t transform = mBufferTransform;
+ if (transform & HAL_TRANSFORM_ROT_90) {
+ vLT = RB;
+ vLB = LB;
+ vRB = LT;
+ vRT = RT;
+ }
+ if (transform & HAL_TRANSFORM_FLIP_V) {
+ swap(vLT, vLB);
+ swap(vRB, vRT);
+ }
+ if (transform & HAL_TRANSFORM_FLIP_H) {
+ swap(vLT, vRB);
+ swap(vLB, vRT);
}
- if (texture.NPOTAdjust) {
- glScalef(texture.wScale, texture.hScale, 1.0f);
- }
+ TexCoords texCoords[4];
+ texCoords[vLT].u = cl;
+ texCoords[vLT].v = ct;
+ texCoords[vLB].u = cl;
+ texCoords[vLB].v = cb;
+ texCoords[vRB].u = cr;
+ texCoords[vRB].v = cb;
+ texCoords[vRT].u = cr;
+ texCoords[vRT].v = ct;
if (needsDithering()) {
glEnable(GL_DITHER);
@@ -420,6 +465,8 @@
glVertexPointer(2, GL_FLOAT, 0, mVertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
while (it != end) {
const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
@@ -429,6 +476,16 @@
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
+void LayerBase::setBufferCrop(const Rect& crop) {
+ if (!crop.isEmpty()) {
+ mBufferCrop = crop;
+ }
+}
+
+void LayerBase::setBufferTransform(uint32_t transform) {
+ mBufferTransform = transform;
+}
+
void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
{
const Layer::State& s(drawingState());
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 22bf857..8cba287 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -226,9 +226,17 @@
void clearWithOpenGL(const Region& clip) const;
void drawWithOpenGL(const Region& clip, const Texture& texture) const;
+ // these must be called from the post/drawing thread
+ void setBufferCrop(const Rect& crop);
+ void setBufferTransform(uint32_t transform);
+
sp<SurfaceFlinger> mFlinger;
uint32_t mFlags;
+ // post/drawing thread
+ Rect mBufferCrop;
+ uint32_t mBufferTransform;
+
// cached during validateVisibility()
bool mNeedsFiltering;
int32_t mOrientation;
diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
index 64a43c7..2ee21b9 100644
--- a/services/surfaceflinger/LayerBlur.cpp
+++ b/services/surfaceflinger/LayerBlur.cpp
@@ -241,6 +241,8 @@
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
}
}
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 5f836366..0240748 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -485,7 +485,7 @@
mTextureManager.loadTexture(&mTexture, dirty, t);
}
- mTexture.transform = mBufferHeap.transform;
+ mLayer.setBufferTransform(mBufferHeap.transform);
mLayer.drawWithOpenGL(clip, mTexture);
}
diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h
index c7c14e7..18c43486 100644
--- a/services/surfaceflinger/TextureManager.h
+++ b/services/surfaceflinger/TextureManager.h
@@ -40,12 +40,11 @@
struct Image {
enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 };
Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0),
- transform(0), dirty(1), target(TEXTURE_2D) { }
+ dirty(1), target(TEXTURE_2D) { }
GLuint name;
EGLImageKHR image;
GLuint width;
GLuint height;
- uint32_t transform;
unsigned dirty : 1;
unsigned target : 1;
};