diff options
164 files changed, 4737 insertions, 1270 deletions
diff --git a/api/current.txt b/api/current.txt index f6bfd73d2935..f21a4f3b721d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -975,6 +975,7 @@ package android { field public static final int textColorTertiary = 16843282; // 0x1010212 field public static final int textColorTertiaryInverse = 16843283; // 0x1010213 field public static final int textCursorDrawable = 16843618; // 0x1010362 + field public static final int textDirection = 16843677; // 0x101039d field public static final int textEditNoPasteWindowLayout = 16843541; // 0x1010315 field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314 field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f @@ -5896,6 +5897,7 @@ package android.content.pm { field public long codeSize; field public long dataSize; field public long externalCacheSize; + field public long externalCodeSize; field public long externalDataSize; field public long externalMediaSize; field public long externalObbSize; @@ -9397,6 +9399,7 @@ package android.inputmethodservice { method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo); method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo); method public void onUpdateSelection(int, int, int, int, int, int); + method public void onViewClicked(boolean); method public void onWindowHidden(); method public void onWindowShown(); method public void requestHideSelf(int); @@ -9440,6 +9443,7 @@ package android.inputmethodservice { method public void updateCursor(android.graphics.Rect); method public void updateExtractedText(int, android.view.inputmethod.ExtractedText); method public void updateSelection(int, int, int, int, int, int); + method public void viewClicked(boolean); } public static final class InputMethodService.Insets { @@ -11240,7 +11244,8 @@ package android.net { method public static long getUidUdpRxPackets(int); method public static long getUidUdpTxBytes(int); method public static long getUidUdpTxPackets(int); - method public static void setThreadStatsTag(java.lang.String); + method public static void setThreadStatsTag(int); + method public static deprecated void setThreadStatsTag(java.lang.String); method public static void tagSocket(java.net.Socket) throws java.net.SocketException; method public static void untagSocket(java.net.Socket) throws java.net.SocketException; field public static final int UNSUPPORTED = -1; // 0xffffffff @@ -11446,11 +11451,14 @@ package android.net.http { } public class SslError { - ctor public SslError(int, android.net.http.SslCertificate); - ctor public SslError(int, java.security.cert.X509Certificate); + ctor public deprecated SslError(int, android.net.http.SslCertificate); + ctor public deprecated SslError(int, java.security.cert.X509Certificate); + ctor public SslError(int, android.net.http.SslCertificate, java.lang.String); + ctor public SslError(int, java.security.cert.X509Certificate, java.lang.String); method public boolean addError(int); method public android.net.http.SslCertificate getCertificate(); method public int getPrimaryError(); + method public java.lang.String getUrl(); method public boolean hasError(int); field public static final int SSL_EXPIRED = 1; // 0x1 field public static final int SSL_IDMISMATCH = 2; // 0x2 @@ -21426,7 +21434,6 @@ package android.view { field public static final int AXIS_Y = 1; // 0x1 field public static final int AXIS_Z = 11; // 0xb field public static final int BUTTON_BACK = 8; // 0x8 - field public static final int BUTTON_ERASER = 32; // 0x20 field public static final int BUTTON_FORWARD = 16; // 0x10 field public static final int BUTTON_PRIMARY = 1; // 0x1 field public static final int BUTTON_SECONDARY = 2; // 0x2 @@ -21875,6 +21882,7 @@ package android.view { method public boolean isHovered(); method public boolean isInEditMode(); method public boolean isInTouchMode(); + method protected static boolean isLayoutDirectionRtl(java.util.Locale); method public boolean isLayoutRequested(); method public boolean isLongClickable(); method public boolean isOpaque(); @@ -21960,8 +21968,10 @@ package android.view { method public void requestLayout(); method public boolean requestRectangleOnScreen(android.graphics.Rect); method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean); + method protected void resetResolvedTextDirection(); method public static int resolveSize(int, int); method public static int resolveSizeAndState(int, int, int); + method protected void resolveTextDirection(); method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>); method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>); method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long); @@ -22011,6 +22021,7 @@ package android.view { method public void setOnDragListener(android.view.View.OnDragListener); method public void setOnFocusChangeListener(android.view.View.OnFocusChangeListener); method public void setOnGenericMotionListener(android.view.View.OnGenericMotionListener); + method public void setOnHoverListener(android.view.View.OnHoverListener); method public void setOnKeyListener(android.view.View.OnKeyListener); method public void setOnLongClickListener(android.view.View.OnLongClickListener); method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener); @@ -22060,6 +22071,7 @@ package android.view { method public boolean willNotCacheDrawing(); method public boolean willNotDraw(); field public static android.util.Property ALPHA; + field protected static int DEFAULT_TEXT_DIRECTION; field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0 field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000 field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000 @@ -23507,6 +23519,7 @@ package android.view.inputmethod { method public void updateCursor(android.view.View, int, int, int, int); method public void updateExtractedText(android.view.View, int, android.view.inputmethod.ExtractedText); method public void updateSelection(android.view.View, int, int, int, int); + method public void viewClicked(android.view.View); field public static final int HIDE_IMPLICIT_ONLY = 1; // 0x1 field public static final int HIDE_NOT_ALWAYS = 2; // 0x2 field public static final int RESULT_HIDDEN = 3; // 0x3 @@ -23527,6 +23540,7 @@ package android.view.inputmethod { method public abstract void updateCursor(android.graphics.Rect); method public abstract void updateExtractedText(int, android.view.inputmethod.ExtractedText); method public abstract void updateSelection(int, int, int, int, int, int); + method public abstract void viewClicked(boolean); } public static abstract interface InputMethodSession.EventCallback { diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 0acba8bc516b..ccd668d08192 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -213,9 +213,10 @@ status_t BootAnimation::readyToRun() { // create the native surface sp<SurfaceControl> control = session()->createSurface( 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); - session()->openTransaction(); + + SurfaceComposerClient::openGlobalTransaction(); control->setLayer(0x40000000); - session()->closeTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); sp<Surface> s = control->getSurface(); diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index d45ac1925a24..26b911356699 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -288,8 +288,9 @@ int protect(char *pkgname, gid_t gid) } int get_size(const char *pkgname, const char *apkpath, - const char *fwdlock_apkpath, - int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize) + const char *fwdlock_apkpath, const char *asecpath, + int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, + int64_t* _asecsize) { DIR *d; int dfd; @@ -300,6 +301,7 @@ int get_size(const char *pkgname, const char *apkpath, int64_t codesize = 0; int64_t datasize = 0; int64_t cachesize = 0; + int64_t asecsize = 0; /* count the source apk as code -- but only if it's not * on the /system partition and its not on the sdcard. @@ -324,6 +326,14 @@ int get_size(const char *pkgname, const char *apkpath, } } + /* compute asec size if it is given + */ + if (asecpath != NULL && asecpath[0] != '!') { + if (stat(asecpath, &s) == 0) { + asecsize += stat_size(&s); + } + } + if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, 0)) { goto done; } @@ -370,6 +380,7 @@ done: *_codesize = codesize; *_datasize = datasize; *_cachesize = cachesize; + *_asecsize = asecsize; return 0; } diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c index c062d36ec93b..feb6b927dd9b 100644 --- a/cmds/installd/installd.c +++ b/cmds/installd/installd.c @@ -77,16 +77,18 @@ static int do_get_size(char **arg, char reply[REPLY_MAX]) int64_t codesize = 0; int64_t datasize = 0; int64_t cachesize = 0; + int64_t asecsize = 0; int res = 0; /* pkgdir, apkpath */ - res = get_size(arg[0], arg[1], arg[2], &codesize, &datasize, &cachesize); + res = get_size(arg[0], arg[1], arg[2], arg[3], &codesize, &datasize, &cachesize, &asecsize); /* * Each int64_t can take up 22 characters printed out. Make sure it * doesn't go over REPLY_MAX in the future. */ - snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64, codesize, datasize, cachesize); + snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64, + codesize, datasize, cachesize, asecsize); return res; } @@ -137,7 +139,7 @@ struct cmdinfo cmds[] = { { "freecache", 1, do_free_cache }, { "rmcache", 1, do_rm_cache }, { "protect", 2, do_protect }, - { "getsize", 3, do_get_size }, + { "getsize", 4, do_get_size }, { "rmuserdata", 2, do_rm_user_data }, { "movefiles", 0, do_movefiles }, { "linklib", 2, do_linklib }, diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index e5f6739d9081..c5872b8efa09 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -143,7 +143,8 @@ int move_dex(const char *src, const char *dst); int rm_dex(const char *path); int protect(char *pkgname, gid_t gid); int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath, - int64_t *codesize, int64_t *datasize, int64_t *cachesize); + const char *asecpath, int64_t *codesize, int64_t *datasize, int64_t *cachesize, + int64_t *asecsize); int free_cache(int64_t free_size); int dexopt(const char *apk_path, uid_t uid, int is_public); int movefiles(); diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp index 289665f51b5d..6fa66cf3524e 100644 --- a/cmds/stagefright/sf2.cpp +++ b/cmds/stagefright/sf2.cpp @@ -29,6 +29,7 @@ #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/NativeWindowWrapper.h> #include <media/stagefright/Utils.h> #include <surfaceflinger/ISurfaceComposer.h> @@ -39,10 +40,12 @@ using namespace android; struct Controller : public AHandler { - Controller(const char *uri, bool decodeAudio, const sp<Surface> &surface) + Controller(const char *uri, bool decodeAudio, + const sp<Surface> &surface, bool renderToSurface) : mURI(uri), mDecodeAudio(decodeAudio), mSurface(surface), + mRenderToSurface(renderToSurface), mCodec(new ACodec) { CHECK(!mDecodeAudio || mSurface == NULL); } @@ -97,7 +100,8 @@ protected: sp<AMessage> format = makeFormat(mSource->getFormat()); if (mSurface != NULL) { - format->setObject("surface", mSurface); + format->setObject( + "native-window", new NativeWindowWrapper(mSurface)); } mCodec->initiateSetup(format); @@ -220,6 +224,7 @@ private: AString mURI; bool mDecodeAudio; sp<Surface> mSurface; + bool mRenderToSurface; sp<ACodec> mCodec; sp<MediaSource> mSource; @@ -451,7 +456,7 @@ private: inBuffer->release(); inBuffer = NULL; - // break; // Don't coalesce + break; // Don't coalesce } LOGV("coalesced %d input buffers", n); @@ -479,6 +484,10 @@ private: sp<AMessage> reply; CHECK(msg->findMessage("reply", &reply)); + if (mRenderToSurface) { + reply->setInt32("render", 1); + } + reply->post(); } @@ -491,7 +500,8 @@ static void usage(const char *me) { fprintf(stderr, " -a(udio)\n"); fprintf(stderr, - " -s(surface) Allocate output buffers on a surface.\n"); + " -S(urface) Allocate output buffers on a surface.\n" + " -R(ender) Render surface-allocated buffers.\n"); } int main(int argc, char **argv) { @@ -499,18 +509,23 @@ int main(int argc, char **argv) { bool decodeAudio = false; bool useSurface = false; + bool renderToSurface = false; int res; - while ((res = getopt(argc, argv, "has")) >= 0) { + while ((res = getopt(argc, argv, "haSR")) >= 0) { switch (res) { case 'a': decodeAudio = true; break; - case 's': + case 'S': useSurface = true; break; + case 'R': + renderToSurface = true; + break; + case '?': case 'h': default: @@ -553,16 +568,18 @@ int main(int argc, char **argv) { CHECK(control != NULL); CHECK(control->isValid()); - CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + SurfaceComposerClient::openGlobalTransaction(); CHECK_EQ(control->setLayer(30000), (status_t)OK); CHECK_EQ(control->show(), (status_t)OK); - CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); surface = control->getSurface(); CHECK(surface != NULL); } - sp<Controller> controller = new Controller(argv[0], decodeAudio, surface); + sp<Controller> controller = + new Controller(argv[0], decodeAudio, surface, renderToSurface); + looper->registerHandler(controller); controller->startAsync(); diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 656f5fdb4805..1a5b7f3ab8b1 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -865,10 +865,10 @@ int main(int argc, char **argv) { CHECK(control != NULL); CHECK(control->isValid()); - CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + SurfaceComposerClient::openGlobalTransaction(); CHECK_EQ(control->setLayer(30000), (status_t)OK); CHECK_EQ(control->show(), (status_t)OK); - CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); gSurface = control->getSurface(); CHECK(gSurface != NULL); diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp index f780afb80a0a..ee91c291c08d 100644 --- a/cmds/stagefright/stream.cpp +++ b/cmds/stagefright/stream.cpp @@ -20,6 +20,11 @@ #include <media/mediaplayer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MPEG2TSWriter.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> #include <binder/IServiceManager.h> #include <media/IMediaPlayerService.h> @@ -31,7 +36,7 @@ using namespace android; struct MyStreamSource : public BnStreamSource { - // Caller retains ownership of fd. + // Object assumes ownership of fd. MyStreamSource(int fd); virtual void setListener(const sp<IStreamListener> &listener); @@ -64,6 +69,8 @@ MyStreamSource::MyStreamSource(int fd) } MyStreamSource::~MyStreamSource() { + close(mFd); + mFd = -1; } void MyStreamSource::setListener(const sp<IStreamListener> &listener) { @@ -99,6 +106,143 @@ void MyStreamSource::onBufferAvailable(size_t index) { mListener->queueBuffer(index, n); } } +//////////////////////////////////////////////////////////////////////////////// + +struct MyConvertingStreamSource : public BnStreamSource { + MyConvertingStreamSource(const char *filename); + + virtual void setListener(const sp<IStreamListener> &listener); + virtual void setBuffers(const Vector<sp<IMemory> > &buffers); + + virtual void onBufferAvailable(size_t index); + +protected: + virtual ~MyConvertingStreamSource(); + +private: + Mutex mLock; + Condition mCondition; + + sp<IStreamListener> mListener; + Vector<sp<IMemory> > mBuffers; + + sp<MPEG2TSWriter> mWriter; + + ssize_t mCurrentBufferIndex; + size_t mCurrentBufferOffset; + + List<size_t> mBufferQueue; + + static ssize_t WriteDataWrapper(void *me, const void *data, size_t size); + ssize_t writeData(const void *data, size_t size); + + DISALLOW_EVIL_CONSTRUCTORS(MyConvertingStreamSource); +}; + +//////////////////////////////////////////////////////////////////////////////// + +MyConvertingStreamSource::MyConvertingStreamSource(const char *filename) + : mCurrentBufferIndex(-1), + mCurrentBufferOffset(0) { + sp<DataSource> dataSource = DataSource::CreateFromURI(filename); + CHECK(dataSource != NULL); + + sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); + CHECK(extractor != NULL); + + mWriter = new MPEG2TSWriter( + this, &MyConvertingStreamSource::WriteDataWrapper); + + for (size_t i = 0; i < extractor->countTracks(); ++i) { + const sp<MetaData> &meta = extractor->getTrackMetaData(i); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (strncasecmp("video/", mime, 6) && strncasecmp("audio/", mime, 6)) { + continue; + } + + CHECK_EQ(mWriter->addSource(extractor->getTrack(i)), (status_t)OK); + } + + CHECK_EQ(mWriter->start(), (status_t)OK); +} + +MyConvertingStreamSource::~MyConvertingStreamSource() { +} + +void MyConvertingStreamSource::setListener( + const sp<IStreamListener> &listener) { + mListener = listener; +} + +void MyConvertingStreamSource::setBuffers( + const Vector<sp<IMemory> > &buffers) { + mBuffers = buffers; +} + +ssize_t MyConvertingStreamSource::WriteDataWrapper( + void *me, const void *data, size_t size) { + return static_cast<MyConvertingStreamSource *>(me)->writeData(data, size); +} + +ssize_t MyConvertingStreamSource::writeData(const void *data, size_t size) { + size_t totalWritten = 0; + + while (size > 0) { + Mutex::Autolock autoLock(mLock); + + if (mCurrentBufferIndex < 0) { + while (mBufferQueue.empty()) { + mCondition.wait(mLock); + } + + mCurrentBufferIndex = *mBufferQueue.begin(); + mCurrentBufferOffset = 0; + + mBufferQueue.erase(mBufferQueue.begin()); + } + + sp<IMemory> mem = mBuffers.itemAt(mCurrentBufferIndex); + + size_t copy = size; + if (copy + mCurrentBufferOffset > mem->size()) { + copy = mem->size() - mCurrentBufferOffset; + } + + memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy); + mCurrentBufferOffset += copy; + + if (mCurrentBufferOffset == mem->size()) { + mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); + mCurrentBufferIndex = -1; + } + + data = (const uint8_t *)data + copy; + size -= copy; + + totalWritten += copy; + } + + return (ssize_t)totalWritten; +} + +void MyConvertingStreamSource::onBufferAvailable(size_t index) { + Mutex::Autolock autoLock(mLock); + + mBufferQueue.push_back(index); + mCondition.signal(); + + if (mWriter->reachedEOS()) { + if (mCurrentBufferIndex >= 0) { + mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); + mCurrentBufferIndex = -1; + } + + mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); + } +} //////////////////////////////////////////////////////////////////////////////// @@ -139,6 +283,8 @@ private: int main(int argc, char **argv) { android::ProcessState::self()->startThreadPool(); + DataSource::RegisterDefaultSniffers(); + if (argc != 2) { fprintf(stderr, "Usage: %s filename\n", argv[0]); return 1; @@ -159,10 +305,10 @@ int main(int argc, char **argv) { CHECK(control != NULL); CHECK(control->isValid()); - CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + SurfaceComposerClient::openGlobalTransaction(); CHECK_EQ(control->setLayer(30000), (status_t)OK); CHECK_EQ(control->show(), (status_t)OK); - CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); sp<Surface> surface = control->getSurface(); CHECK(surface != NULL); @@ -173,17 +319,28 @@ int main(int argc, char **argv) { CHECK(service.get() != NULL); - int fd = open(argv[1], O_RDONLY); + sp<MyClient> client = new MyClient; + + sp<IStreamSource> source; - if (fd < 0) { - fprintf(stderr, "Failed to open file '%s'.", argv[1]); - return 1; - } + size_t len = strlen(argv[1]); + if (len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) { + int fd = open(argv[1], O_RDONLY); - sp<MyClient> client = new MyClient; + if (fd < 0) { + fprintf(stderr, "Failed to open file '%s'.", argv[1]); + return 1; + } + + source = new MyStreamSource(fd); + } else { + printf("Converting file to transport stream for streaming...\n"); + + source = new MyConvertingStreamSource(argv[1]); + } sp<IMediaPlayer> player = - service->create(getpid(), client, new MyStreamSource(fd), 0); + service->create(getpid(), client, source, 0); if (player != NULL) { player->setVideoSurface(surface); @@ -196,9 +353,6 @@ int main(int argc, char **argv) { fprintf(stderr, "failed to instantiate player.\n"); } - close(fd); - fd = -1; - composerClient->dispose(); return 0; diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java index 11068e5d164e..1205da7cdcc6 100755 --- a/core/java/android/content/pm/PackageStats.java +++ b/core/java/android/content/pm/PackageStats.java @@ -40,6 +40,12 @@ public class PackageStats implements Parcelable { public long cacheSize; /** + * Size of the secure container on external storage holding the + * application's code. + */ + public long externalCodeSize; + + /** * Size of the external data used by the application (e.g., * <sdcard>/Android/data/<app>) */ @@ -80,6 +86,8 @@ public class PackageStats implements Parcelable { sb.append(dataSize); sb.append(",cacheSize="); sb.append(cacheSize); + sb.append(",externalCodeSize="); + sb.append(externalCodeSize); sb.append(",externalDataSize="); sb.append(externalDataSize); sb.append(",externalCacheSize="); @@ -100,6 +108,7 @@ public class PackageStats implements Parcelable { codeSize = source.readLong(); dataSize = source.readLong(); cacheSize = source.readLong(); + externalCodeSize = source.readLong(); externalDataSize = source.readLong(); externalCacheSize = source.readLong(); externalMediaSize = source.readLong(); @@ -111,6 +120,7 @@ public class PackageStats implements Parcelable { codeSize = pStats.codeSize; dataSize = pStats.dataSize; cacheSize = pStats.cacheSize; + externalCodeSize = pStats.externalCodeSize; externalDataSize = pStats.externalDataSize; externalCacheSize = pStats.externalCacheSize; externalMediaSize = pStats.externalMediaSize; @@ -126,6 +136,7 @@ public class PackageStats implements Parcelable { dest.writeLong(codeSize); dest.writeLong(dataSize); dest.writeLong(cacheSize); + dest.writeLong(externalCodeSize); dest.writeLong(externalDataSize); dest.writeLong(externalCacheSize); dest.writeLong(externalMediaSize); diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index df8cf9a8164f..e10f2184508b 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -47,6 +47,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_APP_PRIVATE_COMMAND = 100; private static final int DO_TOGGLE_SOFT_INPUT = 105; private static final int DO_FINISH_SESSION = 110; + private static final int DO_VIEW_CLICKED = 115; HandlerCaller mCaller; InputMethodSession mInputMethodSession; @@ -133,6 +134,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession = null; return; } + case DO_VIEW_CLICKED: { + mInputMethodSession.viewClicked(msg.arg1 == 1); + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -167,7 +172,11 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd)); } - + + public void viewClicked(boolean focusChanged) { + mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0)); + } + public void updateCursor(Rect newCursor) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor)); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index feb246ed7125..9481a880fb08 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -488,7 +488,15 @@ public class InputMethodService extends AbstractInputMethodService { InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd); } - + + @Override + public void viewClicked(boolean focusChanged) { + if (!isEnabled()) { + return; + } + InputMethodService.this.onViewClicked(focusChanged); + } + /** * Call {@link InputMethodService#onUpdateCursor * InputMethodService.onUpdateCursor()}. @@ -1609,6 +1617,16 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Called when the user tapped or clicked a text view. + * IMEs can't rely on this method being called because this was not part of the original IME + * protocol, so applications with custom text editing written before this method appeared will + * not call to inform the IME of this interaction. + * @param focusChanged true if the user changed the focused view by this click. + */ + public void onViewClicked(boolean focusChanged) { + } + + /** * Called when the application has reported a new location of its text * cursor. This is only called if explicitly requested by the input method. * The default implementation does nothing. diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 91af16d0010d..593b2b74e301 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -16,14 +16,21 @@ package android.net; +import static android.content.pm.PackageManager.GET_SIGNATURES; import static android.text.format.Time.MONTH_DAY; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; import android.os.RemoteException; import android.text.format.Time; +import com.google.android.collect.Sets; + import java.io.PrintWriter; +import java.util.HashSet; /** * Manager for creating and modifying network policy rules. @@ -42,6 +49,8 @@ public class NetworkPolicyManager { /** Reject traffic on metered networks. */ public static final int RULE_REJECT_METERED = 0x1; + private static final boolean ALLOW_PLATFORM_APP_POLICY = true; + /** * {@link Intent} action launched when user selects {@link NetworkPolicy} * warning notification. @@ -210,8 +219,37 @@ public class NetworkPolicyManager { * usually to protect critical system services. */ public static boolean isUidValidForPolicy(Context context, int uid) { - return (uid >= android.os.Process.FIRST_APPLICATION_UID - && uid <= android.os.Process.LAST_APPLICATION_UID); + // first, quick-reject non-applications + if (uid < android.os.Process.FIRST_APPLICATION_UID + || uid > android.os.Process.LAST_APPLICATION_UID) { + return false; + } + + if (!ALLOW_PLATFORM_APP_POLICY) { + final PackageManager pm = context.getPackageManager(); + final HashSet<Signature> systemSignature; + try { + systemSignature = Sets.newHashSet( + pm.getPackageInfo("android", GET_SIGNATURES).signatures); + } catch (NameNotFoundException e) { + throw new RuntimeException("problem finding system signature", e); + } + + try { + // reject apps signed with platform cert + for (String packageName : pm.getPackagesForUid(uid)) { + final HashSet<Signature> packageSignature = Sets.newHashSet( + pm.getPackageInfo(packageName, GET_SIGNATURES).signatures); + if (packageSignature.containsAll(systemSignature)) { + return false; + } + } + } catch (NameNotFoundException e) { + } + } + + // nothing found above; we can apply policy to UID + return true; } /** {@hide} */ diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index cb47193a6eec..2b59dba15c0e 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -16,14 +16,18 @@ package android.net; +import android.app.DownloadManager; +import android.app.backup.BackupManager; import android.content.Context; +import android.media.MediaPlayer; import android.os.IBinder; import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.ServiceManager; -import dalvik.system.BlockGuard; +import com.android.server.NetworkManagementSocketTagger; +import dalvik.system.SocketTagger; import java.net.Socket; import java.net.SocketException; @@ -50,6 +54,27 @@ public class TrafficStats { public static final int UID_REMOVED = -4; /** + * Default tag value for {@link DownloadManager} traffic. + * + * @hide + */ + public static final int TAG_SYSTEM_DOWNLOAD = 0xFFFF0001; + + /** + * Default tag value for {@link MediaPlayer} traffic. + * + * @hide + */ + public static final int TAG_SYSTEM_MEDIA = 0xFFFF0002; + + /** + * Default tag value for {@link BackupManager} traffic. + * + * @hide + */ + public static final int TAG_SYSTEM_BACKUP = 0xFFFF0003; + + /** * Snapshot of {@link NetworkStats} when the currently active profiling * session started, or {@code null} if no session active. * @@ -67,12 +92,20 @@ public class TrafficStats { * Changes only take effect during subsequent calls to * {@link #tagSocket(Socket)}. */ + public static void setThreadStatsTag(int tag) { + NetworkManagementSocketTagger.setThreadSocketStatsTag(tag); + } + + /** + * @deprecated unsupported, will eventually be removed + */ + @Deprecated public static void setThreadStatsTag(String tag) { - BlockGuard.setThreadSocketStatsTag(tag); + setThreadStatsTag(tag.hashCode()); } public static void clearThreadStatsTag() { - BlockGuard.setThreadSocketStatsTag(null); + NetworkManagementSocketTagger.setThreadSocketStatsTag(-1); } /** @@ -89,12 +122,12 @@ public class TrafficStats { * {@hide} */ public static void setThreadStatsUid(int uid) { - BlockGuard.setThreadSocketStatsUid(uid); + NetworkManagementSocketTagger.setThreadSocketStatsUid(uid); } /** {@hide} */ public static void clearThreadStatsUid() { - BlockGuard.setThreadSocketStatsUid(-1); + NetworkManagementSocketTagger.setThreadSocketStatsUid(-1); } /** @@ -103,18 +136,18 @@ public class TrafficStats { * parameters. When finished, call {@link #untagSocket(Socket)} to remove * statistics parameters. * - * @see #setThreadStatsTag(String) + * @see #setThreadStatsTag(int) * @see #setThreadStatsUid(int) */ public static void tagSocket(Socket socket) throws SocketException { - BlockGuard.tagSocketFd(socket.getFileDescriptor$()); + SocketTagger.get().tag(socket); } /** * Remove any statistics parameters from the given {@link Socket}. */ public static void untagSocket(Socket socket) throws SocketException { - BlockGuard.untagSocketFd(socket.getFileDescriptor$()); + SocketTagger.get().untag(socket); } /** diff --git a/core/java/android/net/http/SslError.java b/core/java/android/net/http/SslError.java index e1b9debb8f2b..1e1cb49dabad 100644 --- a/core/java/android/net/http/SslError.java +++ b/core/java/android/net/http/SslError.java @@ -59,36 +59,97 @@ public class SslError { /** * The SSL certificate associated with the error set */ - SslCertificate mCertificate; + final SslCertificate mCertificate; + + /** + * The URL associated with the error set. + */ + final String mUrl; /** * Creates a new SSL error set object * @param error The SSL error * @param certificate The associated SSL certificate + * @deprecated Use {@link #SslError(int, SslCertificate, String)} */ + @Deprecated public SslError(int error, SslCertificate certificate) { addError(error); + if (certificate == null) { + throw new NullPointerException("certificate is null."); + } mCertificate = certificate; + mUrl = ""; } /** * Creates a new SSL error set object * @param error The SSL error * @param certificate The associated SSL certificate + * @deprecated Use {@link #SslError(int, X509Certificate, String)} */ + @Deprecated public SslError(int error, X509Certificate certificate) { addError(error); + if (certificate == null) { + throw new NullPointerException("certificate is null."); + } mCertificate = new SslCertificate(certificate); + mUrl = ""; } /** - * @return The SSL certificate associated with the error set + * Creates a new SSL error set object + * @param error The SSL error + * @param certificate The associated SSL certificate + * @param url The associated URL. + */ + public SslError(int error, SslCertificate certificate, String url) { + addError(error); + if (certificate == null) { + throw new NullPointerException("certificate is null."); + } + mCertificate = certificate; + if (url == null) { + throw new NullPointerException("url is null."); + } + mUrl = url; + } + + /** + * Creates a new SSL error set object + * @param error The SSL error + * @param certificate The associated SSL certificate + * @param url The associated URL. + */ + public SslError(int error, X509Certificate certificate, String url) { + addError(error); + if (certificate == null) { + throw new NullPointerException("certificate is null."); + } + mCertificate = new SslCertificate(certificate); + if (url == null) { + throw new NullPointerException("url is null."); + } + mUrl = url; + } + + /** + * @return The SSL certificate associated with the error set, non-null. */ public SslCertificate getCertificate() { return mCertificate; } /** + * @return The URL associated with the error set, non-null. + * "" if one of the deprecated constructors is used. + */ + public String getUrl() { + return mUrl; + } + + /** * Adds the SSL error to the error set * @param error The SSL error to add * @return True iff the error being added is a known SSL error @@ -137,6 +198,7 @@ public class SslError { */ public String toString() { return "primary error: " + getPrimaryError() + - " certificate: " + getCertificate(); + " certificate: " + getCertificate() + + " on URL: " + getUrl(); } } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 1b09242d4c03..c9b6121adcf9 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -210,6 +210,21 @@ interface INetworkManagementService NetworkStats getNetworkStatsUidDetail(int uid); /** + * Set quota for an interface. + */ + void setInterfaceQuota(String iface, long quota); + + /** + * Remove quota for an interface. + */ + void removeInterfaceQuota(String iface); + + /** + * Control network activity of a UID over interfaces with a quota limit. + */ + void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces); + + /** * Configures bandwidth throttling on an interface. */ void setInterfaceThrottle(String iface, int rxKbps, int txKbps); diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index c2dc8aeefc43..93020601b29c 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -655,6 +655,26 @@ public interface IMountService extends IInterface { } return _result; } + + /* + * Returns the filesystem path of a mounted secure container. + */ + public String getSecureContainerFilesystemPath(String id) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + String _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(id); + mRemote.transact(Stub.TRANSACTION_getSecureContainerFilesystemPath, _data, _reply, 0); + _reply.readException(); + _result = _reply.readString(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } } private static final String DESCRIPTOR = "IMountService"; @@ -719,6 +739,8 @@ public interface IMountService extends IInterface { static final int TRANSACTION_getVolumeList = IBinder.FIRST_CALL_TRANSACTION + 29; + static final int TRANSACTION_getSecureContainerFilesystemPath = IBinder.FIRST_CALL_TRANSACTION + 30; + /** * Cast an IBinder object into an IMountService interface, generating a * proxy if needed. @@ -1031,6 +1053,15 @@ public interface IMountService extends IInterface { reply.writeParcelableArray(result, 0); return true; } + case TRANSACTION_getSecureContainerFilesystemPath: { + data.enforceInterface(DESCRIPTOR); + String id; + id = data.readString(); + String path = getSecureContainerFilesystemPath(id); + reply.writeNoException(); + reply.writeString(path); + return true; + } } return super.onTransact(code, data, reply, flags); } @@ -1210,4 +1241,6 @@ public interface IMountService extends IInterface { * Returns list of all mountable volumes. */ public Parcelable[] getVolumeList() throws RemoteException; + + public String getSecureContainerFilesystemPath(String id) throws RemoteException; } diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 02faf496843e..39c6f576b5d7 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -24,6 +24,8 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.DataUsageFeedback; import android.text.TextUtils; /** @@ -55,6 +57,29 @@ public class CallLog { Uri.parse("content://call_log/calls/filter"); /** + * An optional URI parameter which instructs the provider to allow the operation to be + * applied to voicemail records as well. + * <p> + * TYPE: Boolean + * <p> + * Using this parameter with a value of {@code true} will result in a security error if the + * calling package does not have appropriate permissions to access voicemails. + * + * @hide + */ + public static final String ALLOW_VOICEMAILS_PARAM_KEY = "allow_voicemails"; + + /** + * Content uri with {@link #ALLOW_VOICEMAILS_PARAM_KEY} set. This can directly be used to + * access call log entries that includes voicemail records. + * + * @hide + */ + public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon() + .appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true") + .build(); + + /** * The default sort order for this table */ public static final String DEFAULT_SORT_ORDER = "date DESC"; @@ -204,7 +229,44 @@ public class CallLog { } if ((ci != null) && (ci.person_id > 0)) { - ContactsContract.Contacts.markAsContacted(resolver, ci.person_id); + // Update usage information for the number associated with the contact ID. + // We need to use both the number and the ID for obtaining a data ID since other + // contacts may have the same number. + + final Cursor cursor; + + // We should prefer normalized one (probably coming from + // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others. + if (ci.normalizedNumber != null) { + final String normalizedPhoneNumber = ci.normalizedNumber; + cursor = resolver.query(Phone.CONTENT_URI, + new String[] { Phone._ID }, + Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?", + new String[] { String.valueOf(ci.person_id), normalizedPhoneNumber}, + null); + } else { + final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number; + cursor = resolver.query(Phone.CONTENT_URI, + new String[] { Phone._ID }, + Phone.CONTACT_ID + " =? AND " + Phone.NUMBER + " =?", + new String[] { String.valueOf(ci.person_id), phoneNumber}, + null); + } + + if (cursor != null) { + try { + if (cursor.getCount() > 0 && cursor.moveToFirst()) { + final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() + .appendPath(cursor.getString(0)) + .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, + DataUsageFeedback.USAGE_TYPE_CALL) + .build(); + resolver.update(feedbackUri, new ContentValues(), null, null); + } + } finally { + cursor.close(); + } + } } Uri result = resolver.insert(CONTENT_URI, values); diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index cb96bfd2c9d1..ad71061f52be 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -174,6 +174,15 @@ public final class ContactsContract { */ public static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account"; + /** + * A boolean parameter for {@link Contacts#CONTENT_STREQUENT_URI} and + * {@link Contacts#CONTENT_STREQUENT_FILTER_URI}, which requires the ContactsProvider to + * return only phone-related results. For example, frequently contacted person list should + * include persons contacted via phone (not email, sms, etc.) + * + * @hide + */ + public static final String STREQUENT_PHONE_ONLY = "strequent_phone_only"; /** * @hide diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index b5ae298f36e6..b5ae298f36e6 100755..100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java index 49f3bbe1bcd7..9b081b207dff 100644 --- a/core/java/android/view/InputEventConsistencyVerifier.java +++ b/core/java/android/view/InputEventConsistencyVerifier.java @@ -239,7 +239,7 @@ public final class InputEventConsistencyVerifier { break; } } finally { - finishEvent(false); + finishEvent(); } } @@ -302,7 +302,7 @@ public final class InputEventConsistencyVerifier { problem("Source was not SOURCE_CLASS_TRACKBALL."); } } finally { - finishEvent(false); + finishEvent(); } } @@ -328,7 +328,9 @@ public final class InputEventConsistencyVerifier { mTouchEventStreamUnhandled = false; mTouchEventStreamPointers = 0; } - final boolean wasTainted = mTouchEventStreamIsTainted; + if (mTouchEventStreamIsTainted) { + event.setTainted(true); + } try { ensureMetaStateIsNormalized(event.getMetaState()); @@ -441,7 +443,7 @@ public final class InputEventConsistencyVerifier { problem("Source was not SOURCE_CLASS_POINTER."); } } finally { - finishEvent(wasTainted); + finishEvent(); } } @@ -499,7 +501,7 @@ public final class InputEventConsistencyVerifier { } } } finally { - finishEvent(false); + finishEvent(); } } @@ -591,9 +593,9 @@ public final class InputEventConsistencyVerifier { return true; } - private void finishEvent(boolean tainted) { + private void finishEvent() { if (mViolationMessage != null && mViolationMessage.length() != 0) { - if (!tainted) { + if (!mCurrentEvent.isTainted()) { // Write a log message only if the event was not already tainted. mViolationMessage.append("\n in ").append(mCaller); mViolationMessage.append("\n "); @@ -614,17 +616,14 @@ public final class InputEventConsistencyVerifier { } Log.d(mLogTag, mViolationMessage.toString()); - tainted = true; + + // Taint the event so that we do not generate additional violations from it + // further downstream. + mCurrentEvent.setTainted(true); } mViolationMessage.setLength(0); } - if (tainted) { - // Taint the event so that we do not generate additional violations from it - // further downstream. - mCurrentEvent.setTainted(true); - } - if (RECENT_EVENTS_TO_LOG != 0) { if (mRecentEvents == null) { mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG]; diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 3436cd1df94f..88f59d4bdd21 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -1130,14 +1130,14 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int BUTTON_PRIMARY = 1 << 0; /** - * Button constant: Secondary button (right mouse button, stylus barrel). + * Button constant: Secondary button (right mouse button, stylus first button). * * @see #getButtonState */ public static final int BUTTON_SECONDARY = 1 << 1; /** - * Button constant: Tertiary button (middle mouse button). + * Button constant: Tertiary button (middle mouse button, stylus second button). * * @see #getButtonState */ @@ -1165,13 +1165,6 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ public static final int BUTTON_FORWARD = 1 << 4; - /** - * Button constant: Eraser button pressed (stylus end). - * - * @see #getButtonState - */ - public static final int BUTTON_ERASER = 1 << 5; - // NOTE: If you add a new axis here you must also add it to: // native/include/android/input.h @@ -1183,7 +1176,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { "BUTTON_TERTIARY", "BUTTON_BACK", "BUTTON_FORWARD", - "BUTTON_ERASER", + "0x00000020", "0x00000040", "0x00000080", "0x00000100", @@ -2176,7 +2169,6 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see #BUTTON_TERTIARY * @see #BUTTON_FORWARD * @see #BUTTON_BACK - * @see #BUTTON_ERASER */ public final int getButtonState() { return nativeGetButtonState(mNativePtr); @@ -2893,7 +2885,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { toolTypeToString(getToolType(i))); } - msg.append(", buttonState=").append(KeyEvent.metaStateToString(getButtonState())); + msg.append(", buttonState=").append(MotionEvent.buttonStateToString(getButtonState())); msg.append(", metaState=").append(KeyEvent.metaStateToString(getMetaState())); msg.append(", flags=0x").append(Integer.toHexString(getFlags())); msg.append(", edgeFlags=0x").append(Integer.toHexString(getEdgeFlags())); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 574313441d15..bf7f359303ef 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2493,6 +2493,84 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit private boolean mSendingHoverAccessibilityEvents; /** + * Undefined text direction (used by resolution algorithm). + * @hide + */ + public static final int TEXT_DIRECTION_UNDEFINED = -1; + + /** + * Text direction is inherited thru {@link ViewGroup} + * @hide + */ + public static final int TEXT_DIRECTION_INHERIT = 0; + + /** + * Text direction is using "first strong algorithm". The first strong directional character + * determines the paragraph direction. If there is no strong directional character, the + * paragraph direction is the view’s resolved ayout direction. + * + * @hide + */ + public static final int TEXT_DIRECTION_FIRST_STRONG = 1; + + /** + * Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains + * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters. + * If there are neither, the paragraph direction is the view’s resolved layout direction. + * + * @hide + */ + public static final int TEXT_DIRECTION_ANY_RTL = 2; + + /** + * Text direction is forced to LTR. + * + * @hide + */ + public static final int TEXT_DIRECTION_LTR = 3; + + /** + * Text direction is forced to RTL. + * + * @hide + */ + public static final int TEXT_DIRECTION_RTL = 4; + + /** + * Default text direction is inherited + */ + protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT; + + /** + * The text direction that has been defined by {@link #setTextDirection(int)}. + * + * {@hide} + */ + @ViewDebug.ExportedProperty(category = "text", mapping = { + @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL") + }) + protected int mTextDirection = DEFAULT_TEXT_DIRECTION; + + /** + * The resolved text direction. If resolution has not yet been done or has been reset, it will + * be equal to {@link #TEXT_DIRECTION_UNDEFINED}. Otherwise it will be either {@link #TEXT_DIRECTION_LTR} + * or {@link #TEXT_DIRECTION_RTL}. + * + * {@hide} + */ + @ViewDebug.ExportedProperty(category = "text", mapping = { + @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"), + @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL") + }) + protected int mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED; + + /** * Consistency verifier for debugging purposes. * @hide */ @@ -2865,6 +2943,9 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit case R.styleable.View_layerType: setLayerType(a.getInt(attr, LAYER_TYPE_NONE), null); break; + case R.styleable.View_textDirection: + mTextDirection = a.getInt(attr, DEFAULT_TEXT_DIRECTION); + break; } } @@ -3390,6 +3471,14 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** + * Register a callback to be invoked when a hover event is sent to this view. + * @param l the hover listener to attach to this view + */ + public void setOnHoverListener(OnHoverListener l) { + mOnHoverListener = l; + } + + /** * Register a drag event listener callback object for this View. The parameter is * an implementation of {@link android.view.View.OnDragListener}. To send a drag event to a * View, the system calls the @@ -8943,6 +9032,8 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit resetLayoutDirectionResolution(); resolveLayoutDirectionIfNeeded(); resolvePadding(); + resetResolvedTextDirection(); + resolveTextDirection(); if (isFocused()) { InputMethodManager imm = InputMethodManager.peekInstance(); imm.focusIn(this); @@ -9043,7 +9134,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * @param locale Locale to check * @return true if a Locale is corresponding to a RTL script. */ - private static boolean isLayoutDirectionRtl(Locale locale) { + protected static boolean isLayoutDirectionRtl(Locale locale) { return (LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE == LocaleUtil.getLayoutDirectionFromLocale(locale)); } @@ -12890,6 +12981,89 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit return getVerticalScrollFactor(); } + /** + * Return the value specifying the text direction or policy that was set with + * {@link #setTextDirection(int)}. + * + * @return the defined text direction. It can be one of: + * + * {@link #TEXT_DIRECTION_INHERIT}, + * {@link #TEXT_DIRECTION_FIRST_STRONG} + * {@link #TEXT_DIRECTION_ANY_RTL}, + * {@link #TEXT_DIRECTION_LTR}, + * {@link #TEXT_DIRECTION_RTL}, + * + * @hide + */ + public int getTextDirection() { + return mTextDirection; + } + + /** + * Set the text direction. + * + * @param textDirection the direction to set. Should be one of: + * + * {@link #TEXT_DIRECTION_INHERIT}, + * {@link #TEXT_DIRECTION_FIRST_STRONG} + * {@link #TEXT_DIRECTION_ANY_RTL}, + * {@link #TEXT_DIRECTION_LTR}, + * {@link #TEXT_DIRECTION_RTL}, + * + * @hide + */ + public void setTextDirection(int textDirection) { + if (textDirection != mTextDirection) { + mTextDirection = textDirection; + resetResolvedTextDirection(); + requestLayout(); + } + } + + /** + * Return the resolved text direction. + * + * @return the resolved text direction. Return one of: + * + * {@link #TEXT_DIRECTION_LTR}, + * {@link #TEXT_DIRECTION_RTL}, + * + * @hide + */ + public int getResolvedTextDirection() { + if (!isResolvedTextDirection()) { + resolveTextDirection(); + } + return mResolvedTextDirection; + } + + /** + * Resolve the text direction. Classes that extend View and want to do a specific text direction + * resolution will need to implement this method and set the mResolvedTextDirection to + * either TEXT_DIRECTION_LTR if direction is LTR or TEXT_DIRECTION_RTL if + * direction is RTL. + */ + protected void resolveTextDirection() { + } + + /** + * Return if the text direction has been resolved or not. + * + * @return true, if resolved and false if not resolved + * + * @hide + */ + public boolean isResolvedTextDirection() { + return (mResolvedTextDirection != TEXT_DIRECTION_UNDEFINED); + } + + /** + * Reset resolved text direction. Will be resolved during a call to getResolvedLayoutDirection(). + */ + protected void resetResolvedTextDirection() { + mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED; + } + // // Properties // diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 640539893961..2a90ddea3b55 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -46,6 +46,7 @@ import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.HashSet; +import java.util.Locale; /** * <p> @@ -5016,6 +5017,51 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * This method will be called during text direction resolution (text direction resolution + * inheritance) + */ + @Override + protected void resolveTextDirection() { + int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED; + switch(mTextDirection) { + default: + case TEXT_DIRECTION_INHERIT: + // Try to the text direction from the parent layout + if (mParent != null && mParent instanceof ViewGroup) { + resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection(); + } else { + // We reached the top of the View hierarchy, so get the direction from + // the Locale + resolvedTextDirection = isLayoutDirectionRtl(Locale.getDefault()) ? + TEXT_DIRECTION_RTL : TEXT_DIRECTION_LTR; + } + break; + // Pass down the hierarchy the following text direction values + case TEXT_DIRECTION_FIRST_STRONG: + case TEXT_DIRECTION_ANY_RTL: + case TEXT_DIRECTION_LTR: + case TEXT_DIRECTION_RTL: + resolvedTextDirection = mTextDirection; + break; + } + mResolvedTextDirection = resolvedTextDirection; + } + + @Override + protected void resetResolvedTextDirection() { + super.resetResolvedTextDirection(); + + // Take care of resetting the children resolution too + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getTextDirection() == TEXT_DIRECTION_INHERIT) { + child.resetResolvedTextDirection(); + } + } + } + + /** * Return true if the pressed state should be delayed for children or descendants of this * ViewGroup. Generally, this should be done for containers that can scroll, such as a List. * This prevents the pressed state from appearing when the user is actually trying to scroll diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 47f5e4c4f9cb..a1a7281c7710 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1126,7 +1126,7 @@ public final class InputMethodManager { if (mServedView == mNextServedView && !mNextServedNeedsStart) { return; } - + InputConnection ic = null; synchronized (mH) { if (mServedView == mNextServedView && !mNextServedNeedsStart) { @@ -1242,6 +1242,27 @@ public final class InputMethodManager { } /** + * Notify the event when the user tapped or clicked the text view. + */ + public void viewClicked(View view) { + final boolean focusChanged = mServedView != mNextServedView; + checkFocus(); + synchronized (mH) { + if ((mServedView != view && (mServedView == null + || !mServedView.checkInputConnectionProxy(view))) + || mCurrentTextBoxAttribute == null || mCurMethod == null) { + return; + } + try { + if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged); + mCurMethod.viewClicked(focusChanged); + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId, e); + } + } + } + + /** * Returns true if the current input method wants to watch the location * of the input editor's cursor in its window. */ diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java index bb03afad56ae..ea6f5ee4a976 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -63,6 +63,15 @@ public interface InputMethodSession { int candidatesStart, int candidatesEnd); /** + * This method is called when the user tapped a text view. + * IMEs can't rely on this method being called because this was not part of the original IME + * protocol, so applications with custom text editing written before this method appeared will + * not call to inform the IME of this interaction. + * @param focusChanged true if the user changed the focused view by this click. + */ + public void viewClicked(boolean focusChanged); + + /** * This method is called when cursor location of the target input field * has changed within its window. This is not normally called, but will * only be reported if requested by the input method. diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 2f4774f61e17..79a5affffa44 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -1150,11 +1150,12 @@ class BrowserFrame extends Handler { * {@link #nativeSslCertErrorProceed(int)} or * {@link #nativeSslCertErrorCancel(int, int)}. */ - private void reportSslCertError(final int handle, final int cert_error, byte cert_der[]) { + private void reportSslCertError( + final int handle, final int cert_error, byte cert_der[], String url) { final SslError ssl_error; try { X509Certificate cert = new X509CertImpl(cert_der); - ssl_error = new SslError(cert_error, cert); + ssl_error = new SslError(cert_error, cert, url); } catch (IOException e) { // Can't get the certificate, not much to do. Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); diff --git a/core/java/android/webkit/CertTool.java b/core/java/android/webkit/CertTool.java index 4c534f93cd26..a2325c328af5 100644 --- a/core/java/android/webkit/CertTool.java +++ b/core/java/android/webkit/CertTool.java @@ -21,31 +21,27 @@ import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier; import com.android.org.bouncycastle.jce.netscape.NetscapeCertRequest; import com.android.org.bouncycastle.util.encoders.Base64; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.Intent; import android.security.Credentials; +import android.security.KeyChain; import android.util.Log; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.util.HashMap; -class CertTool { +final class CertTool { private static final String LOGTAG = "CertTool"; private static final AlgorithmIdentifier MD5_WITH_RSA = new AlgorithmIdentifier(PKCSObjectIdentifiers.md5WithRSAEncryption); - static final String CERT = Credentials.CERTIFICATE; - static final String PKCS12 = Credentials.PKCS12; - private static HashMap<String, String> sCertificateTypeMap; static { sCertificateTypeMap = new HashMap<String, String>(); - sCertificateTypeMap.put("application/x-x509-ca-cert", CertTool.CERT); - sCertificateTypeMap.put("application/x-x509-user-cert", CertTool.CERT); - sCertificateTypeMap.put("application/x-pkcs12", CertTool.PKCS12); + sCertificateTypeMap.put("application/x-x509-ca-cert", KeyChain.EXTRA_CERTIFICATE); + sCertificateTypeMap.put("application/x-x509-user-cert", KeyChain.EXTRA_CERTIFICATE); + sCertificateTypeMap.put("application/x-pkcs12", KeyChain.EXTRA_PKCS12); } static String[] getKeyStrengthList() { @@ -77,7 +73,7 @@ class CertTool { static String getCertType(String mimeType) { return sCertificateTypeMap.get(mimeType); - } + } private CertTool() {} } diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java index 4cae9d80d614..11ab0d778043 100644 --- a/core/java/android/webkit/HTML5VideoFullScreen.java +++ b/core/java/android/webkit/HTML5VideoFullScreen.java @@ -104,9 +104,13 @@ public class HTML5VideoFullScreen extends HTML5VideoView public void surfaceDestroyed(SurfaceHolder holder) { - // after we return from this we can't use the surface any more - mSurfaceHolder = null; + // After we return from this we can't use the surface any more. // The current Video View will be destroy when we play a new video. + pauseAndDispatch(mProxy); + mSurfaceHolder = null; + if (mMediaController != null) { + mMediaController.hide(); + } } }; @@ -210,7 +214,6 @@ public class HTML5VideoFullScreen extends HTML5VideoView // which happens when the video view is detached from its parent // view. This happens in the WebChromeClient before this method // is invoked. - pauseAndDispatch(mProxy); mProxy.dispatchOnStopFullScreen(); mLayout.removeView(getSurfaceView()); diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 883656bd6fae..6c6974b468ec 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -1107,7 +1107,8 @@ class ZoomManager { } reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale); } - mInitialZoomOverview = !exceedsMinScaleIncrement(scale, overviewScale); + mInitialZoomOverview = settings.getLoadWithOverviewMode() && + !exceedsMinScaleIncrement(scale, overviewScale); setZoomScale(scale, reflowText); // update the zoom buttons as the scale can be changed diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 2947ebeb1d4b..aa23ad0f6fa4 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -1201,18 +1201,15 @@ public class NumberPicker extends LinearLayout { private void initializeScrollWheel() { if (mInitialScrollOffset != Integer.MIN_VALUE) { return; - } int[] selectorIndices = getSelectorIndices(); int totalTextHeight = selectorIndices.length * mTextSize; - int totalTextGapHeight = (mBottom - mTop) - totalTextHeight; - int textGapCount = selectorIndices.length - 1; - int selectorTextGapHeight = totalTextGapHeight / textGapCount; - // compensate for integer division loss of the components used to - // calculate the text gap - int integerDivisionLoss = (mTextSize + mBottom - mTop) % textGapCount; - mInitialScrollOffset = mCurrentScrollOffset = mTextSize - integerDivisionLoss / 2; + float totalTextGapHeight = (mBottom - mTop) - totalTextHeight; + float textGapCount = selectorIndices.length - 1; + int selectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f); mSelectorElementHeight = mTextSize + selectorTextGapHeight; + mInitialScrollOffset = mTextSize - 3 * (mSelectorElementHeight % 2); + mCurrentScrollOffset = mInitialScrollOffset; updateInputTextView(); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 85e7eecfdd82..2a70ac87f2b8 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4948,7 +4948,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mMovement != null && mText instanceof Editable && mLayout != null && onCheckIsTextEditor()) { InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) imm.showSoftInput(this, 0); + if (imm != null) { + imm.viewClicked(this); + imm.showSoftInput(this, 0); + } } } } @@ -7398,8 +7401,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if ((isTextEditable() || mTextIsSelectable) && touchIsFinished) { // Show the IME, except when selecting in read-only text. + final InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.viewClicked(this); + } if (!mTextIsSelectable) { - final InputMethodManager imm = InputMethodManager.peekInstance(); handled |= imm != null && imm.showSoftInput(this, 0); } @@ -10010,6 +10016,127 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mInBatchEditControllers; } + /** + * Resolve the text direction. + * + * Text direction of paragraphs in a TextView is determined using a heuristic. If the correct + * text direction cannot be determined by the heuristic, the view’s resolved layout direction + * determines the direction. + * + * This heuristic and result is applied individually to each paragraph in a TextView, based on + * the text and style content of that paragraph. Paragraph text styles can also be used to force + * a particular direction. + */ + @Override + protected void resolveTextDirection() { + int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED; + switch(mTextDirection) { + default: + case TEXT_DIRECTION_INHERIT: + // Try to the text direction from the parent layout. If not possible, then we will + // use the default layout direction to decide later + if (mParent != null && mParent instanceof ViewGroup) { + resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection(); + } + break; + case TEXT_DIRECTION_FIRST_STRONG: + resolvedTextDirection = getTextDirectionFromFirstStrong(mText); + break; + case TEXT_DIRECTION_ANY_RTL: + resolvedTextDirection = getTextDirectionFromAnyRtl(mText); + break; + case TEXT_DIRECTION_LTR: + resolvedTextDirection = TEXT_DIRECTION_LTR; + break; + case TEXT_DIRECTION_RTL: + resolvedTextDirection = TEXT_DIRECTION_RTL; + break; + } + // if we have been so far unable to get the text direction from the heuristics, then we are + // falling back using the layout direction + if (resolvedTextDirection == TEXT_DIRECTION_UNDEFINED) { + switch(getResolvedLayoutDirection()) { + default: + case LAYOUT_DIRECTION_LTR: + resolvedTextDirection = TEXT_DIRECTION_LTR; + break; + case LAYOUT_DIRECTION_RTL: + resolvedTextDirection = TEXT_DIRECTION_RTL; + break; + } + } + mResolvedTextDirection = resolvedTextDirection; + } + + /** + * Get text direction following the "first strong" heuristic. + * + * @param cs the CharSequence used to get the text direction. + * + * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if + * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found. + */ + private static int getTextDirectionFromFirstStrong(final CharSequence cs) { + final int length = cs.length(); + for(int i = 0; i < length; i++) { + final char c = cs.charAt(i); + final byte dir = Character.getDirectionality(c); + if (isStrongLtrChar(dir)) { + return TEXT_DIRECTION_LTR; + } else if (isStrongRtlChar(dir)) { + return TEXT_DIRECTION_RTL; + } + } + return TEXT_DIRECTION_UNDEFINED; + } + + /** + * Get text direction following the "any RTL" heuristic. + * + * @param cs the CharSequence used to get the text direction. + * + * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if + * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found. + */ + private static int getTextDirectionFromAnyRtl(final CharSequence cs) { + final int length = cs.length(); + boolean foundStrongLtr = false; + boolean foundStrongRtl = false; + for(int i = 0; i < length; i++) { + final char c = cs.charAt(i); + final byte dir = Character.getDirectionality(c); + if (isStrongLtrChar(dir)) { + foundStrongLtr = true; + } else if (isStrongRtlChar(dir)) { + foundStrongRtl = true; + } + } + if (foundStrongRtl) { + return TEXT_DIRECTION_RTL; + } + if (foundStrongLtr) { + return TEXT_DIRECTION_LTR; + } + return TEXT_DIRECTION_UNDEFINED; + } + + /** + * Return true if the char direction is corresponding to a "strong RTL char" following the + * Unicode Bidirectional Algorithm (UBA). + */ + private static boolean isStrongRtlChar(final byte dir) { + return (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT || + dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC); + } + + /** + * Return true if the char direction is corresponding to a "strong LTR char" following the + * Unicode Bidirectional Algorithm (UBA). + */ + private static boolean isStrongLtrChar(final byte dir) { + return (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT); + } + @ViewDebug.ExportedProperty(category = "text") private CharSequence mText; private CharSequence mTransformed; diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index b57046c39b2c..ec64552051d4 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -135,6 +135,16 @@ public class PackageHelper { return null; } + public static String getSdFilesystem(String cid) { + try { + return getMountService().getSecureContainerFilesystemPath(cid); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get container path for " + cid + + " with exception " + e); + } + return null; + } + public static boolean finalizeSdDir(String cid) { try { int rc = getMountService().finalizeSecureContainer(cid); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 12687a130e3f..7b65964a2fc0 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -41,6 +41,7 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; import android.util.SparseArray; +import android.util.TimeUtils; import java.io.BufferedReader; import java.io.File; @@ -4870,8 +4871,8 @@ public final class BatteryStatsImpl extends BatteryStats { return 0; } - void readHistory(Parcel in) { - mHistoryBaseTime = in.readLong(); + void readHistory(Parcel in, boolean andOldHistory) { + final long historyBaseTime = in.readLong(); mHistoryBuffer.setDataSize(0); mHistoryBuffer.setDataPosition(0); @@ -4889,15 +4890,35 @@ public final class BatteryStatsImpl extends BatteryStats { in.setDataPosition(curPos + bufSize); } - long oldnow = SystemClock.elapsedRealtime() - (5*60*1000); - if (oldnow > 0) { - // If the system process has restarted, but not the entire - // system, then the mHistoryBaseTime already accounts for - // much of the elapsed time. We thus want to adjust it back, - // to avoid large gaps in the data. We determine we are - // in this case by arbitrarily saying it is so if at this - // point in boot the elapsed time is already more than 5 minutes. - mHistoryBaseTime -= oldnow; + if (andOldHistory) { + readOldHistory(in); + } + + if (DEBUG_HISTORY) { + StringBuilder sb = new StringBuilder(128); + sb.append("****************** OLD mHistoryBaseTime: "); + TimeUtils.formatDuration(mHistoryBaseTime, sb); + Slog.i(TAG, sb.toString()); + } + mHistoryBaseTime = historyBaseTime; + if (DEBUG_HISTORY) { + StringBuilder sb = new StringBuilder(128); + sb.append("****************** NEW mHistoryBaseTime: "); + TimeUtils.formatDuration(mHistoryBaseTime, sb); + Slog.i(TAG, sb.toString()); + } + + // We are just arbitrarily going to insert 1 minute from the sample of + // the last run until samples in this run. + if (mHistoryBaseTime > 0) { + long oldnow = SystemClock.elapsedRealtime(); + mHistoryBaseTime = (mHistoryBaseTime - oldnow) + 60*1000; + if (DEBUG_HISTORY) { + StringBuilder sb = new StringBuilder(128); + sb.append("****************** ADJUSTED mHistoryBaseTime: "); + TimeUtils.formatDuration(mHistoryBaseTime, sb); + Slog.i(TAG, sb.toString()); + } } } @@ -4910,12 +4931,24 @@ public final class BatteryStatsImpl extends BatteryStats { } } - void writeHistory(Parcel out) { - out.writeLong(mLastHistoryTime); + void writeHistory(Parcel out, boolean andOldHistory) { + if (DEBUG_HISTORY) { + StringBuilder sb = new StringBuilder(128); + sb.append("****************** WRITING mHistoryBaseTime: "); + TimeUtils.formatDuration(mHistoryBaseTime, sb); + sb.append(" mLastHistoryTime: "); + TimeUtils.formatDuration(mLastHistoryTime, sb); + Slog.i(TAG, sb.toString()); + } + out.writeLong(mHistoryBaseTime + mLastHistoryTime); out.writeInt(mHistoryBuffer.dataSize()); if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: " + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition()); out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize()); + + if (andOldHistory) { + writeOldHistory(out); + } } void writeOldHistory(Parcel out) { @@ -4935,8 +4968,7 @@ public final class BatteryStatsImpl extends BatteryStats { return; } - readHistory(in); - readOldHistory(in); + readHistory(in, true); mStartCount = in.readInt(); mBatteryUptime = in.readLong(); @@ -5136,8 +5168,7 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(VERSION); - writeHistory(out); - writeOldHistory(out); + writeHistory(out, true); out.writeInt(mStartCount); out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED)); @@ -5340,7 +5371,7 @@ public final class BatteryStatsImpl extends BatteryStats { throw new ParcelFormatException("Bad magic number"); } - readHistory(in); + readHistory(in, false); mStartCount = in.readInt(); mBatteryUptime = in.readLong(); @@ -5461,7 +5492,7 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(MAGIC); - writeHistory(out); + writeHistory(out, false); out.writeInt(mStartCount); out.writeLong(mBatteryUptime); diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 5e9cd23a82cd..f13e77067d5b 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -25,16 +25,13 @@ import android.os.Process; import android.os.SystemProperties; import android.util.Log; import android.util.Slog; - import com.android.internal.logging.AndroidConfig; - +import com.android.server.NetworkManagementSocketTagger; import dalvik.system.VMRuntime; - import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.logging.LogManager; import java.util.TimeZone; - +import java.util.logging.LogManager; import org.apache.harmony.luni.internal.util.TimezoneGetter; /** @@ -129,6 +126,11 @@ public class RuntimeInit { System.setProperty("http.agent", userAgent); /* + * Wire socket tagging to traffic stats. + */ + NetworkManagementSocketTagger.install(); + + /* * If we're running in an emulator launched with "-trace", put the * VM into emulator trace profiling mode so that the user can hit * F9/F10 at any time to capture traces. This has performance diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index 338dcaa7db96..f875cbd95a07 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -36,7 +36,9 @@ oneway interface IInputMethodSession { void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd); - + + void viewClicked(boolean focusChanged); + void updateCursor(in Rect newCursor); void displayCompletions(in CompletionInfo[] completions); diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java index 3e7b976aedb7..5b3510428f7b 100644 --- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java +++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java @@ -224,8 +224,8 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { /** * Animation used to attract user's attention to the target button. - * Assumes mChevronDrawables is an a list with an even number of chevrons filled with left - * followed by right chevrons. + * Assumes mChevronDrawables is an a list with an even number of chevrons filled with + * mFeedbackCount items in the order: left, right, top, bottom. */ private void startChevronAnimation() { final float r = mHandleDrawable.getWidth() / 2; @@ -442,6 +442,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { mHandleDrawable.setX(mWaveCenterX); mHandleDrawable.setY(mWaveCenterY); mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE); + Tweener.reset(); } @Override diff --git a/core/java/com/android/internal/widget/multiwaveview/Tweener.java b/core/java/com/android/internal/widget/multiwaveview/Tweener.java index 0cff00a514ab..bc8a62f2ca0b 100644 --- a/core/java/com/android/internal/widget/multiwaveview/Tweener.java +++ b/core/java/com/android/internal/widget/multiwaveview/Tweener.java @@ -18,25 +18,42 @@ package com.android.internal.widget.multiwaveview; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; +import java.util.Map.Entry; import android.animation.Animator.AnimatorListener; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.util.Log; class Tweener { private static final String TAG = "Tweener"; + private static final boolean DEBUG = false; - private Object object; ObjectAnimator animator; private static HashMap<Object, Tweener> sTweens = new HashMap<Object, Tweener>(); - public Tweener(Object obj, ObjectAnimator anim) { - object = obj; + public Tweener(ObjectAnimator anim) { animator = anim; } + private static void remove(Animator animator) { + Iterator<Entry<Object, Tweener>> iter = sTweens.entrySet().iterator(); + while (iter.hasNext()) { + Entry<Object, Tweener> entry = iter.next(); + if (entry.getValue().animator == animator) { + if (DEBUG) Log.v(TAG, "Removing tweener " + sTweens.get(entry.getKey()) + + " sTweens.size() = " + sTweens.size()); + iter.remove(); + break; // an animator can only be attached to one object + } + } + } + public static Tweener to(Object object, long duration, Object... vars) { long delay = 0; AnimatorUpdateListener updateListener = null; @@ -77,32 +94,35 @@ class Tweener { // Re-use existing tween, if present Tweener tween = sTweens.get(object); + ObjectAnimator anim = null; if (tween == null) { - ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(object, + anim = ObjectAnimator.ofPropertyValuesHolder(object, props.toArray(new PropertyValuesHolder[props.size()])); - tween = new Tweener(object, anim); + tween = new Tweener(anim); sTweens.put(object, tween); + if (DEBUG) Log.v(TAG, "Added new Tweener " + tween); } else { - tween.animator.cancel(); - replace(props, object); + anim = sTweens.get(object).animator; + replace(props, object); // Cancel all animators for given object } if (interpolator != null) { - tween.animator.setInterpolator(interpolator); + anim.setInterpolator(interpolator); } // Update animation with properties discovered in loop above - tween.animator.setStartDelay(delay); - tween.animator.setDuration(duration); + anim.setStartDelay(delay); + anim.setDuration(duration); if (updateListener != null) { - tween.animator.removeAllUpdateListeners(); // There should be only one - tween.animator.addUpdateListener(updateListener); + anim.removeAllUpdateListeners(); // There should be only one + anim.addUpdateListener(updateListener); } if (listener != null) { - tween.animator.removeAllListeners(); // There should be only one. - tween.animator.addListener(listener); + anim.removeAllListeners(); // There should be only one. + anim.addListener(listener); } - tween.animator.start(); + anim.addListener(mCleanupListener); + anim.start(); return tween; } @@ -114,18 +134,40 @@ class Tweener { return Tweener.to(object, duration, vars); } - static void replace(ArrayList<PropertyValuesHolder> props, Object... args) { + // Listener to watch for completed animations and remove them. + private static AnimatorListener mCleanupListener = new AnimatorListenerAdapter() { + + @Override + public void onAnimationEnd(Animator animation) { + remove(animation); + } + + @Override + public void onAnimationCancel(Animator animation) { + remove(animation); + } + }; + + public static void reset() { + if (DEBUG) { + Log.v(TAG, "Reset()"); + if (sTweens.size() > 0) { + Log.v(TAG, "Cleaning up " + sTweens.size() + " animations"); + } + } + sTweens.clear(); + } + + private static void replace(ArrayList<PropertyValuesHolder> props, Object... args) { for (final Object killobject : args) { Tweener tween = sTweens.get(killobject); if (tween != null) { - if (killobject == tween.object) { - tween.animator.cancel(); - if (props != null) { - tween.animator.setValues( - props.toArray(new PropertyValuesHolder[props.size()])); - } else { - sTweens.remove(tween); - } + tween.animator.cancel(); + if (props != null) { + tween.animator.setValues( + props.toArray(new PropertyValuesHolder[props.size()])); + } else { + sTweens.remove(tween); } } } diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java new file mode 100644 index 000000000000..306d2236693a --- /dev/null +++ b/core/java/com/android/server/NetworkManagementSocketTagger.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2011 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.server; + +import dalvik.system.SocketTagger; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.SocketException; +import java.nio.charset.Charsets; + +/** + * Assigns tags to sockets for traffic stats. + */ +public final class NetworkManagementSocketTagger extends SocketTagger { + + private static final boolean LOGI = false; + + private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() { + @Override protected SocketTags initialValue() { + return new SocketTags(); + } + }; + + public static void install() { + SocketTagger.set(new NetworkManagementSocketTagger()); + } + + public static void setThreadSocketStatsTag(int tag) { + threadSocketTags.get().statsTag = tag; + } + + public static void setThreadSocketStatsUid(int uid) { + threadSocketTags.get().statsUid = uid; + } + + @Override public void tag(FileDescriptor fd) throws SocketException { + final SocketTags options = threadSocketTags.get(); + if (LOGI) { + System.logI("tagSocket(" + fd.getInt$() + ") with statsTag=" + + options.statsTag + ", statsUid=" + options.statsUid); + } + try { + // TODO: skip tagging when options would be no-op + tagSocketFd(fd, options.statsTag, options.statsUid); + } catch (IOException e) { + throw new SocketException("Problem tagging socket", e); + } + } + + private void tagSocketFd(FileDescriptor fd, int tag, int uid) throws IOException { + final int fdNum = fd.getInt$(); + if (fdNum == -1 || (tag == -1 && uid == -1)) return; + + String cmd = "t " + fdNum; + if (tag == -1) { + // Case where just the uid needs adjusting. But probably the caller + // will want to track his own name here, just in case. + cmd += " 0"; + } else { + cmd += " " + tagToKernel(tag); + } + if (uid != -1) { + cmd += " " + uid; + } + internalModuleCtrl(cmd); + } + + @Override public void untag(FileDescriptor fd) throws SocketException { + if (LOGI) { + System.logI("untagSocket(" + fd.getInt$() + ")"); + } + try { + unTagSocketFd(fd); + } catch (IOException e) { + throw new SocketException("Problem untagging socket", e); + } + } + + private void unTagSocketFd(FileDescriptor fd) throws IOException { + int fdNum = fd.getInt$(); + if (fdNum == -1) return; + String cmd = "u " + fdNum; + internalModuleCtrl(cmd); + } + + public static class SocketTags { + public int statsTag = -1; + public int statsUid = -1; + } + + /** + * Sends commands to the kernel netfilter module. + * + * @param cmd command string for the qtaguid netfilter module. May not be null. + * <p>Supports: + * <ul><li>tag a socket:<br> + * <code>t <i>sock_fd</i> <i>acct_tag</i> [<i>uid_in_case_caller_is_acting_on_behalf_of</i>]</code><br> + * <code>*_tag</code> defaults to default_policy_tag_from_uid(uid_of_caller)<br> + * <code>acct_tag</code> is either 0 or greater that 2^32.<br> + * <code>uid_*</code> is only settable by privileged UIDs (DownloadManager,...) + * </li> + * <li>untag a socket, preserving counters:<br> + * <code>u <i>sock_fd</i></code> + * </li></ul> + * <p>Notes:<br> + * <ul><li><i>sock_fd</i> is withing the callers process space.</li> + * <li><i>*_tag</i> are 64bit values</li></ul> + * + */ + private void internalModuleCtrl(String cmd) throws IOException { + final FileOutputStream procOut; + // TODO: Use something like + // android.os.SystemProperties.getInt("persist.bandwidth.enable", 0) + // to see if tagging should happen or not. + try { + procOut = new FileOutputStream("/proc/net/xt_qtaguid/ctrl"); + } catch (FileNotFoundException e) { + if (LOGI) { + System.logI("Can't talk to kernel module:" + e); + } + return; + } + try { + procOut.write(cmd.getBytes(Charsets.US_ASCII)); + } finally { + procOut.close(); + } + } + + /** + * Convert {@link Integer} tag to {@code /proc/} format. Assumes unsigned + * base-10 format like {@code 2147483647}. Currently strips signed bit to + * avoid using {@link java.math.BigInteger}. + */ + public static String tagToKernel(int tag) { + // TODO: eventually write in hex, since that's what proc exports + // TODO: migrate to direct integer instead of odd shifting + return Long.toString((((long) tag) << 32) & 0x7FFFFFFF00000000L); + } + + /** + * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming + * format like {@code 0x7fffffff00000000}. + */ + public static int kernelToTag(String string) { + // TODO: migrate to direct integer instead of odd shifting + return (int) (Long.decode(string) >> 32); + } +} diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index dd8b3780f06c..2de0932e540f 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -233,12 +233,6 @@ static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz) return surfaceTexture->getTimestamp(); } -static jint SurfaceTexture_getQueuedCount(JNIEnv* env, jobject thiz) -{ - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); - return surfaceTexture->getQueuedCount(); -} - // ---------------------------------------------------------------------------- static JNINativeMethod gSurfaceTextureMethods[] = { @@ -249,7 +243,6 @@ static JNINativeMethod gSurfaceTextureMethods[] = { {"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage }, {"nativeGetTransformMatrix", "([F)V", (void*)SurfaceTexture_getTransformMatrix }, {"nativeGetTimestamp", "()J", (void*)SurfaceTexture_getTimestamp }, - {"nativeGetQueuedCount", "()I", (void*)SurfaceTexture_getQueuedCount } }; int register_android_graphics_SurfaceTexture(JNIEnv* env) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 47902a8bc2ac..1a320608efae 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1384,7 +1384,7 @@ <permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" android:label="@string/permlab_readNetworkUsageHistory" android:description="@string/permdesc_readNetworkUsageHistory" - android:protectionLevel="signature" /> + android:protectionLevel="signatureOrSystem" /> <!-- Allows an application to manage network policies (such as warning and disable limits) and to define application-specific rules. @hide --> @@ -1393,6 +1393,14 @@ android:description="@string/permdesc_manageNetworkPolicy" android:protectionLevel="signature" /> + <!-- Allows an application to account its network traffic against other UIDs. Used + by system services like download manager and media server. Not for use by + third party apps. @hide --> + <permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" + android:label="@string/permlab_modifyNetworkAccounting" + android:description="@string/permdesc_modifyNetworkAccounting" + android:protectionLevel="signatureOrSystem" /> + <!-- C2DM permission. @hide Used internally. --> diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png Binary files differdeleted file mode 100644 index 1f7aeee3bbd5..000000000000 --- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png Binary files differdeleted file mode 100755 index 4c1d89d79ed6..000000000000 --- a/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png Binary files differdeleted file mode 100644 index 6963a0e6d974..000000000000 --- a/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/cab_holo_dark.9.png b/core/res/res/drawable-hdpi/cab_holo_dark.9.png Binary files differdeleted file mode 100755 index 64e2052599ba..000000000000 --- a/core/res/res/drawable-hdpi/cab_holo_dark.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png Binary files differdeleted file mode 100644 index 3a84de954139..000000000000 --- a/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png b/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png Binary files differindex e21a87caa40c..0f4bfe6d0d1f 100644 --- a/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png +++ b/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png b/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png Binary files differindex 3283f99a555e..995705dc607e 100644 --- a/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png +++ b/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png Binary files differdeleted file mode 100644 index 48cc017c4e4b..000000000000 --- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png Binary files differdeleted file mode 100755 index 66cbe48cf941..000000000000 --- a/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png Binary files differdeleted file mode 100644 index b6508fcc88e7..000000000000 --- a/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/cab_holo_dark.9.png b/core/res/res/drawable-mdpi/cab_holo_dark.9.png Binary files differdeleted file mode 100755 index 7daae1f5d055..000000000000 --- a/core/res/res/drawable-mdpi/cab_holo_dark.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png Binary files differdeleted file mode 100644 index cb3d0f29dd58..000000000000 --- a/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png b/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png Binary files differindex c10344f83bba..754d7bc830e5 100644 --- a/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png +++ b/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png b/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png Binary files differindex 08c6cfe71bc6..0187a02afc21 100644 --- a/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png +++ b/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png Binary files differnew file mode 100644 index 000000000000..544924e4bcf9 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png Binary files differnew file mode 100644 index 000000000000..2d28009f2235 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png diff --git a/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png b/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png Binary files differnew file mode 100644 index 000000000000..cdc0afb4c30e --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png diff --git a/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png b/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png Binary files differnew file mode 100644 index 000000000000..36b044822278 --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png diff --git a/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png b/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png Binary files differnew file mode 100644 index 000000000000..3a7a8b347105 --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png diff --git a/core/res/res/drawable-xhdpi/popup_center_bright.9.png b/core/res/res/drawable-xhdpi/popup_center_bright.9.png Binary files differnew file mode 100644 index 000000000000..b1a8e3e182a1 --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_center_bright.9.png diff --git a/core/res/res/drawable-xhdpi/popup_center_dark.9.png b/core/res/res/drawable-xhdpi/popup_center_dark.9.png Binary files differnew file mode 100644 index 000000000000..87378e153330 --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_center_dark.9.png diff --git a/core/res/res/drawable-xhdpi/popup_center_medium.9.png b/core/res/res/drawable-xhdpi/popup_center_medium.9.png Binary files differnew file mode 100644 index 000000000000..ea29ed48f4ff --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_center_medium.9.png diff --git a/core/res/res/drawable-xhdpi/popup_full_bright.9.png b/core/res/res/drawable-xhdpi/popup_full_bright.9.png Binary files differnew file mode 100644 index 000000000000..114faa0e1b8c --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_full_bright.9.png diff --git a/core/res/res/drawable-xhdpi/popup_full_dark.9.png b/core/res/res/drawable-xhdpi/popup_full_dark.9.png Binary files differnew file mode 100644 index 000000000000..996beaa6e94b --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_full_dark.9.png diff --git a/core/res/res/drawable-xhdpi/popup_top_bright.9.png b/core/res/res/drawable-xhdpi/popup_top_bright.9.png Binary files differnew file mode 100644 index 000000000000..64e41394201c --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_top_bright.9.png diff --git a/core/res/res/drawable-xhdpi/popup_top_dark.9.png b/core/res/res/drawable-xhdpi/popup_top_dark.9.png Binary files differnew file mode 100644 index 000000000000..902bc291319e --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_top_dark.9.png diff --git a/core/res/res/layout-sw600dp/date_picker_dialog.xml b/core/res/res/layout-sw600dp/date_picker_dialog.xml new file mode 100644 index 000000000000..004d52a6593b --- /dev/null +++ b/core/res/res/layout-sw600dp/date_picker_dialog.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 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. +*/ +--> + +<DatePicker xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/datePicker" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> diff --git a/core/res/res/layout-sw600dp/number_picker.xml b/core/res/res/layout-sw600dp/number_picker.xml new file mode 100644 index 000000000000..807daf2b2dae --- /dev/null +++ b/core/res/res/layout-sw600dp/number_picker.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2008, 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. +*/ +--> + +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <ImageButton android:id="@+id/increment" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + style="?android:attr/numberPickerUpButtonStyle" + android:contentDescription="@string/number_picker_increment_button" /> + + <EditText android:id="@+id/numberpicker_input" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + style="?android:attr/numberPickerInputTextStyle" /> + + <ImageButton android:id="@+id/decrement" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + style="?android:attr/numberPickerDownButtonStyle" + android:contentDescription="@string/number_picker_decrement_button" /> + +</merge> diff --git a/core/res/res/layout/date_picker_dialog.xml b/core/res/res/layout/date_picker_dialog.xml index 004d52a6593b..db8f311c99c7 100644 --- a/core/res/res/layout/date_picker_dialog.xml +++ b/core/res/res/layout/date_picker_dialog.xml @@ -22,4 +22,6 @@ android:layout_gravity="center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:spinnersShown="true" + android:calendarViewShown="false" /> diff --git a/core/res/res/layout/date_picker_holo.xml b/core/res/res/layout/date_picker_holo.xml index 026cbfb8bca1..8627637b3b96 100644 --- a/core/res/res/layout/date_picker_holo.xml +++ b/core/res/res/layout/date_picker_holo.xml @@ -77,7 +77,8 @@ android:id="@+id/calendar_view" android:layout_width="245dip" android:layout_height="280dip" - android:layout_marginLeft="22dip" + android:layout_marginLeft="16dip" + android:layout_marginRight="16dip" android:layout_weight="1" android:focusable="true" android:focusableInTouchMode="true" diff --git a/core/res/res/layout/number_picker.xml b/core/res/res/layout/number_picker.xml index 807daf2b2dae..f2f524caf813 100644 --- a/core/res/res/layout/number_picker.xml +++ b/core/res/res/layout/number_picker.xml @@ -23,6 +23,8 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" style="?android:attr/numberPickerUpButtonStyle" + android:paddingTop="22dip" + android:paddingBottom="22dip" android:contentDescription="@string/number_picker_increment_button" /> <EditText android:id="@+id/numberpicker_input" @@ -34,6 +36,8 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" style="?android:attr/numberPickerDownButtonStyle" + android:paddingTop="22dip" + android:paddingBottom="22dip" android:contentDescription="@string/number_picker_decrement_button" /> </merge> diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml index d6a0cdd7d4a7..13bbac60f5c8 100644 --- a/core/res/res/values-sw600dp/config.xml +++ b/core/res/res/values-sw600dp/config.xml @@ -23,9 +23,6 @@ <!-- see comment in values/config.xml --> <integer name="config_longPressOnPowerBehavior">2</integer> - <!-- Show sliding tab before lockscreen --> - <bool name="config_enableSlidingTabFirst">false</bool> - <!-- Enable lockscreen rotation --> <bool name="config_enableLockScreenRotation">true</bool> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 37e6027fd175..fd61cfdaf6b7 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1972,6 +1972,24 @@ <!-- Locale --> <enum name="locale" value="3" /> </attr> + <!-- Direction of the text. A heuristic is used to determine the resolved text direction + of paragraphs. --> + <attr name="textDirection" format="integer"> + <!-- Default --> + <enum name="inherit" value="0" /> + <!-- Default for the root view. The first strong directional character determines the + paragraph direction. If there is o strong directional character, the paragraph + direction is the view’s resolved layout direction. --> + <enum name="firstStrong" value="1" /> + <!-- The paragraph direction is RTL if it contains any strong RTL character, otherwise + it is LTR if it contains any strong LTR characters. If there are neither, the + paragraph direction is the view’s resolved layout direction. --> + <enum name="anyRtl" value="2" /> + <!-- The text direction is left to right. --> + <enum name="ltr" value="3" /> + <!-- The text direction is right to left. --> + <enum name="rtl" value="4" /> + </attr> </declare-styleable> <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0ad3184fabda..827153e7243e 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3,16 +3,16 @@ /* ** 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 +** 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 +** 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 +** 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. */ --> @@ -54,25 +54,25 @@ connected. If you use the ims apn DCT will block any other apn from connecting until ims apn is connected--> <bool name="ImsConnectedDefaultValue">false</bool> - + <!-- Flag indicating whether the surface flinger is inefficient at performing a blur. Used by parts of the UI to turn off the blur effect where it isn't worth the performance hit. --> <bool name="config_sf_slowBlur">false</bool> - + <!-- The duration (in milliseconds) of a short animation. --> <integer name="config_shortAnimTime">200</integer> - + <!-- The duration (in milliseconds) of a medium-length animation. --> <integer name="config_mediumAnimTime">400</integer> - + <!-- The duration (in milliseconds) of a long animation. --> <integer name="config_longAnimTime">500</integer> <!-- The duration (in milliseconds) of the activity open/close and fragment open/close animations. --> <integer name="config_activityShortDur">150</integer> <integer name="config_activityDefaultDur">220</integer> - + <!-- Duration for the dim animation behind a dialog. This may be either a percentage, which is relative to the duration of the enter/open animation of the window being shown that is dimming behind, or it may @@ -83,11 +83,11 @@ maximum (let them grow as large as the screen). Actual values are specified for -large and -xlarge configurations. --> <dimen name="config_prefDialogWidth">320dp</dimen> - + <!-- Whether dialogs should close automatically when the user touches outside of them. This should not normally be modified. --> <bool name="config_closeDialogWhenTouchOutside">false</bool> - + <!-- The duration (in milliseconds) that the radio will scan for a signal when there's no network connection. If the scan doesn't timeout, use zero --> <integer name="config_radioScanningTimeout">0</integer> @@ -202,7 +202,7 @@ the slider is open. This can be set or unset depending how easily the slider can be opened (for example, in a pocket or purse). --> <bool name="config_bypass_keyguard_if_slider_open">true</bool> - + <!-- Flag indicating whether the we should enable the automatic brightness in Settings. Software implementation will be used if config_hardware_auto_brightness_available is not set --> <bool name="config_automatic_brightness_available">false</bool> @@ -212,10 +212,10 @@ <!-- If this is true, the screen will come on when you unplug usb/power/whatever. --> <bool name="config_unplugTurnsOnScreen">false</bool> - + <!-- If this is true, the screen will fade off. --> <bool name="config_animateScreenLights">true</bool> - + <!-- XXXXXX END OF RESOURCES USING WRONG NAMING CONVENTION --> <!-- If true, the screen can be rotated via the accelerometer in all 4 @@ -300,7 +300,7 @@ <item>20</item> <item>21</item> </integer-array> - + <!-- Vibrator pattern for feedback about touching a virtual key --> <integer-array name="config_virtualKeyVibePattern"> <item>0</item> @@ -380,8 +380,8 @@ <!-- Allow the menu hard key to be disabled in LockScreen on some devices --> <bool name="config_disableMenuKeyInLockScreen">false</bool> - <!-- Show sliding tab before lockscreen --> - <bool name="config_enableSlidingTabFirst">true</bool> + <!-- Don't show lock screen before unlock screen (PIN/pattern/password) --> + <bool name="config_enableLockBeforeUnlockScreen">false</bool> <!-- Diable lockscreen rotation by default --> <bool name="config_enableLockScreenRotation">false</bool> @@ -460,7 +460,7 @@ This feature should be disabled for most devices. --> <integer name="config_virtualKeyQuietTimeMillis">0</integer> - <!-- Component name of the default wallpaper. This will be ImageWallpaper if not + <!-- Component name of the default wallpaper. This will be ImageWallpaper if not specified --> <string name="default_wallpaper_component">@null</string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 7bbfa9c44e25..db6f98fa3400 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1782,4 +1782,5 @@ <public type="integer" name="status_bar_notification_info_maxnum" /> <public type="string" name="status_bar_notification_info_overflow" /> + <public type="attr" name="textDirection"/> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index cd52a5aea219..88ed9c6bc586 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1443,6 +1443,11 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_manageNetworkPolicy">Allows an application to manage network policies and define application-specific rules.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_modifyNetworkAccounting">modify network usage accounting</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_modifyNetworkAccounting">Allows modification of how network usage is accounted against applications. Not for use by normal applications.</string> + <!-- Policy administration --> <!-- Title of policy access to limiting the user's password choices --> diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index d8d145c98b2e..a37f1a37e8dc 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -22,6 +22,7 @@ import com.google.android.collect.Maps; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.text.GetChars; +import android.view.View; import android.widget.TextView; /** @@ -58,4 +59,122 @@ public class TextViewTest extends AndroidTestCase { assertEquals('o', c2[4]); assertEquals('\0', c2[5]); } + + @SmallTest + public void testTextDirectionDefault() { + TextView tv = new TextView(mContext); + assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getTextDirection()); + } + + @SmallTest + public void testSetGetTextDirection() { + TextView tv = new TextView(mContext); + + tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); + assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); + assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_LTR); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection()); + } + + @SmallTest + public void testGetResolvedTextDirectionLtr() { + TextView tv = new TextView(mContext); + tv.setText("this is a test"); + + tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_LTR); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + } + + @SmallTest + public void testGetResolvedTextDirectionLtrWithInheritance() { + LinearLayout ll = new LinearLayout(mContext); + ll.setTextDirection(View.TEXT_DIRECTION_RTL); + + TextView tv = new TextView(mContext); + tv.setText("this is a test"); + ll.addView(tv); + + tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_LTR); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + } + + @SmallTest + public void testGetResolvedTextDirectionRtl() { + TextView tv = new TextView(mContext); + tv.setText("\u05DD\u05DE"); // hebrew + + tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_LTR); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + } + + @SmallTest + public void testGetResolvedTextDirectionRtlWithInheritance() { + LinearLayout ll = new LinearLayout(mContext); + ll.setTextDirection(View.TEXT_DIRECTION_RTL); + + TextView tv = new TextView(mContext); + tv.setText("\u05DD\u05DE"); // hebrew + ll.addView(tv); + + tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_LTR); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + } } diff --git a/data/etc/platform.xml b/data/etc/platform.xml index b9c0d80ce25d..0b8d40f5d97c 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -84,6 +84,16 @@ <group gid="diag" /> </permission> + <!-- Group that can read detailed network usage statistics --> + <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"> + <group gid="net_bw_stats" /> + </permission> + + <!-- Group that can modify how network statistics are accounted --> + <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING"> + <group gid="net_bw_acct" /> + </permission> + <!-- ================================================================== --> <!-- ================================================================== --> <!-- ================================================================== --> diff --git a/data/fonts/DroidSansHebrew-Bold.ttf b/data/fonts/DroidSansHebrew-Bold.ttf Binary files differnew file mode 100644 index 000000000000..c1acb38df569 --- /dev/null +++ b/data/fonts/DroidSansHebrew-Bold.ttf diff --git a/data/fonts/DroidSansHebrew-Regular.ttf b/data/fonts/DroidSansHebrew-Regular.ttf Binary files differnew file mode 100644 index 000000000000..af6a58d8a500 --- /dev/null +++ b/data/fonts/DroidSansHebrew-Regular.ttf diff --git a/data/fonts/DroidSansHebrew.ttf b/data/fonts/DroidSansHebrew.ttf Binary files differdeleted file mode 100644 index 8d77e3e4cfaf..000000000000 --- a/data/fonts/DroidSansHebrew.ttf +++ /dev/null diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index 692ce3425d1e..d222c0b4d20e 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -18,7 +18,8 @@ PRODUCT_COPY_FILES := \ frameworks/base/data/fonts/DroidSans.ttf:system/fonts/DroidSans.ttf \ frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.ttf \ frameworks/base/data/fonts/DroidSansArabic.ttf:system/fonts/DroidSansArabic.ttf \ - frameworks/base/data/fonts/DroidSansHebrew.ttf:system/fonts/DroidSansHebrew.ttf \ + frameworks/base/data/fonts/DroidSansHebrew-Regular.ttf:system/fonts/DroidSansHebrew-Regular.ttf \ + frameworks/base/data/fonts/DroidSansHebrew-Bold.ttf:system/fonts/DroidSansHebrew-Bold.ttf \ frameworks/base/data/fonts/DroidSansThai.ttf:system/fonts/DroidSansThai.ttf \ frameworks/base/data/fonts/DroidSerif-Regular.ttf:system/fonts/DroidSerif-Regular.ttf \ frameworks/base/data/fonts/DroidSerif-Bold.ttf:system/fonts/DroidSerif-Bold.ttf \ diff --git a/docs/html/sdk/android-3.1-highlights.jd b/docs/html/sdk/android-3.1-highlights.jd index 3d132a3e8802..88bc1eec0c55 100644 --- a/docs/html/sdk/android-3.1-highlights.jd +++ b/docs/html/sdk/android-3.1-highlights.jd @@ -143,8 +143,8 @@ point, select, drag, scroll, hover, and other standard actions.</p> <p>To make the platform even better for gaming, Android 3.1 adds support for most PC joysticks and gamepads that are connected over USB or Bluetooth HID.</p> -<p>For example, users can connect Sony Playstation™ 3 and XBox 360™ game -controllers over USB (but not Bluetooth), Logitech Dual Action™ gamepads and +<p>For example, users can connect PlayStation<sup>®</sup>3 and Xbox 360<sup>®</sup> +game controllers over USB (but not Bluetooth), Logitech Dual Action™ gamepads and flight sticks, or a car racing controller. Game controllers that use proprietary networking or pairing are not supported by default, but in general, the platform supports most PC-connectible joysticks and gamepads.</p> diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java index adb6eac3e6d5..90a7ac22335b 100644 --- a/graphics/java/android/graphics/SurfaceTexture.java +++ b/graphics/java/android/graphics/SurfaceTexture.java @@ -144,10 +144,6 @@ public class SurfaceTexture { */ public void updateTexImage() { nativeUpdateTexImage(); - if (nativeGetQueuedCount() > 0) { - Message m = mEventHandler.obtainMessage(); - mEventHandler.sendMessage(m); - } } /** diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index e558dfd6c252..c82fb9b77ed4 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -46,11 +46,14 @@ public: enum { NUM_BUFFER_SLOTS = 32 }; struct FrameAvailableListener : public virtual RefBase { - // onFrameAvailable() is called from queueBuffer() is the FIFO is - // empty. You can use SurfaceTexture::getQueuedCount() to - // figure out if there are more frames waiting. - // This is called without any lock held can be called concurrently by - // multiple threads. + // onFrameAvailable() is called from queueBuffer() each time an + // additional frame becomes available for consumption. This means that + // frames that are queued while in asynchronous mode only trigger the + // callback if no previous frames are pending. Frames queued while in + // synchronous mode always trigger the callback. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. virtual void onFrameAvailable() = 0; }; @@ -101,11 +104,6 @@ public: // target texture belongs is bound to the calling thread. status_t updateTexImage(); - // getqueuedCount returns the number of queued frames waiting in the - // FIFO. In asynchronous mode, this always returns 0 or 1 since - // frames are not accumulating in the FIFO. - size_t getQueuedCount() const; - // setBufferCountServer set the buffer count. If the client has requested // a buffer count using setBufferCount, the server-buffer count will // take effect once the client sets the count back to zero. diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h index f2c65057b809..e4c1c49a72d6 100644 --- a/include/media/stagefright/MPEG2TSWriter.h +++ b/include/media/stagefright/MPEG2TSWriter.h @@ -31,6 +31,10 @@ struct MPEG2TSWriter : public MediaWriter { MPEG2TSWriter(int fd); MPEG2TSWriter(const char *filename); + MPEG2TSWriter( + void *cookie, + ssize_t (*write)(void *cookie, const void *data, size_t size)); + virtual status_t addSource(const sp<MediaSource> &source); virtual status_t start(MetaData *param = NULL); virtual status_t stop(); @@ -51,6 +55,10 @@ private: struct SourceInfo; FILE *mFile; + + void *mWriteCookie; + ssize_t (*mWriteFunc)(void *cookie, const void *data, size_t size); + sp<ALooper> mLooper; sp<AHandlerReflector<MPEG2TSWriter> > mReflector; @@ -69,6 +77,8 @@ private: void writeProgramMap(); void writeAccessUnit(int32_t sourceIndex, const sp<ABuffer> &buffer); + ssize_t internalWrite(const void *data, size_t size); + DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSWriter); }; diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 589cefda2038..92331a16f96a 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -79,6 +79,13 @@ struct OMXCodec : public MediaSource, // from MediaBufferObserver virtual void signalBufferReturned(MediaBuffer *buffer); + // for use by ACodec + static void findMatchingCodecs( + const char *mime, + bool createEncoder, const char *matchComponentName, + uint32_t flags, + Vector<String8> *matchingCodecs); + protected: virtual ~OMXCodec(); @@ -311,12 +318,6 @@ private: static uint32_t getComponentQuirks( const char *componentName, bool isEncoder); - static void findMatchingCodecs( - const char *mime, - bool createEncoder, const char *matchComponentName, - uint32_t flags, - Vector<String8> *matchingCodecs); - void restorePatchedDataPointer(BufferInfo *info); status_t applyRotation(); diff --git a/include/private/surfaceflinger/LayerState.h b/include/private/surfaceflinger/LayerState.h index d7fe572e954e..d2fed418f0d3 100644 --- a/include/private/surfaceflinger/LayerState.h +++ b/include/private/surfaceflinger/LayerState.h @@ -29,6 +29,7 @@ namespace android { class Parcel; +class ISurfaceComposerClient; struct layer_state_t { @@ -68,6 +69,13 @@ struct layer_state_t { Region transparentRegion; }; +struct ComposerState { + sp<ISurfaceComposerClient> client; + layer_state_t state; + status_t write(Parcel& output) const; + status_t read(const Parcel& input); +}; + }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index 03fd01b31850..dba98a32c325 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -34,6 +34,7 @@ namespace android { // ---------------------------------------------------------------------------- class IMemoryHeap; +class ComposerState; class ISurfaceComposer : public IInterface { @@ -105,8 +106,7 @@ public: virtual sp<IMemoryHeap> getCblk() const = 0; /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ - virtual void openGlobalTransaction() = 0; - virtual void closeGlobalTransaction() = 0; + virtual void setTransactionState(const Vector<ComposerState>& state) = 0; /* [un]freeze display. requires ACCESS_SURFACE_FLINGER permission */ virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags) = 0; @@ -149,8 +149,7 @@ public: CREATE_CONNECTION, CREATE_GRAPHIC_BUFFER_ALLOC, GET_CBLK, - OPEN_GLOBAL_TRANSACTION, - CLOSE_GLOBAL_TRANSACTION, + SET_TRANSACTION_STATE, SET_ORIENTATION, FREEZE_DISPLAY, UNFREEZE_DISPLAY, diff --git a/include/surfaceflinger/ISurfaceComposerClient.h b/include/surfaceflinger/ISurfaceComposerClient.h index 2e75a0e4cfd9..6e9a654642c5 100644 --- a/include/surfaceflinger/ISurfaceComposerClient.h +++ b/include/surfaceflinger/ISurfaceComposerClient.h @@ -37,8 +37,6 @@ typedef int32_t DisplayID; // ---------------------------------------------------------------------------- -class layer_state_t; - class ISurfaceComposerClient : public IInterface { public: @@ -69,11 +67,6 @@ public: * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t destroySurface(SurfaceID sid) = 0; - - /* - * Requires ACCESS_SURFACE_FLINGER permission - */ - virtual status_t setState(int32_t count, const layer_state_t* states) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h index 140b9f860bfd..7fbbfb24f03a 100644 --- a/include/surfaceflinger/SurfaceComposerClient.h +++ b/include/surfaceflinger/SurfaceComposerClient.h @@ -37,10 +37,12 @@ namespace android { // --------------------------------------------------------------------------- class DisplayInfo; +class Composer; class IMemoryHeap; class ISurfaceComposer; class Region; class surface_flinger_cblk_t; +struct layer_state_t; // --------------------------------------------------------------------------- @@ -59,8 +61,11 @@ public: // --------------------------------------------------------------------------- +class Composer; + class SurfaceComposerClient : public RefBase { + friend class Composer; public: SurfaceComposerClient(); virtual ~SurfaceComposerClient(); @@ -101,13 +106,7 @@ public: // All composer parameters must be changed within a transaction // several surfaces can be updated in one transaction, all changes are // committed at once when the transaction is closed. - // CloseTransaction() usually requires an IPC with the server. - - //! Open a composer transaction - status_t openTransaction(); - - //! commit the transaction - status_t closeTransaction(); + // closeGlobalTransaction() usually requires an IPC with the server. //! Open a composer transaction on all active SurfaceComposerClients. static void openGlobalTransaction(); @@ -152,19 +151,12 @@ public: private: virtual void onFirstRef(); - inline layer_state_t* get_state_l(SurfaceID id); - layer_state_t* lockLayerState(SurfaceID id); - inline void unlockLayerState(); - - mutable Mutex mLock; - SortedVector<layer_state_t> mStates; - int32_t mTransactionOpen; - layer_state_t* mPrebuiltLayerState; + Composer& getComposer(); - // these don't need to be protected because they never change - // after assignment + mutable Mutex mLock; status_t mStatus; sp<ISurfaceComposerClient> mClient; + Composer& mComposer; }; // --------------------------------------------------------------------------- diff --git a/include/utils/LinearTransform.h b/include/utils/LinearTransform.h new file mode 100644 index 000000000000..04cb355c7ff7 --- /dev/null +++ b/include/utils/LinearTransform.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 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 _LIBS_UTILS_LINEAR_TRANSFORM_H +#define _LIBS_UTILS_LINEAR_TRANSFORM_H + +#include <stdint.h> + +namespace android { + +// LinearTransform defines a structure which hold the definition of a +// transformation from single dimensional coordinate system A into coordinate +// system B (and back again). Values in A and in B are 64 bit, the linear +// scale factor is expressed as a rational number using two 32 bit values. +// +// Specifically, let +// f(a) = b +// F(b) = f^-1(b) = a +// then +// +// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero; +// +// and +// +// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero; +// +struct LinearTransform { + int64_t a_zero; + int64_t b_zero; + int32_t a_to_b_numer; + uint32_t a_to_b_denom; + + // Transform from A->B + // Returns true on success, or false in the case of a singularity or an + // overflow. + bool doForwardTransform(int64_t a_in, int64_t* b_out) const; + + // Transform from B->A + // Returns true on success, or false in the case of a singularity or an + // overflow. + bool doReverseTransform(int64_t b_in, int64_t* a_out) const; + + // Helpers which will reduce the fraction N/D using Euclid's method. + template <class T> static void reduce(T* N, T* D); + static void reduce(int32_t* N, uint32_t* D); +}; + + +} + +#endif // _LIBS_UTILS_LINEAR_TRANSFORM_H diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h index 8beec573205e..0e98aeb05fd8 100644 --- a/include/utils/SortedVector.h +++ b/include/utils/SortedVector.h @@ -32,6 +32,8 @@ namespace android { template <class TYPE> class SortedVector : private SortedVectorImpl { + friend class Vector<TYPE>; + public: typedef TYPE value_type; diff --git a/include/utils/Vector.h b/include/utils/Vector.h index f1e87e60907e..b908e2ab2e2c 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -29,6 +29,9 @@ namespace android { +template <typename TYPE> +class SortedVector; + /*! * The main templated vector class ensuring type safety * while making use of VectorImpl. @@ -47,13 +50,17 @@ public: Vector(); Vector(const Vector<TYPE>& rhs); + explicit Vector(const SortedVector<TYPE>& rhs); virtual ~Vector(); /*! copy operator */ const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const; Vector<TYPE>& operator = (const Vector<TYPE>& rhs); - /* + const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const; + Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs); + + /* * empty the vector */ @@ -215,6 +222,11 @@ Vector<TYPE>::Vector(const Vector<TYPE>& rhs) } template<class TYPE> inline +Vector<TYPE>::Vector(const SortedVector<TYPE>& rhs) + : VectorImpl(static_cast<const VectorImpl&>(rhs)) { +} + +template<class TYPE> inline Vector<TYPE>::~Vector() { finish_vector(); } @@ -227,6 +239,18 @@ Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) { template<class TYPE> inline const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const { + VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)); + return *this; +} + +template<class TYPE> inline +Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) { + VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)); + return *this; +} + +template<class TYPE> inline +const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const { VectorImpl::operator = (rhs); return *this; } diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index ab4b9e04d671..f75208dfd0aa 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -60,16 +60,10 @@ public class Credentials { public static final String WIFI = "WIFI_"; /** Data type for public keys. */ - public static final String PUBLIC_KEY = "KEY"; + public static final String EXTRA_PUBLIC_KEY = "KEY"; /** Data type for private keys. */ - public static final String PRIVATE_KEY = "PKEY"; - - /** Data type for certificates. */ - public static final String CERTIFICATE = "CERT"; - - /** Data type for PKCS12. */ - public static final String PKCS12 = "PKCS12"; + public static final String EXTRA_PRIVATE_KEY = "PKEY"; // historically used by Android public static final String EXTENSION_CRT = ".crt"; @@ -130,16 +124,9 @@ public class Credentials { } } - private Intent createInstallIntent() { - Intent intent = new Intent(INSTALL_ACTION); - intent.setClassName("com.android.certinstaller", - "com.android.certinstaller.CertInstallerMain"); - return intent; - } - public void install(Context context) { try { - Intent intent = createInstallIntent(); + Intent intent = KeyChain.createInstallIntent(); context.startActivity(intent); } catch (ActivityNotFoundException e) { Log.w(LOGTAG, e.toString()); @@ -148,9 +135,9 @@ public class Credentials { public void install(Context context, KeyPair pair) { try { - Intent intent = createInstallIntent(); - intent.putExtra(PRIVATE_KEY, pair.getPrivate().getEncoded()); - intent.putExtra(PUBLIC_KEY, pair.getPublic().getEncoded()); + Intent intent = KeyChain.createInstallIntent(); + intent.putExtra(EXTRA_PRIVATE_KEY, pair.getPrivate().getEncoded()); + intent.putExtra(EXTRA_PUBLIC_KEY, pair.getPublic().getEncoded()); context.startActivity(intent); } catch (ActivityNotFoundException e) { Log.w(LOGTAG, e.toString()); @@ -159,7 +146,7 @@ public class Credentials { public void install(Context context, String type, byte[] value) { try { - Intent intent = createInstallIntent(); + Intent intent = KeyChain.createInstallIntent(); intent.putExtra(type, value); context.startActivity(intent); } catch (ActivityNotFoundException e) { diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 18011e618f49..b567207873fd 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -89,31 +89,117 @@ public final class KeyChain { public static final String ACCOUNT_TYPE = "com.android.keychain"; /** + * Action to bring up the KeyChainActivity + */ + private static final String ACTION_CHOOSER = "com.android.keychain.CHOOSER"; + + /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_RESPONSE = "response"; /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_HOST = "host"; /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_PORT = "port"; /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_ALIAS = "alias"; /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_SENDER = "sender"; /** + * Action to bring up the CertInstaller + */ + private static final String ACTION_INSTALL = "android.credentials.INSTALL"; + + /** + * Optional extra to specify a {@code String} credential name on + * the {@code Intent} returned by {@link #createInstallIntent}. + * + * @hide TODO make public + */ + // Compatible with old com.android.certinstaller.CredentialHelper.CERT_NAME_KEY + public static final String EXTRA_NAME = "name"; + + /** + * Optional extra to specify an X.509 certificate to install on + * the {@code Intent} returned by {@link #createInstallIntent}. + * The extra value should be a PEM or ASN.1 DER encoded {@code + * byte[]}. An {@link X509Certificate} can be converted to DER + * encoded bytes with {@link X509Certificate#getEncoded}. + * + * <p>{@link #EXTRA_NAME} may be used to provide a default alias + * name for the installed certificate. + * + * @hide TODO make public + */ + // Compatible with old android.security.Credentials.CERTIFICATE + public static final String EXTRA_CERTIFICATE = "CERT"; + + /** + * Optional extra for use with the {@code Intent} returned by + * {@link #createInstallIntent} to specify a PKCS#12 key store to + * install. The extra value should be a {@code byte[]}. The bytes + * may come from an external source or be generated with {@link + * KeyStore#store} on a "PKCS12" instance. + * + * <p>The user will be prompted for the password to load the key store. + * + * <p>The key store will be scanned for {@link + * java.security.KeyStore.PrivateKeyEntry} entries and both the + * private key and associated certificate chain will be installed. + * + * <p>{@link #EXTRA_NAME} may be used to provide a default alias + * name for the installed credentials. + * + * @hide TODO make public + */ + // Compatible with old android.security.Credentials.PKCS12 + public static final String EXTRA_PKCS12 = "PKCS12"; + + /** + * Returns an {@code Intent} that can be used for credential + * installation. The intent may be used without any extras, in + * which case the user will be able to install credentials from + * their own source. + * + * <p>Alternatively, {@link #EXTRA_CERTIFICATE} or {@link + * #EXTRA_PKCS12} maybe used to specify the bytes of an X.509 + * certificate or a PKCS#12 key store for installation. These + * extras may be combined with {@link EXTRA_NAME} to provide a + * default alias name for credentials being installed. + * + * <p>When used with {@link Activity#startActivityForResult}, + * {@link Activity#RESULT_OK} will be returned if a credential was + * successfully installed, otherwise {@link + * Activity#RESULT_CANCELED} will be returned. + * + * @hide TODO make public with createInstallIntent, EXTRA_NAME, EXTRA_CERTIFICATE, EXTRA_PKCS12 + */ + public static Intent createInstallIntent() { + Intent intent = new Intent(ACTION_INSTALL); + intent.setClassName("com.android.certinstaller", + "com.android.certinstaller.CertInstallerMain"); + return intent; + } + + /** * Launches an {@code Activity} for the user to select the alias * for a private key and certificate pair for authentication. The * selected alias or null will be returned via the @@ -176,7 +262,7 @@ public final class KeyChain { if (response == null) { throw new NullPointerException("response == null"); } - Intent intent = new Intent("com.android.keychain.CHOOSER"); + Intent intent = new Intent(ACTION_CHOOSER); intent.putExtra(EXTRA_RESPONSE, new AliasResponse(activity, response)); intent.putExtra(EXTRA_HOST, host); intent.putExtra(EXTRA_PORT, port); diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 40450a3d0750..c1156d5c86c5 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -25,6 +25,8 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <private/surfaceflinger/LayerState.h> + #include <surfaceflinger/ISurfaceComposer.h> #include <ui/DisplayInfo.h> @@ -74,18 +76,17 @@ public: return interface_cast<IMemoryHeap>(reply.readStrongBinder()); } - virtual void openGlobalTransaction() + virtual void setTransactionState(const Vector<ComposerState>& state) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::OPEN_GLOBAL_TRANSACTION, data, &reply); - } - - virtual void closeGlobalTransaction() - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CLOSE_GLOBAL_TRANSACTION, data, &reply); + Vector<ComposerState>::const_iterator b(state.begin()); + Vector<ComposerState>::const_iterator e(state.end()); + data.writeInt32(state.size()); + for ( ; b != e ; ++b ) { + b->write(data); + } + remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); } virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags) @@ -218,13 +219,17 @@ status_t BnSurfaceComposer::onTransact( sp<IBinder> b = createGraphicBufferAlloc()->asBinder(); reply->writeStrongBinder(b); } break; - case OPEN_GLOBAL_TRANSACTION: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - openGlobalTransaction(); - } break; - case CLOSE_GLOBAL_TRANSACTION: { + case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - closeGlobalTransaction(); + size_t count = data.readInt32(); + ComposerState s; + Vector<ComposerState> state; + state.setCapacity(count); + for (size_t i=0 ; i<count ; i++) { + s.read(data); + state.add(s); + } + setTransactionState(state); } break; case SET_ORIENTATION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index 8d8339258860..bc97cacbe0a3 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -51,8 +51,7 @@ namespace android { enum { CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, - DESTROY_SURFACE, - SET_STATE + DESTROY_SURFACE }; class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient> @@ -92,17 +91,6 @@ public: remote()->transact(DESTROY_SURFACE, data, &reply); return reply.readInt32(); } - - virtual status_t setState(int32_t count, const layer_state_t* states) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - data.writeInt32(count); - for (int i=0 ; i<count ; i++) - states[i].write(data); - remote()->transact(SET_STATE, data, &reply); - return reply.readInt32(); - } }; IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient"); @@ -133,17 +121,6 @@ status_t BnSurfaceComposerClient::onTransact( reply->writeInt32( destroySurface( data.readInt32() ) ); return NO_ERROR; } break; - case SET_STATE: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - int32_t count = data.readInt32(); - layer_state_t* states = new layer_state_t[count]; - for (int i=0 ; i<count ; i++) - states[i].read(data); - status_t err = setState(count, states); - delete [] states; - reply->writeInt32(err); - return NO_ERROR; - } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 01c4c7ebfe84..87901e87f706 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -17,6 +17,7 @@ #include <utils/Errors.h> #include <binder/Parcel.h> #include <private/surfaceflinger/LayerState.h> +#include <surfaceflinger/ISurfaceComposerClient.h> namespace android { @@ -58,4 +59,14 @@ status_t layer_state_t::read(const Parcel& input) return NO_ERROR; } +status_t ComposerState::write(Parcel& output) const { + output.writeStrongBinder(client->asBinder()); + return state.write(output); +} + +status_t ComposerState::read(const Parcel& input) { + client = interface_cast<ISurfaceComposerClient>(input.readStrongBinder()); + return state.read(input); +} + }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 1678711aaf42..8cead808a1b2 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -74,75 +74,52 @@ static inline surface_flinger_cblk_t const volatile * get_cblk() { // --------------------------------------------------------------------------- +// NOTE: this is NOT a member function (it's a friend defined with its +// declaration). +static inline +int compare_type( const ComposerState& lhs, const ComposerState& rhs) { + if (lhs.client < rhs.client) return -1; + if (lhs.client > rhs.client) return 1; + if (lhs.state.surface < rhs.state.surface) return -1; + if (lhs.state.surface > rhs.state.surface) return 1; + return 0; +} + class Composer : public Singleton<Composer> { - Mutex mLock; - SortedVector< wp<SurfaceComposerClient> > mActiveConnections; - SortedVector<sp<SurfaceComposerClient> > mOpenTransactions; + friend class Singleton<Composer>; - Composer() : Singleton<Composer>() { - } + mutable Mutex mLock; + SortedVector<ComposerState> mStates; - void addClientImpl(const sp<SurfaceComposerClient>& client) { - Mutex::Autolock _l(mLock); - mActiveConnections.add(client); - } + Composer() : Singleton<Composer>() { } - void removeClientImpl(const sp<SurfaceComposerClient>& client) { - Mutex::Autolock _l(mLock); - mActiveConnections.remove(client); - } + void closeGlobalTransactionImpl(); - void openGlobalTransactionImpl() - { - Mutex::Autolock _l(mLock); - if (mOpenTransactions.size()) { - LOGE("openGlobalTransaction() called more than once. skipping."); - return; - } - const size_t N = mActiveConnections.size(); - for (size_t i=0; i<N; i++) { - sp<SurfaceComposerClient> client(mActiveConnections[i].promote()); - if (client != 0 && mOpenTransactions.indexOf(client) < 0) { - if (client->openTransaction() == NO_ERROR) { - mOpenTransactions.add(client); - } else { - LOGE("openTransaction on client %p failed", client.get()); - // let it go, it'll fail later when the user - // tries to do something with the transaction - } - } - } - } + layer_state_t* getLayerStateLocked( + const sp<SurfaceComposerClient>& client, SurfaceID id); - void closeGlobalTransactionImpl() - { - mLock.lock(); - SortedVector< sp<SurfaceComposerClient> > clients(mOpenTransactions); - mOpenTransactions.clear(); - mLock.unlock(); - - sp<ISurfaceComposer> sm(getComposerService()); - sm->openGlobalTransaction(); - const size_t N = clients.size(); - for (size_t i=0; i<N; i++) { - clients[i]->closeTransaction(); - } - sm->closeGlobalTransaction(); - } +public: - friend class Singleton<Composer>; + status_t setPosition(const sp<SurfaceComposerClient>& client, SurfaceID id, + int32_t x, int32_t y); + status_t setSize(const sp<SurfaceComposerClient>& client, SurfaceID id, + uint32_t w, uint32_t h); + status_t setLayer(const sp<SurfaceComposerClient>& client, SurfaceID id, + int32_t z); + status_t setFlags(const sp<SurfaceComposerClient>& client, SurfaceID id, + uint32_t flags, uint32_t mask); + status_t setTransparentRegionHint( + const sp<SurfaceComposerClient>& client, SurfaceID id, + const Region& transparentRegion); + status_t setAlpha(const sp<SurfaceComposerClient>& client, SurfaceID id, + float alpha); + status_t setMatrix(const sp<SurfaceComposerClient>& client, SurfaceID id, + float dsdx, float dtdx, float dsdy, float dtdy); + status_t setFreezeTint( + const sp<SurfaceComposerClient>& client, SurfaceID id, + uint32_t tint); -public: - static void addClient(const sp<SurfaceComposerClient>& client) { - Composer::getInstance().addClientImpl(client); - } - static void removeClient(const sp<SurfaceComposerClient>& client) { - Composer::getInstance().removeClientImpl(client); - } - static void openGlobalTransaction() { - Composer::getInstance().openGlobalTransactionImpl(); - } static void closeGlobalTransaction() { Composer::getInstance().closeGlobalTransactionImpl(); } @@ -152,127 +129,185 @@ ANDROID_SINGLETON_STATIC_INSTANCE(Composer); // --------------------------------------------------------------------------- -static inline int compare_type( const layer_state_t& lhs, - const layer_state_t& rhs) { - if (lhs.surface < rhs.surface) return -1; - if (lhs.surface > rhs.surface) return 1; - return 0; +void Composer::closeGlobalTransactionImpl() { + sp<ISurfaceComposer> sm(getComposerService()); + + Vector<ComposerState> transaction; + + { // scope for the lock + Mutex::Autolock _l(mLock); + transaction = mStates; + mStates.clear(); + } + + sm->setTransactionState(transaction); +} + +layer_state_t* Composer::getLayerStateLocked( + const sp<SurfaceComposerClient>& client, SurfaceID id) { + + ComposerState s; + s.client = client->mClient; + s.state.surface = id; + + ssize_t index = mStates.indexOf(s); + if (index < 0) { + // we don't have it, add an initialized layer_state to our list + index = mStates.add(s); + } + + ComposerState* const out = mStates.editArray(); + return &(out[index].state); +} + +status_t Composer::setPosition(const sp<SurfaceComposerClient>& client, + SurfaceID id, int32_t x, int32_t y) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::ePositionChanged; + s->x = x; + s->y = y; + return NO_ERROR; +} + +status_t Composer::setSize(const sp<SurfaceComposerClient>& client, + SurfaceID id, uint32_t w, uint32_t h) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eSizeChanged; + s->w = w; + s->h = h; + return NO_ERROR; +} + +status_t Composer::setLayer(const sp<SurfaceComposerClient>& client, + SurfaceID id, int32_t z) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eLayerChanged; + s->z = z; + return NO_ERROR; +} + +status_t Composer::setFlags(const sp<SurfaceComposerClient>& client, + SurfaceID id, uint32_t flags, + uint32_t mask) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eVisibilityChanged; + s->flags &= ~mask; + s->flags |= (flags & mask); + s->mask |= mask; + return NO_ERROR; +} + +status_t Composer::setTransparentRegionHint( + const sp<SurfaceComposerClient>& client, SurfaceID id, + const Region& transparentRegion) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eTransparentRegionChanged; + s->transparentRegion = transparentRegion; + return NO_ERROR; +} + +status_t Composer::setAlpha(const sp<SurfaceComposerClient>& client, + SurfaceID id, float alpha) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eAlphaChanged; + s->alpha = alpha; + return NO_ERROR; +} + +status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client, + SurfaceID id, float dsdx, float dtdx, + float dsdy, float dtdy) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eMatrixChanged; + layer_state_t::matrix22_t matrix; + matrix.dsdx = dsdx; + matrix.dtdx = dtdx; + matrix.dsdy = dsdy; + matrix.dtdy = dtdy; + s->matrix = matrix; + return NO_ERROR; } +status_t Composer::setFreezeTint(const sp<SurfaceComposerClient>& client, + SurfaceID id, uint32_t tint) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eFreezeTintChanged; + s->tint = tint; + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + SurfaceComposerClient::SurfaceComposerClient() - : mTransactionOpen(0), mPrebuiltLayerState(0), mStatus(NO_INIT) + : mStatus(NO_INIT), mComposer(Composer::getInstance()) { } -void SurfaceComposerClient::onFirstRef() -{ +void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sm(getComposerService()); if (sm != 0) { sp<ISurfaceComposerClient> conn = sm->createConnection(); if (conn != 0) { mClient = conn; - Composer::addClient(this); - mPrebuiltLayerState = new layer_state_t; mStatus = NO_ERROR; } } } -SurfaceComposerClient::~SurfaceComposerClient() -{ - delete mPrebuiltLayerState; +SurfaceComposerClient::~SurfaceComposerClient() { dispose(); } -status_t SurfaceComposerClient::initCheck() const -{ +status_t SurfaceComposerClient::initCheck() const { return mStatus; } -sp<IBinder> SurfaceComposerClient::connection() const -{ +sp<IBinder> SurfaceComposerClient::connection() const { return (mClient != 0) ? mClient->asBinder() : 0; } status_t SurfaceComposerClient::linkToComposerDeath( const sp<IBinder::DeathRecipient>& recipient, - void* cookie, uint32_t flags) -{ + void* cookie, uint32_t flags) { sp<ISurfaceComposer> sm(getComposerService()); return sm->asBinder()->linkToDeath(recipient, cookie, flags); } -void SurfaceComposerClient::dispose() -{ +void SurfaceComposerClient::dispose() { // this can be called more than once. sp<ISurfaceComposerClient> client; Mutex::Autolock _lm(mLock); if (mClient != 0) { - Composer::removeClient(this); client = mClient; // hold ref while lock is held mClient.clear(); } mStatus = NO_INIT; } -status_t SurfaceComposerClient::getDisplayInfo( - DisplayID dpy, DisplayInfo* info) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - - info->w = dcblk->w; - info->h = dcblk->h; - info->orientation = dcblk->orientation; - info->xdpi = dcblk->xdpi; - info->ydpi = dcblk->ydpi; - info->fps = dcblk->fps; - info->density = dcblk->density; - return getPixelFormatInfo(dcblk->format, &(info->pixelFormatInfo)); -} - -ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - return dcblk->w; -} - -ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - return dcblk->h; -} - -ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - return dcblk->orientation; -} - -ssize_t SurfaceComposerClient::getNumberOfDisplays() -{ - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - uint32_t connected = cblk->connected; - int n = 0; - while (connected) { - if (connected&1) n++; - connected >>= 1; - } - return n; -} - sp<SurfaceControl> SurfaceComposerClient::createSurface( DisplayID display, uint32_t w, @@ -310,237 +345,167 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface( return result; } -status_t SurfaceComposerClient::destroySurface(SurfaceID sid) -{ +status_t SurfaceComposerClient::destroySurface(SurfaceID sid) { if (mStatus != NO_ERROR) return mStatus; - - // it's okay to destroy a surface while a transaction is open, - // (transactions really are a client-side concept) - // however, this indicates probably a misuse of the API or a bug - // in the client code. - LOGW_IF(mTransactionOpen, - "Destroying surface while a transaction is open. " - "Client %p: destroying surface %d, mTransactionOpen=%d", - this, sid, mTransactionOpen); - status_t err = mClient->destroySurface(sid); return err; } -void SurfaceComposerClient::openGlobalTransaction() -{ - Composer::openGlobalTransaction(); +inline Composer& SurfaceComposerClient::getComposer() { + return mComposer; } -void SurfaceComposerClient::closeGlobalTransaction() -{ - Composer::closeGlobalTransaction(); -} +// ---------------------------------------------------------------------------- -status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) -{ - sp<ISurfaceComposer> sm(getComposerService()); - return sm->freezeDisplay(dpy, flags); +void SurfaceComposerClient::openGlobalTransaction() { + // Currently a no-op } -status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags) -{ - sp<ISurfaceComposer> sm(getComposerService()); - return sm->unfreezeDisplay(dpy, flags); +void SurfaceComposerClient::closeGlobalTransaction() { + Composer::closeGlobalTransaction(); } -int SurfaceComposerClient::setOrientation(DisplayID dpy, - int orientation, uint32_t flags) -{ - sp<ISurfaceComposer> sm(getComposerService()); - return sm->setOrientation(dpy, orientation, flags); -} +// ---------------------------------------------------------------------------- -status_t SurfaceComposerClient::openTransaction() -{ - if (mStatus != NO_ERROR) - return mStatus; - Mutex::Autolock _l(mLock); - mTransactionOpen++; - return NO_ERROR; +status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) { + return getComposer().setFreezeTint(this, id, tint); } -status_t SurfaceComposerClient::closeTransaction() -{ - if (mStatus != NO_ERROR) - return mStatus; - - Mutex::Autolock _l(mLock); - if (mTransactionOpen <= 0) { - LOGE( "closeTransaction (client %p, mTransactionOpen=%d) " - "called more times than openTransaction()", - this, mTransactionOpen); - return INVALID_OPERATION; - } +status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) { + return getComposer().setPosition(this, id, x, y); +} - if (mTransactionOpen >= 2) { - mTransactionOpen--; - return NO_ERROR; - } +status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) { + return getComposer().setSize(this, id, w, h); +} - mTransactionOpen = 0; - const ssize_t count = mStates.size(); - if (count) { - mClient->setState(count, mStates.array()); - mStates.clear(); - } - return NO_ERROR; +status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) { + return getComposer().setLayer(this, id, z); } -layer_state_t* SurfaceComposerClient::get_state_l(SurfaceID index) -{ - // API usage error, do nothing. - if (mTransactionOpen<=0) { - LOGE("Not in transaction (client=%p, SurfaceID=%d, mTransactionOpen=%d", - this, int(index), mTransactionOpen); - return 0; - } +status_t SurfaceComposerClient::hide(SurfaceID id) { + return getComposer().setFlags(this, id, + ISurfaceComposer::eLayerHidden, + ISurfaceComposer::eLayerHidden); +} - // use mPrebuiltLayerState just to find out if we already have it - layer_state_t& dummy(*mPrebuiltLayerState); - dummy.surface = index; - ssize_t i = mStates.indexOf(dummy); - if (i < 0) { - // we don't have it, add an initialized layer_state to our list - i = mStates.add(dummy); - } - return mStates.editArray() + i; +status_t SurfaceComposerClient::show(SurfaceID id, int32_t) { + return getComposer().setFlags(this, id, + 0, + ISurfaceComposer::eLayerHidden); } -layer_state_t* SurfaceComposerClient::lockLayerState(SurfaceID id) -{ - layer_state_t* s; - mLock.lock(); - s = get_state_l(id); - if (!s) mLock.unlock(); - return s; +status_t SurfaceComposerClient::freeze(SurfaceID id) { + return getComposer().setFlags(this, id, + ISurfaceComposer::eLayerFrozen, + ISurfaceComposer::eLayerFrozen); } -void SurfaceComposerClient::unlockLayerState() -{ - mLock.unlock(); +status_t SurfaceComposerClient::unfreeze(SurfaceID id) { + return getComposer().setFlags(this, id, + 0, + ISurfaceComposer::eLayerFrozen); } -status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) -{ - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::ePositionChanged; - s->x = x; - s->y = y; - unlockLayerState(); - return NO_ERROR; +status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags, + uint32_t mask) { + return getComposer().setFlags(this, id, flags, mask); } -status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) -{ - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eSizeChanged; - s->w = w; - s->h = h; - unlockLayerState(); - return NO_ERROR; +status_t SurfaceComposerClient::setTransparentRegionHint(SurfaceID id, + const Region& transparentRegion) { + return getComposer().setTransparentRegionHint(this, id, transparentRegion); } -status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) -{ - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eLayerChanged; - s->z = z; - unlockLayerState(); - return NO_ERROR; +status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) { + return getComposer().setAlpha(this, id, alpha); } -status_t SurfaceComposerClient::hide(SurfaceID id) -{ - return setFlags(id, ISurfaceComposer::eLayerHidden, - ISurfaceComposer::eLayerHidden); +status_t SurfaceComposerClient::setMatrix(SurfaceID id, float dsdx, float dtdx, + float dsdy, float dtdy) { + return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy); } -status_t SurfaceComposerClient::show(SurfaceID id, int32_t) +// ---------------------------------------------------------------------------- + +status_t SurfaceComposerClient::getDisplayInfo( + DisplayID dpy, DisplayInfo* info) { - return setFlags(id, 0, ISurfaceComposer::eLayerHidden); + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + + info->w = dcblk->w; + info->h = dcblk->h; + info->orientation = dcblk->orientation; + info->xdpi = dcblk->xdpi; + info->ydpi = dcblk->ydpi; + info->fps = dcblk->fps; + info->density = dcblk->density; + return getPixelFormatInfo(dcblk->format, &(info->pixelFormatInfo)); } -status_t SurfaceComposerClient::freeze(SurfaceID id) +ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) { - return setFlags(id, ISurfaceComposer::eLayerFrozen, - ISurfaceComposer::eLayerFrozen); + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->w; } -status_t SurfaceComposerClient::unfreeze(SurfaceID id) +ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) { - return setFlags(id, 0, ISurfaceComposer::eLayerFrozen); + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->h; } -status_t SurfaceComposerClient::setFlags(SurfaceID id, - uint32_t flags, uint32_t mask) +ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eVisibilityChanged; - s->flags &= ~mask; - s->flags |= (flags & mask); - s->mask |= mask; - unlockLayerState(); - return NO_ERROR; + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->orientation; } -status_t SurfaceComposerClient::setTransparentRegionHint( - SurfaceID id, const Region& transparentRegion) +ssize_t SurfaceComposerClient::getNumberOfDisplays() { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eTransparentRegionChanged; - s->transparentRegion = transparentRegion; - unlockLayerState(); - return NO_ERROR; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + uint32_t connected = cblk->connected; + int n = 0; + while (connected) { + if (connected&1) n++; + connected >>= 1; + } + return n; } -status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) +// ---------------------------------------------------------------------------- + +status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eAlphaChanged; - s->alpha = alpha; - unlockLayerState(); - return NO_ERROR; + sp<ISurfaceComposer> sm(getComposerService()); + return sm->freezeDisplay(dpy, flags); } -status_t SurfaceComposerClient::setMatrix( - SurfaceID id, - float dsdx, float dtdx, - float dsdy, float dtdy ) +status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags) { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eMatrixChanged; - layer_state_t::matrix22_t matrix; - matrix.dsdx = dsdx; - matrix.dtdx = dtdx; - matrix.dsdy = dsdy; - matrix.dtdy = dtdy; - s->matrix = matrix; - unlockLayerState(); - return NO_ERROR; + sp<ISurfaceComposer> sm(getComposerService()); + return sm->unfreezeDisplay(dpy, flags); } -status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) +int SurfaceComposerClient::setOrientation(DisplayID dpy, + int orientation, uint32_t flags) { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eFreezeTintChanged; - s->tint = tint; - unlockLayerState(); - return NO_ERROR; + sp<ISurfaceComposer> sm(getComposerService()); + return sm->setOrientation(dpy, orientation, flags); } // ---------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 37e6d1187db8..0925001965dd 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -417,17 +417,22 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) { return -EINVAL; } - if (mQueue.empty()) { - listener = mFrameAvailableListener; - } - if (mSynchronousMode) { - // in synchronous mode we queue all buffers in a FIFO + // In synchronous mode we queue all buffers in a FIFO. mQueue.push_back(buf); + + // Synchronous mode always signals that an additional frame should + // be consumed. + listener = mFrameAvailableListener; } else { - // in asynchronous mode we only keep the most recent buffer + // In asynchronous mode we only keep the most recent buffer. if (mQueue.empty()) { mQueue.push_back(buf); + + // Asynchronous mode only signals that a frame should be + // consumed if no previous frame was pending. If a frame were + // pending then the consumer would have already been notified. + listener = mFrameAvailableListener; } else { Fifo::iterator front(mQueue.begin()); // buffer currently queued is freed @@ -483,24 +488,14 @@ status_t SurfaceTexture::setTransform(uint32_t transform) { status_t SurfaceTexture::updateTexImage() { LOGV("SurfaceTexture::updateTexImage"); - Mutex::Autolock lock(mMutex); - int buf = mCurrentTexture; + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. if (!mQueue.empty()) { - // in asynchronous mode the list is guaranteed to be one buffer deep, - // while in synchronous mode we use the oldest buffer Fifo::iterator front(mQueue.begin()); - buf = *front; - mQueue.erase(front); - if (mQueue.isEmpty()) { - mDequeueCondition.signal(); - } - } + int buf = *front; - // Initially both mCurrentTexture and buf are INVALID_BUFFER_SLOT, - // so this check will fail until a buffer gets queued. - if (mCurrentTexture != buf) { // Update the GL texture object. EGLImageKHR image = mSlots[buf].mEglImage; if (image == EGL_NO_IMAGE_KHR) { @@ -538,7 +533,7 @@ status_t SurfaceTexture::updateTexImage() { } if (mCurrentTexture != INVALID_BUFFER_SLOT) { - // the current buffer becomes FREE if it was still in the queued + // The current buffer becomes FREE if it was still in the queued // state. If it has already been given to the client // (synchronous mode), then it stays in DEQUEUED state. if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) @@ -553,17 +548,17 @@ status_t SurfaceTexture::updateTexImage() { mCurrentTransform = mSlots[buf].mTransform; mCurrentTimestamp = mSlots[buf].mTimestamp; computeCurrentTransformMatrix(); + + // Now that we've passed the point at which failures can happen, + // it's safe to remove the buffer from the front of the queue. + mQueue.erase(front); mDequeueCondition.signal(); } else { // We always bind the texture even if we don't update its contents. glBindTexture(mCurrentTextureTarget, mTexName); } - return OK; -} -size_t SurfaceTexture::getQueuedCount() const { - Mutex::Autolock lock(mMutex); - return mQueue.size(); + return OK; } bool SurfaceTexture::isExternalFormat(uint32_t format) diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index f219639a0627..c06400e5219a 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -84,10 +84,10 @@ protected: ASSERT_TRUE(mSurfaceControl != NULL); ASSERT_TRUE(mSurfaceControl->isValid()); - ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction()); + SurfaceComposerClient::openGlobalTransaction(); ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); - ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction()); + SurfaceComposerClient::closeGlobalTransaction(); sp<ANativeWindow> window = mSurfaceControl->getSurface(); mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, @@ -419,6 +419,31 @@ protected: ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); } + class FrameWaiter : public SurfaceTexture::FrameAvailableListener { + public: + FrameWaiter(): + mPendingFrames(0) { + } + + void waitForFrame() { + Mutex::Autolock lock(mMutex); + while (mPendingFrames == 0) { + mCondition.wait(mMutex); + } + mPendingFrames--; + } + + virtual void onFrameAvailable() { + Mutex::Autolock lock(mMutex); + mPendingFrames++; + mCondition.signal(); + } + + int mPendingFrames; + Mutex mMutex; + Condition mCondition; + }; + sp<SurfaceTexture> mST; sp<SurfaceTextureClient> mSTC; sp<ANativeWindow> mANW; @@ -648,6 +673,157 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { } } +// This test is intended to catch synchronization bugs between the CPU-written +// and GPU-read buffers. +TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { + enum { texWidth = 16 }; + enum { texHeight = 16 }; + enum { numFrames = 1024 }; + + ASSERT_EQ(NO_ERROR, mST->setSynchronousMode(true)); + ASSERT_EQ(NO_ERROR, mST->setBufferCountServer(2)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_WRITE_OFTEN)); + + struct TestPixel { + int x; + int y; + }; + const TestPixel testPixels[] = { + { 4, 11 }, + { 12, 14 }, + { 7, 2 }, + }; + enum {numTestPixels = sizeof(testPixels) / sizeof(testPixels[0])}; + + class ProducerThread : public Thread { + public: + ProducerThread(const sp<ANativeWindow>& anw, const TestPixel* testPixels): + mANW(anw), + mTestPixels(testPixels) { + } + + virtual ~ProducerThread() { + } + + virtual bool threadLoop() { + for (int i = 0; i < numFrames; i++) { + ANativeWindowBuffer* anb; + if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + if (mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()) + != NO_ERROR) { + return false; + } + + const int yuvTexOffsetY = 0; + int stride = buf->getStride(); + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * texHeight; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2; + int yuvTexStrideU = yuvTexStrideV; + + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + + // Gray out all the test pixels first, so we're more likely to + // see a failure if GL is still texturing from the buffer we + // just dequeued. + for (int j = 0; j < numTestPixels; j++) { + int x = mTestPixels[j].x; + int y = mTestPixels[j].y; + uint8_t value = 128; + img[y*stride + x] = value; + } + + // Fill the buffer with gray. + for (int y = 0; y < texHeight; y++) { + for (int x = 0; x < texWidth; x++) { + img[yuvTexOffsetY + y*yuvTexStrideY + x] = 128; + img[yuvTexOffsetU + (y/2)*yuvTexStrideU + x/2] = 128; + img[yuvTexOffsetV + (y/2)*yuvTexStrideV + x/2] = 128; + } + } + + // Set the test pixels to either white or black. + for (int j = 0; j < numTestPixels; j++) { + int x = mTestPixels[j].x; + int y = mTestPixels[j].y; + uint8_t value = 0; + if (j == (i % numTestPixels)) { + value = 255; + } + img[y*stride + x] = value; + } + + buf->unlock(); + if (mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()) + != NO_ERROR) { + return false; + } + } + return false; + } + + sp<ANativeWindow> mANW; + const TestPixel* mTestPixels; + }; + + sp<FrameWaiter> fw(new FrameWaiter); + mST->setFrameAvailableListener(fw); + + sp<Thread> pt(new ProducerThread(mANW, testPixels)); + pt->run(); + + glViewport(0, 0, texWidth, texHeight); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + // We wait for the first two frames up front so that the producer will be + // likely to dequeue the buffer that's currently being textured from. + fw->waitForFrame(); + fw->waitForFrame(); + + for (int i = 0; i < numFrames; i++) { + SCOPED_TRACE(String8::format("frame %d", i).string()); + + // We must wait for each frame to come in because if we ever do an + // updateTexImage call that doesn't consume a newly available buffer + // then the producer and consumer will get out of sync, which will cause + // a deadlock. + if (i > 1) { + fw->waitForFrame(); + } + mST->updateTexImage(); + drawTexture(); + + for (int j = 0; j < numTestPixels; j++) { + int x = testPixels[j].x; + int y = testPixels[j].y; + uint8_t value = 0; + if (j == (i % numTestPixels)) { + // We must y-invert the texture coords + EXPECT_TRUE(checkPixel(x, texHeight-y-1, 255, 255, 255, 255)); + } else { + // We must y-invert the texture coords + EXPECT_TRUE(checkPixel(x, texHeight-y-1, 0, 0, 0, 255)); + } + } + } + + pt->requestExitAndWait(); +} + // XXX: This test is disabled because there are currently no drivers that can // handle RGBA textures with the GL_TEXTURE_EXTERNAL_OES target. TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledRGBABufferNpot) { diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 35c86405584b..450cdf1f8f47 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -36,10 +36,10 @@ protected: ASSERT_TRUE(mSurfaceControl != NULL); ASSERT_TRUE(mSurfaceControl->isValid()); - ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction()); + SurfaceComposerClient::openGlobalTransaction(); ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000)); ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); - ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction()); + SurfaceComposerClient::closeGlobalTransaction(); mSurface = mSurfaceControl->getSurface(); ASSERT_TRUE(mSurface != NULL); diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index e43f6e55fb23..8b1caeeedf59 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -699,9 +699,10 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float* vertices = getFloats(verticesCount); bool hasColors = getInt(); int* colors = hasColors ? getInts(colorsCount) : NULL; + SkPaint* paint = getPaint(); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); - renderer.drawBitmapMesh(bitmap, meshWidth, meshHeight, vertices, colors, getPaint()); + renderer.drawBitmapMesh(bitmap, meshWidth, meshHeight, vertices, colors, paint); } break; case DrawPatch: { @@ -718,9 +719,15 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) yDivs = getInts(yDivsCount); colors = getUInts(numColors); + float left = getFloat(); + float top = getFloat(); + float right = getFloat(); + float bottom = getFloat(); + SkPaint* paint = getPaint(); + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); renderer.drawPatch(bitmap, xDivs, yDivs, colors, xDivsCount, yDivsCount, - numColors, getFloat(), getFloat(), getFloat(), getFloat(), getPaint()); + numColors, left, top, right, bottom, paint); } break; case DrawColor: { @@ -799,15 +806,17 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) case DrawLines: { int count = 0; float* points = getFloats(count); + SkPaint* paint = getPaint(); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); - renderer.drawLines(points, count, getPaint()); + renderer.drawLines(points, count, paint); } break; case DrawPoints: { int count = 0; float* points = getFloats(count); + SkPaint* paint = getPaint(); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); - renderer.drawPoints(points, count, getPaint()); + renderer.drawPoints(points, count, paint); } break; case DrawText: { diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index ffdfe663afe9..c46d6f4724a2 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -443,7 +443,8 @@ status_t InputPublisher::appendMotionSample( if (! mPinned || ! mMotionEventSampleDataTail) { LOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current " - "AMOTION_EVENT_ACTION_MOVE event.", mChannel->getName().string()); + "AMOTION_EVENT_ACTION_MOVE or AMOTION_EVENT_ACTION_HOVER_MOVE event.", + mChannel->getName().string()); return INVALID_OPERATION; } diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 093189c5c05a..774e8c974558 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -27,6 +27,7 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ + LinearTransform.cpp \ ObbFile.cpp \ Pool.cpp \ PropertyMap.cpp \ diff --git a/libs/utils/LinearTransform.cpp b/libs/utils/LinearTransform.cpp new file mode 100644 index 000000000000..d752415cf5f0 --- /dev/null +++ b/libs/utils/LinearTransform.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2011 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 __STDC_LIMIT_MACROS + +#include <assert.h> +#include <stdint.h> + +#include <utils/LinearTransform.h> + +namespace android { + +template<class T> static inline T ABS(T x) { return (x < 0) ? -x : x; } + +// Static math methods involving linear transformations +static bool scale_u64_to_u64( + uint64_t val, + uint32_t N, + uint32_t D, + uint64_t* res, + bool round_up_not_down) { + uint64_t tmp1, tmp2; + uint32_t r; + + assert(res); + assert(D); + + // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit + // integer X. + // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit + // integer X. + // Let X[A, B] with A <= B denote bits A through B of the integer X. + // Let (A | B) denote the concatination of two 32 bit ints, A and B. + // IOW X = (A | B) => U32(X) == A && L32(X) == B + // + // compute M = val * N (a 96 bit int) + // --------------------------------- + // tmp2 = U32(val) * N (a 64 bit int) + // tmp1 = L32(val) * N (a 64 bit int) + // which means + // M = val * N = (tmp2 << 32) + tmp1 + tmp2 = (val >> 32) * N; + tmp1 = (val & UINT32_MAX) * N; + + // compute M[32, 95] + // tmp2 = tmp2 + U32(tmp1) + // = (U32(val) * N) + U32(L32(val) * N) + // = M[32, 95] + tmp2 += tmp1 >> 32; + + // if M[64, 95] >= D, then M/D has bits > 63 set and we have + // an overflow. + if ((tmp2 >> 32) >= D) { + *res = UINT64_MAX; + return false; + } + + // Divide. Going in we know + // tmp2 = M[32, 95] + // U32(tmp2) < D + r = tmp2 % D; + tmp2 /= D; + + // At this point + // tmp1 = L32(val) * N + // tmp2 = M[32, 95] / D + // = (M / D)[32, 95] + // r = M[32, 95] % D + // U32(tmp2) = 0 + // + // compute tmp1 = (r | M[0, 31]) + tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32); + + // Divide again. Keep the remainder around in order to round properly. + r = tmp1 % D; + tmp1 /= D; + + // At this point + // tmp2 = (M / D)[32, 95] + // tmp1 = (M / D)[ 0, 31] + // r = M % D + // U32(tmp1) = 0 + // U32(tmp2) = 0 + + // Pack the result and deal with the round-up case (As well as the + // remote possiblility over overflow in such a case). + *res = (tmp2 << 32) | tmp1; + if (r && round_up_not_down) { + ++(*res); + if (!(*res)) { + *res = UINT64_MAX; + return false; + } + } + + return true; +} + +static bool linear_transform_s64_to_s64( + int64_t val, + int64_t basis1, + int32_t N, + uint32_t D, + int64_t basis2, + int64_t* out) { + uint64_t scaled, res; + uint64_t abs_val; + bool is_neg; + + if (!out) + return false; + + // Compute abs(val - basis_64). Keep track of whether or not this delta + // will be negative after the scale opertaion. + if (val < basis1) { + is_neg = true; + abs_val = basis1 - val; + } else { + is_neg = false; + abs_val = val - basis1; + } + + if (N < 0) + is_neg = !is_neg; + + if (!scale_u64_to_u64(abs_val, + ABS(N), + D, + &scaled, + is_neg)) + return false; // overflow/undeflow + + // if scaled is >= 0x8000<etc>, then we are going to overflow or + // underflow unless ABS(basis2) is large enough to pull us back into the + // non-overflow/underflow region. + if (scaled & INT64_MIN) { + if (is_neg && (basis2 < 0)) + return false; // certain underflow + + if (!is_neg && (basis2 >= 0)) + return false; // certain overflow + + if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX)) + return false; // not enough + + // Looks like we are OK + *out = (is_neg ? (-scaled) : scaled) + basis2; + } else { + // Scaled fits within signed bounds, so we just need to check for + // over/underflow for two signed integers. Basically, if both scaled + // and basis2 have the same sign bit, and the result has a different + // sign bit, then we have under/overflow. An easy way to compute this + // is + // (scaled_signbit XNOR basis_signbit) && + // (scaled_signbit XOR res_signbit) + // == + // (scaled_signbit XOR basis_signbit XOR 1) && + // (scaled_signbit XOR res_signbit) + + if (is_neg) + scaled = -scaled; + res = scaled + basis2; + + if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN) + return false; + + *out = res; + } + + return true; +} + +bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const { + if (0 == a_to_b_denom) + return false; + + return linear_transform_s64_to_s64(a_in, + a_zero, + a_to_b_numer, + a_to_b_denom, + b_zero, + b_out); +} + +bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const { + if (0 == a_to_b_numer) + return false; + + return linear_transform_s64_to_s64(b_in, + b_zero, + a_to_b_denom, + a_to_b_numer, + a_zero, + a_out); +} + +template <class T> void LinearTransform::reduce(T* N, T* D) { + T a, b; + if (!N || !D || !(*D)) { + assert(false); + return; + } + + a = *N; + b = *D; + + if (a == 0) { + *D = 1; + return; + } + + // This implements Euclid's method to find GCD. + if (a < b) { + T tmp = a; + a = b; + b = tmp; + } + + while (1) { + // a is now the greater of the two. + const T remainder = a % b; + if (remainder == 0) { + *N /= b; + *D /= b; + return; + } + // by swapping remainder and b, we are guaranteeing that a is + // still the greater of the two upon entrance to the loop. + a = b; + b = remainder; + } +}; + +template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D); +template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D); + +void LinearTransform::reduce(int32_t* N, uint32_t* D) { + if (N && D && *D) { + if (*N < 0) { + *N = -(*N); + reduce(reinterpret_cast<uint32_t*>(N), D); + *N = -(*N); + } else { + reduce(reinterpret_cast<uint32_t*>(N), D); + } + } +} + +} // namespace android diff --git a/location/java/android/location/Country.java b/location/java/android/location/Country.java index 3c05403a6bc2..939bd4af109c 100755 --- a/location/java/android/location/Country.java +++ b/location/java/android/location/Country.java @@ -19,6 +19,8 @@ package android.location; import android.os.Parcel; import android.os.Parcelable; +import java.util.Locale; + /** * This class wraps the country information. * @@ -74,7 +76,7 @@ public class Country implements Parcelable { || source > COUNTRY_SOURCE_LOCALE) { throw new IllegalArgumentException(); } - mCountryIso = countryIso.toLowerCase(); + mCountryIso = countryIso.toUpperCase(Locale.US); mSource = source; } diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 6f425966d592..e3cbd5783c8f 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -720,8 +720,9 @@ public class MediaRecorder * * <p>Since API level 13, if applications set a camera via * {@link #setCamera(Camera)}, the apps can use the camera after this method - * call. The apps do not need to lock the camera again. The apps should not - * start another recording session during recording. + * call. The apps do not need to lock the camera again. However, if this + * method fails, the apps should still lock the camera back. The apps should + * not start another recording session during recording. * * @throws IllegalStateException if it is called before * prepare(). diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java index 29c4b89e8517..0d2bcd5376e5 100644 --- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java +++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java @@ -957,12 +957,8 @@ class MediaArtistNativeHelper { public static final int FADE_FROM_BLACK = 8; - public static final int CURTAIN_OPENING = 9; - public static final int FADE_TO_BLACK = 16; - public static final int CURTAIN_CLOSING = 17; - public static final int EXTERNAL = 256; public static final int BLACK_AND_WHITE = 257; diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp index d43a5622a769..277e16c4dc3b 100755 --- a/media/jni/mediaeditor/VideoEditorClasses.cpp +++ b/media/jni/mediaeditor/VideoEditorClasses.cpp @@ -378,9 +378,7 @@ VIDEOEDIT_JAVA_DEFINE_CONSTANTS(VideoEffect) { VIDEOEDIT_JAVA_CONSTANT_INIT("NONE", M4VSS3GPP_kVideoEffectType_None), VIDEOEDIT_JAVA_CONSTANT_INIT("FADE_FROM_BLACK", M4VSS3GPP_kVideoEffectType_FadeFromBlack), - VIDEOEDIT_JAVA_CONSTANT_INIT("CURTAIN_OPENING", M4VSS3GPP_kVideoEffectType_CurtainOpening), VIDEOEDIT_JAVA_CONSTANT_INIT("FADE_TO_BLACK", M4VSS3GPP_kVideoEffectType_FadeToBlack), - VIDEOEDIT_JAVA_CONSTANT_INIT("CURTAIN_CLOSING", M4VSS3GPP_kVideoEffectType_CurtainClosing), VIDEOEDIT_JAVA_CONSTANT_INIT("EXTERNAL", M4VSS3GPP_kVideoEffectType_External), VIDEOEDIT_JAVA_CONSTANT_INIT("BLACK_AND_WHITE", M4xVSS_kVideoEffectType_BlackAndWhite), VIDEOEDIT_JAVA_CONSTANT_INIT("PINK", M4xVSS_kVideoEffectType_Pink), diff --git a/media/jni/mediaeditor/VideoEditorOsal.cpp b/media/jni/mediaeditor/VideoEditorOsal.cpp index 53e7de176ae4..a8c08ac936bd 100755 --- a/media/jni/mediaeditor/VideoEditorOsal.cpp +++ b/media/jni/mediaeditor/VideoEditorOsal.cpp @@ -166,7 +166,6 @@ static const VideoEdit_Osal_Result gkRESULTS[] = VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_NO_SUPPORTED_VIDEO_STREAM_IN_FILE ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_INTERNAL_STATE ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_LUMA_FILTER_ERROR ), - VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_CURTAIN_FILTER_ERROR ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_TRANSITION_FILTER_ERROR ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_AUDIO_DECODER_INIT_FAILED ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_AUDIO_DECODED_PCM_SIZE_ISSUE ), diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index d628301b5b71..513eda82f868 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -29,6 +29,7 @@ #include <media/stagefright/MediaDefs.h> #include <media/stagefright/NativeWindowWrapper.h> #include <media/stagefright/OMXClient.h> +#include <media/stagefright/OMXCodec.h> #include <surfaceflinger/Surface.h> #include <gui/SurfaceTextureClient.h> @@ -401,11 +402,22 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { CHECK(mem.get() != NULL); IOMX::buffer_id buffer; -#if 0 - err = mOMX->allocateBufferWithBackup(mNode, portIndex, mem, &buffer); -#else - err = mOMX->useBuffer(mNode, portIndex, mem, &buffer); -#endif + + if (!strcasecmp( + mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.DECODER")) { + if (portIndex == kPortIndexInput && i == 0) { + // Only log this warning once per allocation round. + + LOGW("OMX.TI.DUCATI1.VIDEO.DECODER requires the use of " + "OMX_AllocateBuffer instead of the preferred " + "OMX_UseBuffer. Vendor must fix this."); + } + + err = mOMX->allocateBufferWithBackup( + mNode, portIndex, mem, &buffer); + } else { + err = mOMX->useBuffer(mNode, portIndex, mem, &buffer); + } if (err != OK) { return err; @@ -891,6 +903,7 @@ status_t ACodec::setSupportedOutputFormat() { CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || format.eColorFormat == OMX_COLOR_FormatCbYCrY + || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar); return mOMX->setParameter( @@ -1639,27 +1652,33 @@ void ACodec::UninitializedState::onSetup( AString mime; CHECK(msg->findString("mime", &mime)); + Vector<String8> matchingCodecs; + OMXCodec::findMatchingCodecs( + mime.c_str(), + false, // createEncoder + NULL, // matchComponentName + 0, // flags + &matchingCodecs); + + sp<CodecObserver> observer = new CodecObserver; + IOMX::node_id node = NULL; + AString componentName; - if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) { - componentName = "OMX.Nvidia.h264.decode"; - } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) { - componentName = "OMX.google.aac.decoder"; - } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) { - componentName = "OMX.Nvidia.mp3.decoder"; - } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG2)) { - componentName = "OMX.Nvidia.mpeg2v.decode"; - } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG4)) { - componentName = "OMX.google.mpeg4.decoder"; - } else { - TRESPASS(); - } + for (size_t matchIndex = 0; matchIndex < matchingCodecs.size(); + ++matchIndex) { + componentName = matchingCodecs.itemAt(matchIndex).string(); - sp<CodecObserver> observer = new CodecObserver; + status_t err = omx->allocateNode(componentName.c_str(), observer, &node); - IOMX::node_id node; - CHECK_EQ(omx->allocateNode(componentName.c_str(), observer, &node), - (status_t)OK); + if (err == OK) { + break; + } + + node = NULL; + } + + CHECK(node != NULL); sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id()); observer->setNotificationMessage(notify); diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index bdffce9c10fe..ed8149abbbd3 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -158,9 +158,12 @@ CameraSource::CameraSource( mVideoSize.width = -1; mVideoSize.height = -1; + int64_t token = IPCThreadState::self()->clearCallingIdentity(); mInitCheck = init(camera, proxy, cameraId, videoSize, frameRate, storeMetaDataInVideoBuffers); + if (mInitCheck != OK) releaseCamera(); + IPCThreadState::self()->restoreCallingIdentity(token); } status_t CameraSource::initCheck() const { @@ -295,7 +298,6 @@ status_t CameraSource::configureCamera( if (width != -1 && height != -1) { if (!isVideoSizeSupported(width, height, sizes)) { LOGE("Video dimension (%dx%d) is unsupported", width, height); - releaseCamera(); return BAD_VALUE; } if (isSetVideoSizeSupportedByCamera) { @@ -309,7 +311,6 @@ status_t CameraSource::configureCamera( // If one and only one of the width and height is -1 // we reject such a request. LOGE("Requested video size (%dx%d) is not supported", width, height); - releaseCamera(); return BAD_VALUE; } else { // width == -1 && height == -1 // Do not configure the camera. @@ -327,7 +328,6 @@ status_t CameraSource::configureCamera( if (strstr(supportedFrameRates, buf) == NULL) { LOGE("Requested frame rate (%d) is not supported: %s", frameRate, supportedFrameRates); - releaseCamera(); return BAD_VALUE; } @@ -463,7 +463,6 @@ status_t CameraSource::init( bool storeMetaDataInVideoBuffers) { status_t err = OK; - int64_t token = IPCThreadState::self()->clearCallingIdentity(); if ((err = isCameraAvailable(camera, proxy, cameraId)) != OK) { LOGE("Camera connection could not be established."); @@ -505,8 +504,6 @@ status_t CameraSource::init( } } - IPCThreadState::self()->restoreCallingIdentity(token); - int64_t glitchDurationUs = (1000000LL / mVideoFrameRate); if (glitchDurationUs > mGlitchDurationThresholdUs) { mGlitchDurationThresholdUs = glitchDurationUs; @@ -573,13 +570,21 @@ void CameraSource::stopCameraRecording() { void CameraSource::releaseCamera() { LOGV("releaseCamera"); - if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) { - LOGV("Camera was cold when we started, stopping preview"); - mCamera->stopPreview(); + if (mCamera != 0) { + if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) { + LOGV("Camera was cold when we started, stopping preview"); + mCamera->stopPreview(); + mCamera->disconnect(); + } else { + // Unlock the camera so the application can lock it back. + mCamera->unlock(); + } + mCamera.clear(); + } + if (mCameraRecordingProxy != 0) { + mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier); + mCameraRecordingProxy.clear(); } - mCamera.clear(); - mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier); - mCameraRecordingProxy.clear(); mCameraFlags = 0; } diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp index 4e4f2898b622..02eeb40030c4 100644 --- a/media/libstagefright/MPEG2TSWriter.cpp +++ b/media/libstagefright/MPEG2TSWriter.cpp @@ -466,6 +466,8 @@ bool MPEG2TSWriter::SourceInfo::eosReceived() const { MPEG2TSWriter::MPEG2TSWriter(int fd) : mFile(fdopen(dup(fd), "wb")), + mWriteCookie(NULL), + mWriteFunc(NULL), mStarted(false), mNumSourcesDone(0), mNumTSPacketsWritten(0), @@ -475,6 +477,21 @@ MPEG2TSWriter::MPEG2TSWriter(int fd) MPEG2TSWriter::MPEG2TSWriter(const char *filename) : mFile(fopen(filename, "wb")), + mWriteCookie(NULL), + mWriteFunc(NULL), + mStarted(false), + mNumSourcesDone(0), + mNumTSPacketsWritten(0), + mNumTSPacketsBeforeMeta(0) { + init(); +} + +MPEG2TSWriter::MPEG2TSWriter( + void *cookie, + ssize_t (*write)(void *cookie, const void *data, size_t size)) + : mFile(NULL), + mWriteCookie(cookie), + mWriteFunc(write), mStarted(false), mNumSourcesDone(0), mNumTSPacketsWritten(0), @@ -483,7 +500,7 @@ MPEG2TSWriter::MPEG2TSWriter(const char *filename) } void MPEG2TSWriter::init() { - CHECK(mFile != NULL); + CHECK(mFile != NULL || mWriteFunc != NULL); mLooper = new ALooper; mLooper->setName("MPEG2TSWriter"); @@ -502,8 +519,10 @@ MPEG2TSWriter::~MPEG2TSWriter() { mLooper->unregisterHandler(mReflector->id()); mLooper->stop(); - fclose(mFile); - mFile = NULL; + if (mFile != NULL) { + fclose(mFile); + mFile = NULL; + } } status_t MPEG2TSWriter::addSource(const sp<MediaSource> &source) { @@ -718,7 +737,7 @@ void MPEG2TSWriter::writeProgramAssociationTable() { static const unsigned kContinuityCounter = 5; buffer->data()[3] |= kContinuityCounter; - CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size()); + CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size()); } void MPEG2TSWriter::writeProgramMap() { @@ -794,7 +813,7 @@ void MPEG2TSWriter::writeProgramMap() { *ptr++ = 0x00; *ptr++ = 0x00; - CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size()); + CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size()); } void MPEG2TSWriter::writeAccessUnit( @@ -890,7 +909,7 @@ void MPEG2TSWriter::writeAccessUnit( memcpy(ptr, accessUnit->data(), copy); - CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size()); + CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size()); size_t offset = copy; while (offset < accessUnit->size()) { @@ -923,7 +942,7 @@ void MPEG2TSWriter::writeAccessUnit( } memcpy(ptr, accessUnit->data() + offset, copy); - CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), + CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size()); offset += copy; @@ -939,5 +958,13 @@ void MPEG2TSWriter::writeTS() { } } +ssize_t MPEG2TSWriter::internalWrite(const void *data, size_t size) { + if (mFile != NULL) { + return fwrite(data, 1, size, mFile); + } + + return (*mWriteFunc)(mWriteCookie, data, size); +} + } // namespace android diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index cd97302c290b..e36b01fbd7a8 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1838,7 +1838,7 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { } } - LOGV("native_window_set_usage usage=0x%x", usage); + LOGV("native_window_set_usage usage=0x%lx", usage); err = native_window_set_usage( mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP); if (err != 0) { diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp index ed6846c4e406..967f126c8adf 100644 --- a/media/libstagefright/chromium_http/support.cpp +++ b/media/libstagefright/chromium_http/support.cpp @@ -41,6 +41,7 @@ namespace android { static Mutex gNetworkThreadLock; static base::Thread *gNetworkThread = NULL; static scoped_refptr<net::URLRequestContext> gReqContext; +static scoped_ptr<net::NetworkChangeNotifier> gNetworkChangeNotifier; static void InitializeNetworkThreadIfNecessary() { Mutex::Autolock autoLock(gNetworkThreadLock); @@ -52,6 +53,8 @@ static void InitializeNetworkThreadIfNecessary() { gReqContext = new SfRequestContext; + gNetworkChangeNotifier.reset(net::NetworkChangeNotifier::Create()); + net::AndroidNetworkLibrary::RegisterSharedInstance( new SfNetworkLibrary); } diff --git a/native/include/android/input.h b/native/include/android/input.h index 26cac50cb169..0d8ea2841ea5 100644 --- a/native/include/android/input.h +++ b/native/include/android/input.h @@ -404,7 +404,6 @@ enum { AMOTION_EVENT_BUTTON_TERTIARY = 1 << 2, AMOTION_EVENT_BUTTON_BACK = 1 << 3, AMOTION_EVENT_BUTTON_FORWARD = 1 << 4, - AMOTION_EVENT_BUTTON_ERASER = 1 << 5, }; /* diff --git a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java index eb2f6f859191..63c2de200224 100644 --- a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java +++ b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java @@ -55,6 +55,64 @@ public class NfcExecutionEnvironment { */ public static final String EXTRA_AID = "com.android.nfc_extras.extra.AID"; + /** + * Broadcast action: A filtered APDU was received. + * + * <p>This happens when an APDU of interest was matched by the Nfc adapter, + * for instance as the result of matching an externally-configured filter. + * + * <p>The filter configuration mechanism is not currently defined. + * + * <p>Always contains the extra field {@link EXTRA_APDU_BYTES}. + * + * @hide + */ + public static final String ACTION_APDU_RECEIVED = + "com.android.nfc_extras.action.APDU_RECEIVED"; + + /** + * Mandatory byte array extra field in {@link #ACTION_APDU_RECEIVED}. + * + * <p>Contains the bytes of the received APDU. + * + * @hide + */ + public static final String EXTRA_APDU_BYTES = + "com.android.nfc_extras.extra.APDU_BYTES"; + + /** + * Broadcast action: An EMV card removal event was detected. + * + * @hide + */ + public static final String ACTION_EMV_CARD_REMOVAL = + "com.android.nfc_extras.action.EMV_CARD_REMOVAL"; + + /** + * Broadcast action: An adapter implementing MIFARE Classic via card + * emulation detected that a block has been accessed. + * + * <p>This may only be issued for the first block that the reader + * authenticates to. + * + * <p>May contain the extra field {@link #EXTRA_MIFARE_BLOCK}. + * + * @hide + */ + public static final String ACTION_MIFARE_ACCESS_DETECTED = + "com.android.nfc_extras.action.MIFARE_ACCESS_DETECTED"; + + /** + * Optional integer extra field in {@link #ACTION_MIFARE_ACCESS_DETECTED}. + * + * <p>Provides the block number being accessed. If not set, the block + * number being accessed is unknown. + * + * @hide + */ + public static final String EXTRA_MIFARE_BLOCK = + "com.android.nfc_extras.extra.MIFARE_BLOCK"; + NfcExecutionEnvironment(NfcAdapterExtras extras) { mExtras = extras; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index a9aa31b61284..946960122565 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -531,7 +531,12 @@ public class SettingsBackupAgent extends BackupAgentHelper { private void restoreFileData(String filename, BackupDataInput data) { byte[] bytes = new byte[data.getDataSize()]; if (bytes.length <= 0) return; - restoreFileData(filename, bytes, bytes.length); + try { + data.readEntityData(bytes, 0, data.getDataSize()); + restoreFileData(filename, bytes, bytes.length); + } catch (IOException e) { + Log.w(TAG, "Unable to read file data for " + filename); + } } private void restoreFileData(String filename, byte[] bytes, int size) { diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index bc2f7ee3ae44..51e7d97ef1ec 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -25,14 +25,13 @@ android:layout_width="match_parent" > - <FrameLayout - android:id="@+id/background" + <FrameLayout android:id="@+id/rot0" android:layout_height="match_parent" android:layout_width="match_parent" android:background="#FF000000" > - <LinearLayout android:id="@+id/rot0" + <LinearLayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="horizontal" @@ -40,12 +39,12 @@ <!-- navigation controls --> <View - android:layout_width="32dp" + android:layout_width="40dp" android:layout_height="match_parent" android:layout_weight="0" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back" - android:layout_width="54dp" + android:layout_width="80dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_back_default" systemui:keyCode="4" @@ -57,7 +56,7 @@ android:layout_weight="1" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home" - android:layout_width="54dp" + android:layout_width="80dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_home_default" systemui:keyCode="3" @@ -69,13 +68,13 @@ android:layout_weight="1" /> <ImageView android:id="@+id/recent_apps" - android:layout_width="54dp" + android:layout_width="80dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_recent_default" android:layout_weight="0" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu" - android:layout_width="32dp" + android:layout_width="40dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_menu_default" systemui:keyCode="82" @@ -84,17 +83,31 @@ /> </LinearLayout> - <LinearLayout android:id="@+id/rot90" + <View android:id="@+id/deadzone" + android:layout_height="@dimen/navigation_bar_deadzone_size" + android:layout_width="match_parent" + android:layout_gravity="top" + android:clickable="true" + /> + </FrameLayout> + + <FrameLayout android:id="@+id/rot90" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:background="#FF000000" + android:visibility="gone" + android:paddingTop="24dp" + > + + <LinearLayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" - android:visibility="gone" - android:paddingTop="24dp" > <!-- navigation controls --> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu" - android:layout_height="32dp" + android:layout_height="40dp" android:layout_width="match_parent" android:src="@drawable/ic_sysbar_menu_default_land" systemui:keyCode="82" @@ -102,7 +115,7 @@ android:visibility="invisible" /> <ImageView android:id="@+id/recent_apps" - android:layout_height="54dp" + android:layout_height="80dp" android:layout_width="match_parent" android:src="@drawable/ic_sysbar_recent_default_land" android:layout_weight="0" @@ -113,7 +126,7 @@ android:layout_weight="1" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home" - android:layout_height="54dp" + android:layout_height="80dp" android:layout_width="match_parent" android:src="@drawable/ic_sysbar_home_default_land" systemui:keyCode="3" @@ -125,28 +138,32 @@ android:layout_weight="1" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back" - android:layout_height="54dp" + android:layout_height="80dp" android:layout_width="match_parent" android:src="@drawable/ic_sysbar_back_default_land" systemui:keyCode="4" android:layout_weight="0" /> <View - android:layout_height="32dp" + android:layout_height="40dp" android:layout_width="match_parent" android:layout_weight="0" /> </LinearLayout> - <LinearLayout android:id="@+id/rot270" + <View android:id="@+id/deadzone" + android:layout_width="@dimen/navigation_bar_deadzone_size" android:layout_height="match_parent" - android:layout_width="match_parent" - android:orientation="vertical" - android:visibility="gone" - > - - <!-- not used --> - </LinearLayout> - + android:layout_gravity="left" + android:clickable="true" + /> </FrameLayout> + + <!-- not used --> + <View android:id="@+id/rot270" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:visibility="gone" + /> + </com.android.systemui.statusbar.phone.NavigationBarView> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 3944c203cc3d..da28e1eac25d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -40,7 +40,11 @@ <dimen name="peek_window_y_offset">-12dp</dimen> <!-- thickness (height) of the navigation bar on phones that require it --> - <dimen name="navigation_bar_size">32dp</dimen> + <dimen name="navigation_bar_size">48dp</dimen> + + <!-- thickness (height) of the dead zone at the top of the navigation bar, + reducing false presses on navbar buttons; approx 2mm --> + <dimen name="navigation_bar_deadzone_size">12dp</dimen> <!-- thickness (height) of each notification row, including any separators or padding --> <dimen name="notification_height">65dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 22181b8337ae..550fc57e9a78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -36,13 +36,14 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; public class NavigationBarView extends LinearLayout { + final static boolean DEBUG_DEADZONE = false; + final static boolean NAVBAR_ALWAYS_AT_RIGHT = true; protected IStatusBarService mBarService; final Display mDisplay; View mCurrentView = null; View[] mRotatedViews = new View[4]; - View mBackground; Animator mLastAnimator = null; public View getRecentsButton() { @@ -74,13 +75,13 @@ public class NavigationBarView extends LinearLayout { } private void setLights(final boolean on) { - float oldAlpha = mBackground.getAlpha(); + float oldAlpha = mCurrentView.getAlpha(); android.util.Log.d("NavigationBarView", "animating alpha: " + oldAlpha + " -> " + (on ? 1f : 0f)); if (mLastAnimator != null && mLastAnimator.isRunning()) mLastAnimator.cancel(); - mLastAnimator = ObjectAnimator.ofFloat(mBackground, "alpha", oldAlpha, on ? 1f : 0f) + mLastAnimator = ObjectAnimator.ofFloat(mCurrentView, "alpha", oldAlpha, on ? 1f : 0f) .setDuration(on ? 250 : 1500); mLastAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -92,8 +93,6 @@ public class NavigationBarView extends LinearLayout { } public void onFinishInflate() { - mBackground = findViewById(R.id.background); - mRotatedViews[Surface.ROTATION_0] = mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0); @@ -121,6 +120,10 @@ public class NavigationBarView extends LinearLayout { mCurrentView = mRotatedViews[rot]; mCurrentView.setVisibility(View.VISIBLE); + if (DEBUG_DEADZONE) { + mCurrentView.findViewById(R.id.deadzone).setBackgroundColor(0x808080FF); + } + android.util.Log.d("NavigationBarView", "reorient(): rot=" + mDisplay.getRotation()); } } diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index eea30407af55..b60bae7d49eb 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -682,8 +682,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase { final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; - boolean showSlidingTab = getResources().getBoolean(R.bool.config_enableSlidingTabFirst); - if (isSecure() && (usingLockPattern || !showSlidingTab)) { + boolean showLockBeforeUnlock = getResources() + .getBoolean(R.bool.config_enableLockBeforeUnlockScreen); + if (isSecure() && (usingLockPattern || !showLockBeforeUnlock)) { return Mode.UnlockScreen; } else { return Mode.LockScreen; diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 4a50d8a7eda8..85ce38ae2020 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -569,9 +569,9 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR break; case DROP_REASON_BLOCKED: LOGI("Dropped event because the current application is not responding and the user " - "has started interating with a different application."); + "has started interacting with a different application."); reason = "inbound event was dropped because the current application is not responding " - "and the user has started interating with a different application"; + "and the user has started interacting with a different application"; break; case DROP_REASON_STALE: LOGI("Dropped event because it is stale."); @@ -1239,37 +1239,35 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const InputWindow* newHoverWindow = NULL; bool isSplit = mTouchState.split; - bool wrongDevice = mTouchState.down - && (mTouchState.deviceId != entry->deviceId - || mTouchState.source != entry->source); + bool switchedDevice = mTouchState.deviceId != entry->deviceId + || mTouchState.source != entry->source; bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); + bool wrongDevice = false; if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; - if (wrongDevice && !down) { + if (switchedDevice && mTouchState.down && !down) { +#if DEBUG_FOCUS + LOGD("Dropping event because a pointer for a different device is already down."); +#endif mTempTouchState.copyFrom(mTouchState); - } else { - mTempTouchState.reset(); - mTempTouchState.down = down; - mTempTouchState.deviceId = entry->deviceId; - mTempTouchState.source = entry->source; - isSplit = false; - wrongDevice = false; + injectionResult = INPUT_EVENT_INJECTION_FAILED; + switchedDevice = false; + wrongDevice = true; + goto Failed; } + mTempTouchState.reset(); + mTempTouchState.down = down; + mTempTouchState.deviceId = entry->deviceId; + mTempTouchState.source = entry->source; + isSplit = false; } else { mTempTouchState.copyFrom(mTouchState); } - if (wrongDevice) { -#if DEBUG_FOCUS - LOGD("Dropping event because a pointer for a different device is already down."); -#endif - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ @@ -1598,18 +1596,38 @@ Failed: // Update final pieces of touch state if the injector had permission. if (injectionPermission == INJECTION_PERMISSION_GRANTED) { if (!wrongDevice) { - if (maskedAction == AMOTION_EVENT_ACTION_UP - || maskedAction == AMOTION_EVENT_ACTION_CANCEL - || isHoverAction) { + if (switchedDevice) { +#if DEBUG_FOCUS + LOGD("Conflicting pointer actions: Switched to a different device."); +#endif + *outConflictingPointerActions = true; + } + + if (isHoverAction) { + // Started hovering, therefore no longer down. + if (mTouchState.down) { +#if DEBUG_FOCUS + LOGD("Conflicting pointer actions: Hover received while pointer was down."); +#endif + *outConflictingPointerActions = true; + } + mTouchState.reset(); + if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER + || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { + mTouchState.deviceId = entry->deviceId; + mTouchState.source = entry->source; + } + } else if (maskedAction == AMOTION_EVENT_ACTION_UP + || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { // All pointers up or canceled. mTouchState.reset(); } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. if (mTouchState.down) { - *outConflictingPointerActions = true; #if DEBUG_FOCUS - LOGD("Pointer down received while already down."); + LOGD("Conflicting pointer actions: Down received while already down."); #endif + *outConflictingPointerActions = true; } mTouchState.copyFrom(mTempTouchState); } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { @@ -1868,6 +1886,18 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, return; } + // If the motion event was modified in flight, then we cannot stream the sample. + if ((motionEventDispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_MASK) + != InputTarget::FLAG_DISPATCH_AS_IS) { +#if DEBUG_BATCHING + LOGD("channel '%s' ~ Not streaming because the motion event was not " + "being dispatched as-is. " + "(Waiting for next dispatch cycle to start.)", + connection->getInputChannelName()); +#endif + return; + } + // The dispatch entry is in progress and is still potentially open for streaming. // Try to stream the new motion sample. This might fail if the consumer has already // consumed the motion event (or if the channel is broken). @@ -1972,6 +2002,66 @@ void InputDispatcher::enqueueDispatchEntryLocked( dispatchEntry->headMotionSample = appendedMotionSample; } + // Apply target flags and update the connection's input state. + switch (eventEntry->type) { + case EventEntry::TYPE_KEY: { + KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); + dispatchEntry->resolvedAction = keyEntry->action; + dispatchEntry->resolvedFlags = keyEntry->flags; + + if (!connection->inputState.trackKey(keyEntry, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", + connection->getInputChannelName()); +#endif + return; // skip the inconsistent event + } + break; + } + + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); + if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; + } else { + dispatchEntry->resolvedAction = motionEntry->action; + } + if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE + && !connection->inputState.isHovering( + motionEntry->deviceId, motionEntry->source)) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event", + connection->getInputChannelName()); +#endif + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; + } + + dispatchEntry->resolvedFlags = motionEntry->flags; + if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { + dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + } + + if (!connection->inputState.trackMotion(motionEntry, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event", + connection->getInputChannelName()); +#endif + return; // skip the inconsistent event + } + break; + } + } + // Enqueue the dispatch entry. connection->outboundQueue.enqueueAtTail(dispatchEntry); } @@ -1999,16 +2089,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::TYPE_KEY: { KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); - // Apply target flags. - int32_t action = keyEntry->action; - int32_t flags = keyEntry->flags; - - // Update the connection's input state. - connection->inputState.trackKey(keyEntry, action); - // Publish the key event. - status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source, - action, flags, keyEntry->keyCode, keyEntry->scanCode, + status = connection->inputPublisher.publishKeyEvent( + keyEntry->deviceId, keyEntry->source, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, + keyEntry->keyCode, keyEntry->scanCode, keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, keyEntry->eventTime); @@ -2024,24 +2109,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::TYPE_MOTION: { MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); - // Apply target flags. - int32_t action = motionEntry->action; - int32_t flags = motionEntry->flags; - if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { - action = AMOTION_EVENT_ACTION_OUTSIDE; - } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { - action = AMOTION_EVENT_ACTION_HOVER_EXIT; - } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { - action = AMOTION_EVENT_ACTION_HOVER_ENTER; - } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { - action = AMOTION_EVENT_ACTION_CANCEL; - } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { - action = AMOTION_EVENT_ACTION_DOWN; - } - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { - flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - } - // If headMotionSample is non-NULL, then it points to the first new sample that we // were unable to dispatch during the previous cycle so we resume dispatching from // that point in the list of motion samples. @@ -2082,13 +2149,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } } - // Update the connection's input state. - connection->inputState.trackMotion(motionEntry, action); - // Publish the motion event and the first motion sample. - status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId, - motionEntry->source, action, flags, motionEntry->edgeFlags, - motionEntry->metaState, motionEntry->buttonState, + status = connection->inputPublisher.publishMotionEvent( + motionEntry->deviceId, motionEntry->source, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, + motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->downTime, firstMotionSample->eventTime, @@ -2102,8 +2167,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, return; } - if (action == AMOTION_EVENT_ACTION_MOVE - || action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_MOVE + || dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { // Append additional motion samples. MotionSample* nextMotionSample = firstMotionSample->next; for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) { @@ -2355,23 +2420,22 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( break; } - int32_t xOffset, yOffset; - float scaleFactor; + InputTarget target; const InputWindow* window = getWindowLocked(connection->inputChannel); if (window) { - xOffset = -window->frameLeft; - yOffset = -window->frameTop; - scaleFactor = window->scaleFactor; + target.xOffset = -window->frameLeft; + target.yOffset = -window->frameTop; + target.scaleFactor = window->scaleFactor; } else { - xOffset = 0; - yOffset = 0; - scaleFactor = 1.0f; + target.xOffset = 0; + target.yOffset = 0; + target.scaleFactor = 1.0f; } + target.inputChannel = connection->inputChannel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - DispatchEntry* cancelationDispatchEntry = - mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref - 0, xOffset, yOffset, scaleFactor); - connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry); + enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref + &target, false, InputTarget::FLAG_DISPATCH_AS_IS); mAllocator.releaseEventEntry(cancelationEventEntry); } @@ -3327,6 +3391,7 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { resetTargetsLocked(); mTouchState.reset(); + mLastHoverWindow = NULL; } void InputDispatcher::logDispatchStateLocked() { @@ -4125,113 +4190,188 @@ bool InputDispatcher::InputState::isNeutral() const { return mKeyMementos.isEmpty() && mMotionMementos.isEmpty(); } -void InputDispatcher::InputState::trackEvent(const EventEntry* entry, int32_t action) { - switch (entry->type) { - case EventEntry::TYPE_KEY: - trackKey(static_cast<const KeyEntry*>(entry), action); - break; - - case EventEntry::TYPE_MOTION: - trackMotion(static_cast<const MotionEntry*>(entry), action); - break; +bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos.itemAt(i); + if (memento.deviceId == deviceId + && memento.source == source + && memento.hovering) { + return true; + } } + return false; } -void InputDispatcher::InputState::trackKey(const KeyEntry* entry, int32_t action) { - if (action == AKEY_EVENT_ACTION_UP - && (entry->flags & AKEY_EVENT_FLAG_FALLBACK)) { - for (size_t i = 0; i < mFallbackKeys.size(); ) { - if (mFallbackKeys.valueAt(i) == entry->keyCode) { - mFallbackKeys.removeItemsAt(i); - } else { - i += 1; +bool InputDispatcher::InputState::trackKey(const KeyEntry* entry, + int32_t action, int32_t flags) { + switch (action) { + case AKEY_EVENT_ACTION_UP: { + if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) { + for (size_t i = 0; i < mFallbackKeys.size(); ) { + if (mFallbackKeys.valueAt(i) == entry->keyCode) { + mFallbackKeys.removeItemsAt(i); + } else { + i += 1; + } } } + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.removeAt(index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " + "keyCode=%d, scanCode=%d", + entry->deviceId, entry->source, entry->keyCode, entry->scanCode); +#endif + return false; } - for (size_t i = 0; i < mKeyMementos.size(); i++) { - KeyMemento& memento = mKeyMementos.editItemAt(i); - if (memento.deviceId == entry->deviceId - && memento.source == entry->source - && memento.keyCode == entry->keyCode - && memento.scanCode == entry->scanCode) { - switch (action) { - case AKEY_EVENT_ACTION_UP: - mKeyMementos.removeAt(i); - return; - - case AKEY_EVENT_ACTION_DOWN: - mKeyMementos.removeAt(i); - goto Found; - - default: - return; - } + case AKEY_EVENT_ACTION_DOWN: { + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.removeAt(index); } + addKeyMemento(entry, flags); + return true; } -Found: - if (action == AKEY_EVENT_ACTION_DOWN) { - mKeyMementos.push(); - KeyMemento& memento = mKeyMementos.editTop(); - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.keyCode = entry->keyCode; - memento.scanCode = entry->scanCode; - memento.flags = entry->flags; - memento.downTime = entry->downTime; + default: + return true; } } -void InputDispatcher::InputState::trackMotion(const MotionEntry* entry, int32_t action) { +bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, + int32_t action, int32_t flags) { int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; - for (size_t i = 0; i < mMotionMementos.size(); i++) { - MotionMemento& memento = mMotionMementos.editItemAt(i); - if (memento.deviceId == entry->deviceId - && memento.source == entry->source) { - switch (actionMasked) { - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: - case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - case AMOTION_EVENT_ACTION_HOVER_EXIT: - mMotionMementos.removeAt(i); - return; + switch (actionMasked) { + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.removeAt(index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " + "actionMasked=%d", + entry->deviceId, entry->source, actionMasked); +#endif + return false; + } - case AMOTION_EVENT_ACTION_DOWN: - mMotionMementos.removeAt(i); - goto Found; + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.removeAt(index); + } + addMotionMemento(entry, flags, false /*hovering*/); + return true; + } - case AMOTION_EVENT_ACTION_POINTER_UP: - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_MOVE: - memento.setPointers(entry); - return; + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_MOVE: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + MotionMemento& memento = mMotionMementos.editItemAt(index); + memento.setPointers(entry); + return true; + } + if (actionMasked == AMOTION_EVENT_ACTION_MOVE + && (entry->source & (AINPUT_SOURCE_CLASS_JOYSTICK + | AINPUT_SOURCE_CLASS_NAVIGATION))) { + // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP. + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Dropping inconsistent motion pointer up/down or move event: " + "deviceId=%d, source=%08x, actionMasked=%d", + entry->deviceId, entry->source, actionMasked); +#endif + return false; + } - default: - return; - } + case AMOTION_EVENT_ACTION_HOVER_EXIT: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.removeAt(index); + return true; } +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x", + entry->deviceId, entry->source); +#endif + return false; } -Found: - switch (actionMasked) { - case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - case AMOTION_EVENT_ACTION_HOVER_EXIT: - mMotionMementos.push(); - MotionMemento& memento = mMotionMementos.editTop(); - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.xPrecision = entry->xPrecision; - memento.yPrecision = entry->yPrecision; - memento.downTime = entry->downTime; - memento.setPointers(entry); - memento.hovering = actionMasked != AMOTION_EVENT_ACTION_DOWN; + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.removeAt(index); + } + addMotionMemento(entry, flags, true /*hovering*/); + return true; + } + + default: + return true; } } +ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const { + for (size_t i = 0; i < mKeyMementos.size(); i++) { + const KeyMemento& memento = mKeyMementos.itemAt(i); + if (memento.deviceId == entry->deviceId + && memento.source == entry->source + && memento.keyCode == entry->keyCode + && memento.scanCode == entry->scanCode) { + return i; + } + } + return -1; +} + +ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry, + bool hovering) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos.itemAt(i); + if (memento.deviceId == entry->deviceId + && memento.source == entry->source + && memento.hovering == hovering) { + return i; + } + } + return -1; +} + +void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) { + mKeyMementos.push(); + KeyMemento& memento = mKeyMementos.editTop(); + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.keyCode = entry->keyCode; + memento.scanCode = entry->scanCode; + memento.flags = flags; + memento.downTime = entry->downTime; +} + +void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, + int32_t flags, bool hovering) { + mMotionMementos.push(); + MotionMemento& memento = mMotionMementos.editTop(); + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.flags = flags; + memento.xPrecision = entry->xPrecision; + memento.yPrecision = entry->yPrecision; + memento.downTime = entry->downTime; + memento.setPointers(entry); + memento.hovering = hovering; +} + void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { pointerCount = entry->pointerCount; for (uint32_t i = 0; i < entry->pointerCount; i++) { @@ -4243,20 +4383,17 @@ void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator, Vector<EventEntry*>& outEvents, const CancelationOptions& options) { - for (size_t i = 0; i < mKeyMementos.size(); ) { + for (size_t i = 0; i < mKeyMementos.size(); i++) { const KeyMemento& memento = mKeyMementos.itemAt(i); if (shouldCancelKey(memento, options)) { outEvents.push(allocator->obtainKeyEntry(currentTime, memento.deviceId, memento.source, 0, AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode, memento.scanCode, 0, 0, memento.downTime)); - mKeyMementos.removeAt(i); - } else { - i += 1; } } - for (size_t i = 0; i < mMotionMementos.size(); ) { + for (size_t i = 0; i < mMotionMementos.size(); i++) { const MotionMemento& memento = mMotionMementos.itemAt(i); if (shouldCancelMotion(memento, options)) { outEvents.push(allocator->obtainMotionEntry(currentTime, @@ -4264,12 +4401,9 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL, - 0, 0, 0, 0, + memento.flags, 0, 0, 0, memento.xPrecision, memento.yPrecision, memento.downTime, memento.pointerCount, memento.pointerProperties, memento.pointerCoords)); - mMotionMementos.removeAt(i); - } else { - i += 1; } } } diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h index 676d162daaac..bdd1922b9339 100644 --- a/services/input/InputDispatcher.h +++ b/services/input/InputDispatcher.h @@ -522,6 +522,10 @@ private: // True if dispatch has started. bool inProgress; + // Set to the resolved action and flags when the event is enqueued. + int32_t resolvedAction; + int32_t resolvedFlags; + // For motion events: // Pointer to the first motion sample to dispatch in this cycle. // Usually NULL to indicate that the list of motion samples begins at @@ -709,14 +713,19 @@ private: // Returns true if there is no state to be canceled. bool isNeutral() const; - // Records tracking information for an event that has just been published. - void trackEvent(const EventEntry* entry, int32_t action); + // Returns true if the specified source is known to have received a hover enter + // motion event. + bool isHovering(int32_t deviceId, uint32_t source) const; // Records tracking information for a key event that has just been published. - void trackKey(const KeyEntry* entry, int32_t action); + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); // Records tracking information for a motion event that has just been published. - void trackMotion(const MotionEntry* entry, int32_t action); + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); // Synthesizes cancelation events for the current state and resets the tracked state. void synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator, @@ -756,6 +765,7 @@ private: struct MotionMemento { int32_t deviceId; uint32_t source; + int32_t flags; float xPrecision; float yPrecision; nsecs_t downTime; @@ -771,6 +781,12 @@ private: Vector<MotionMemento> mMotionMementos; KeyedVector<int32_t, int32_t> mFallbackKeys; + ssize_t findKeyMemento(const KeyEntry* entry) const; + ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; + + void addKeyMemento(const KeyEntry* entry, int32_t flags); + void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); + static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options); static bool shouldCancelMotion(const MotionMemento& memento, diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 3e4c66691cb0..82c3af370ea8 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -53,6 +53,7 @@ #define INDENT2 " " #define INDENT3 " " #define INDENT4 " " +#define INDENT5 " " namespace android { @@ -154,14 +155,15 @@ static uint32_t getButtonStateForScanCode(int32_t scanCode) { case BTN_LEFT: return AMOTION_EVENT_BUTTON_PRIMARY; case BTN_RIGHT: + case BTN_STYLUS: return AMOTION_EVENT_BUTTON_SECONDARY; case BTN_MIDDLE: + case BTN_STYLUS2: return AMOTION_EVENT_BUTTON_TERTIARY; case BTN_SIDE: return AMOTION_EVENT_BUTTON_BACK; - case BTN_EXTRA: - return AMOTION_EVENT_BUTTON_FORWARD; case BTN_FORWARD: + case BTN_EXTRA: return AMOTION_EVENT_BUTTON_FORWARD; case BTN_BACK: return AMOTION_EVENT_BUTTON_BACK; @@ -176,8 +178,7 @@ static uint32_t getButtonStateForScanCode(int32_t scanCode) { static bool isPointerDown(int32_t buttonState) { return buttonState & (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY - | AMOTION_EVENT_BUTTON_TERTIARY - | AMOTION_EVENT_BUTTON_ERASER); + | AMOTION_EVENT_BUTTON_TERTIARY); } static int32_t calculateEdgeFlagsUsingPointerBounds( @@ -921,7 +922,7 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) { #if DEBUG_RAW_EVENTS LOGD("Input event: device=%d type=0x%04x scancode=0x%04x " - "keycode=0x%04x value=0x%04x flags=0x%08x", + "keycode=0x%04x value=0x%08x flags=0x%08x", rawEvent->deviceId, rawEvent->type, rawEvent->scanCode, rawEvent->keyCode, rawEvent->value, rawEvent->flags); #endif @@ -1921,8 +1922,17 @@ void TouchInputMapper::dump(String8& dump) { dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mLocked.distanceScale); dump.appendFormat(INDENT3 "Last Touch:\n"); - dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount); dump.appendFormat(INDENT4 "Button State: 0x%08x\n", mLastTouch.buttonState); + dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount); + for (uint32_t i = 0; i < mLastTouch.pointerCount; i++) { + const PointerData& pointer = mLastTouch.pointers[i]; + dump.appendFormat(INDENT5 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " + "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " + "orientation=%d, distance=%d, isStylus=%s\n", i, + pointer.id, pointer.x, pointer.y, pointer.pressure, + pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, pointer.toolMinor, + pointer.orientation, pointer.distance, toString(pointer.isStylus)); + } if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n"); @@ -3704,6 +3714,29 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); + } else if (dispatchedGestureIdBits.isEmpty() + && !mPointerGesture.lastGestureIdBits.isEmpty()) { + // Synthesize a hover move event after all pointers go up to indicate that + // the pointer is hovering again even if the user is not currently touching + // the touch pad. This ensures that a view will receive a fresh hover enter + // event after a tap. + float x, y; + mPointerController->getPosition(&x, &y); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER; + + PointerCoords pointerCoords; + pointerCoords.clear(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + + getDispatcher()->notifyMotion(when, getDeviceId(), mPointerSource, policyFlags, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, &pointerProperties, &pointerCoords, 0, 0, mPointerGesture.downTime); } // Update state. diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp index 08cc75e631eb..0ae2ab898d67 100644 --- a/services/input/SpriteController.cpp +++ b/services/input/SpriteController.cpp @@ -252,11 +252,7 @@ void SpriteController::doUpdateSprites() { | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) { status_t status; if (!haveTransaction) { - status = mSurfaceComposerClient->openTransaction(); - if (status) { - LOGE("Error %d opening transation to update sprite surface.", status); - break; - } + SurfaceComposerClient::openGlobalTransaction(); haveTransaction = true; } @@ -322,10 +318,7 @@ void SpriteController::doUpdateSprites() { } if (haveTransaction) { - status_t status = mSurfaceComposerClient->closeTransaction(); - if (status) { - LOGE("Error %d closing transaction to update sprite surface.", status); - } + SurfaceComposerClient::closeGlobalTransaction(); } // If any surfaces were changed, write back the new surface properties to the sprites. diff --git a/services/java/com/android/server/DnsPinger.java b/services/java/com/android/server/DnsPinger.java index 05de53a2810c..a7324d9a958d 100644 --- a/services/java/com/android/server/DnsPinger.java +++ b/services/java/com/android/server/DnsPinger.java @@ -20,7 +20,9 @@ import android.content.ContentResolver; import android.content.Context; import android.net.ConnectivityManager; import android.net.LinkProperties; +import android.net.NetworkUtils; import android.os.SystemClock; +import android.provider.Settings; import android.util.Slog; import java.net.DatagramPacket; @@ -38,7 +40,7 @@ import java.util.Random; * API may not differentiate between a time out and a failure lookup (which we * really care about). * <p> - * TODO : More general API. Socket does not bind to specified connection type + * TODO : More general API. Socket does not bind to specified connection type * TODO : Choice of DNS query location - current looks up www.android.com * * @hide @@ -56,44 +58,65 @@ public final class DnsPinger { private static Random sRandom = new Random(); private ConnectivityManager mConnectivityManager = null; - private ContentResolver mContentResolver; private Context mContext; private int mConnectionType; + private InetAddress mDefaultDns; private String TAG; - /** - * @param connectionType The connection type from @link {@link ConnectivityManager} + * @param connectionType The connection type from {@link ConnectivityManager} */ public DnsPinger(String TAG, Context context, int connectionType) { mContext = context; - mContentResolver = context.getContentResolver(); mConnectionType = connectionType; + if (!ConnectivityManager.isNetworkTypeValid(connectionType)) { + Slog.e(TAG, "Invalid connectionType in constructor: " + connectionType); + } this.TAG = TAG; + + mDefaultDns = getDefaultDns(); } /** - * @return The first DNS in the link properties of the specified connection type + * @return The first DNS in the link properties of the specified connection + * type or the default system DNS if the link properties has null + * dns set. Should not be null. */ public InetAddress getDns() { - LinkProperties linkProperties = getCurLinkProperties(); - if (linkProperties == null) - return null; + if (mConnectivityManager == null) { + mConnectivityManager = (ConnectivityManager) mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + } - Collection<InetAddress> dnses = linkProperties.getDnses(); - if (dnses == null || dnses.size() == 0) - return null; + LinkProperties curLinkProps = mConnectivityManager.getLinkProperties(mConnectionType); + if (curLinkProps == null) { + Slog.e(TAG, "getCurLinkProperties:: LP for type" + mConnectionType + " is null!"); + return mDefaultDns; + } + + Collection<InetAddress> dnses = curLinkProps.getDnses(); + if (dnses == null || dnses.size() == 0) { + Slog.v(TAG, "getDns::LinkProps has null dns - returning default"); + return mDefaultDns; + } return dnses.iterator().next(); } - private LinkProperties getCurLinkProperties() { - if (mConnectivityManager == null) { - mConnectivityManager = (ConnectivityManager) mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); + private InetAddress getDefaultDns() { + String dns = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_DNS_SERVER); + if (dns == null || dns.length() == 0) { + dns = mContext.getResources().getString( + com.android.internal.R.string.config_default_dns_server); + } + try { + return NetworkUtils.numericToInetAddress(dns); + } catch (IllegalArgumentException e) { + Slog.w(TAG, "getDefaultDns::malformed default dns address"); + return null; } - return mConnectivityManager.getLinkProperties(mConnectionType); } /** diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index b03bd4d28df0..14abf808b71f 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1641,7 +1641,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final InputMethodInfo imi = mMethodMap.get(mCurMethodId); if (imi == null) return false; final int N = subtypes.length; - mFileManager.addInputMethodSubtypes(mCurMethodId, subtypes); + mFileManager.addInputMethodSubtypes(imi, subtypes); buildInputMethodListLocked(mMethodList, mMethodMap); return true; } @@ -2023,25 +2023,26 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final CharSequence label = imi.loadLabel(pm); if (showSubtypes && enabledSubtypeSet.size() > 0) { final int subtypeCount = imi.getSubtypeCount(); + if (DEBUG) { + Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); + } for (int j = 0; j < subtypeCount; ++j) { - InputMethodSubtype subtype = imi.getSubtypeAt(j); - if (enabledSubtypeSet.contains(String.valueOf(subtype.hashCode())) - && !subtype.isAuxiliary()) { + final InputMethodSubtype subtype = imi.getSubtypeAt(j); + final String subtypeHashCode = String.valueOf(subtype.hashCode()); + // We show all enabled IMEs and subtypes when an IME is shown. + if (enabledSubtypeSet.contains(subtypeHashCode) + && (mInputShown || !subtype.isAuxiliary())) { final CharSequence title; - int nameResId = subtype.getNameResId(); - String mode = subtype.getMode(); - if (nameResId != 0) { - title = TextUtils.concat(subtype.getDisplayName(context, - imi.getPackageName(), imi.getServiceInfo().applicationInfo), - (TextUtils.isEmpty(label) ? "" : " (" + label + ")")); - } else { - CharSequence language = subtype.getLocale(); - // TODO: Use more friendly Title and UI - title = label + "," + (mode == null ? "" : mode) + "," - + (language == null ? "" : language); - } + final String mode = subtype.getMode(); + title = TextUtils.concat(subtype.getDisplayName(context, + imi.getPackageName(), imi.getServiceInfo().applicationInfo), + (TextUtils.isEmpty(label) ? "" : " (" + label + ")")); imList.add(new Pair<CharSequence, Pair<InputMethodInfo, Integer>>( title, new Pair<InputMethodInfo, Integer>(imi, j))); + // Removing this subtype from enabledSubtypeSet because we no longer + // need to add an entry of this subtype to imList to avoid duplicated + // entries. + enabledSubtypeSet.remove(subtypeHashCode); } } } else { @@ -2338,7 +2339,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } - ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>( + final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>( applicableModeAndSubtypesMap.values()); if (!containsKeyboardSubtype) { InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked( @@ -3016,17 +3017,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public void addInputMethodSubtypes( - String imiId, InputMethodSubtype[] additionalSubtypes) { + InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) { synchronized (mMethodMap) { + final HashSet<InputMethodSubtype> existingSubtypes = + new HashSet<InputMethodSubtype>(); + for (int i = 0; i < imi.getSubtypeCount(); ++i) { + existingSubtypes.add(imi.getSubtypeAt(i)); + } + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); final int N = additionalSubtypes.length; for (int i = 0; i < N; ++i) { final InputMethodSubtype subtype = additionalSubtypes[i]; - if (!subtypes.contains(subtype)) { + if (!subtypes.contains(subtype) && !existingSubtypes.contains(subtype)) { subtypes.add(subtype); } } - mSubtypesMap.put(imiId, subtypes); + mSubtypesMap.put(imi.getId(), subtypes); writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap); } @@ -3133,8 +3140,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE); final String imeSubtypeExtraValue = parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE); - final boolean isAuxiliary = - Boolean.valueOf(parser.getAttributeValue(null, ATTR_IS_AUXILIARY)); + final boolean isAuxiliary = "1".equals(String.valueOf( + parser.getAttributeValue(null, ATTR_IS_AUXILIARY))); final InputMethodSubtype subtype = new InputMethodSubtype(label, icon, imeSubtypeLocale, imeSubtypeMode, imeSubtypeExtraValue, isAuxiliary); diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index d3244ecc376b..54e54327f8f7 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -1626,6 +1626,30 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC } } + public String getSecureContainerFilesystemPath(String id) { + validatePermission(android.Manifest.permission.ASEC_ACCESS); + waitForReady(); + warnOnNotMounted(); + + try { + ArrayList<String> rsp = mConnector.doCommand(String.format("asec fspath %s", id)); + String []tok = rsp.get(0).split(" "); + int code = Integer.parseInt(tok[0]); + if (code != VoldResponseCode.AsecPathResult) { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + return tok[1]; + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedStorageNotFound) { + Slog.i(TAG, String.format("Container '%s' not found", id)); + return null; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + } + public void finishMediaUpdate() { mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index e730d0d6311b..adc65708d5ef 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -37,6 +37,11 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; import android.util.Slog; +import android.util.SparseBooleanArray; + +import com.google.android.collect.Lists; +import com.google.android.collect.Maps; +import com.google.android.collect.Sets; import java.io.BufferedReader; import java.io.DataInputStream; @@ -48,6 +53,8 @@ import java.io.InputStreamReader; import java.net.Inet4Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.NoSuchElementException; import java.util.StringTokenizer; import java.util.concurrent.CountDownLatch; @@ -65,9 +72,18 @@ class NetworkManagementService extends INetworkManagementService.Stub { private static final int ADD = 1; private static final int REMOVE = 2; + /** Path to {@code /proc/uid_stat}. */ @Deprecated - private static final File STATS_UIDSTAT = new File("/proc/uid_stat"); - private static final File STATS_NETFILTER = new File("/proc/net/xt_qtaguid/stats"); + private final File mProcStatsUidstat; + /** Path to {@code /proc/net/xt_qtaguid/stats}. */ + private final File mProcStatsNetfilter; + + /** {@link #mProcStatsNetfilter} headers. */ + private static final String KEY_IFACE = "iface"; + private static final String KEY_TAG_HEX = "acct_tag_hex"; + private static final String KEY_UID = "uid_tag_int"; + private static final String KEY_RX = "rx_bytes"; + private static final String KEY_TX = "tx_bytes"; class NetdResponseCode { public static final int InterfaceListResult = 110; @@ -102,15 +118,23 @@ class NetworkManagementService extends INetworkManagementService.Stub { private ArrayList<INetworkManagementEventObserver> mObservers; + /** Set of interfaces with active quotas. */ + private HashSet<String> mInterfaceQuota = Sets.newHashSet(); + /** Set of UIDs with active reject rules. */ + private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray(); + /** * Constructs a new NetworkManagementService instance * * @param context Binder context for this service */ - private NetworkManagementService(Context context) { + private NetworkManagementService(Context context, File procRoot) { mContext = context; mObservers = new ArrayList<INetworkManagementEventObserver>(); + mProcStatsUidstat = new File(procRoot, "uid_stat"); + mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats"); + if ("simulator".equals(SystemProperties.get("ro.product.device"))) { return; } @@ -121,7 +145,8 @@ class NetworkManagementService extends INetworkManagementService.Stub { } public static NetworkManagementService create(Context context) throws InterruptedException { - NetworkManagementService service = new NetworkManagementService(context); + NetworkManagementService service = new NetworkManagementService( + context, new File("/proc/")); if (DBG) Slog.d(TAG, "Creating NetworkManagementService"); service.mThread.start(); if (DBG) Slog.d(TAG, "Awaiting socket connection"); @@ -130,6 +155,12 @@ class NetworkManagementService extends INetworkManagementService.Stub { return service; } + // @VisibleForTesting + public static NetworkManagementService createForTest(Context context, File procRoot) { + // TODO: eventually connect with mock netd + return new NetworkManagementService(context, procRoot); + } + public void registerObserver(INetworkManagementEventObserver obs) { Slog.d(TAG, "Registering observer"); mObservers.add(obs); @@ -888,7 +919,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - if (STATS_NETFILTER.exists()) { + if (mProcStatsNetfilter.exists()) { return getNetworkStatsDetailNetfilter(UID_ALL); } else { return getNetworkStatsDetailUidstat(UID_ALL); @@ -896,13 +927,91 @@ class NetworkManagementService extends INetworkManagementService.Stub { } @Override + public void setInterfaceQuota(String iface, long quota) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + synchronized (mInterfaceQuota) { + if (mInterfaceQuota.contains(iface)) { + // TODO: eventually consider throwing + return; + } + + final StringBuilder command = new StringBuilder(); + command.append("bandwidth setiquota ").append(iface).append(" ").append(quota); + + try { + // TODO: add support for quota shared across interfaces + mConnector.doCommand(command.toString()); + mInterfaceQuota.add(iface); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } + } + } + + @Override + public void removeInterfaceQuota(String iface) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + synchronized (mInterfaceQuota) { + if (!mInterfaceQuota.contains(iface)) { + // TODO: eventually consider throwing + return; + } + + final StringBuilder command = new StringBuilder(); + command.append("bandwidth removeiquota ").append(iface); + + try { + // TODO: add support for quota shared across interfaces + mConnector.doCommand(command.toString()); + mInterfaceQuota.remove(iface); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } + } + } + + @Override + public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + synchronized (mUidRejectOnQuota) { + final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false); + if (oldRejectOnQuota == rejectOnQuotaInterfaces) { + // TODO: eventually consider throwing + return; + } + + final StringBuilder command = new StringBuilder(); + command.append("bandwidth"); + if (rejectOnQuotaInterfaces) { + command.append(" addnaughtyapps"); + } else { + command.append(" removenaughtyapps"); + } + command.append(" ").append(uid); + + try { + mConnector.doCommand(command.toString()); + if (rejectOnQuotaInterfaces) { + mUidRejectOnQuota.put(uid, true); + } else { + mUidRejectOnQuota.delete(uid); + } + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } + } + } + public NetworkStats getNetworkStatsUidDetail(int uid) { if (Binder.getCallingUid() != uid) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); } - if (STATS_NETFILTER.exists()) { + if (mProcStatsNetfilter.exists()) { return getNetworkStatsDetailNetfilter(uid); } else { return getNetworkStatsDetailUidstat(uid); @@ -914,35 +1023,36 @@ class NetworkManagementService extends INetworkManagementService.Stub { */ private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) { final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); + final ArrayList<String> keys = Lists.newArrayList(); + final ArrayList<String> values = Lists.newArrayList(); + final HashMap<String, String> parsed = Maps.newHashMap(); BufferedReader reader = null; try { - reader = new BufferedReader(new FileReader(STATS_NETFILTER)); - - // assumes format from kernel: - // idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes + reader = new BufferedReader(new FileReader(mProcStatsNetfilter)); - // skip first line, which is legend + // parse first line as header String line = reader.readLine(); - while ((line = reader.readLine()) != null) { - final StringTokenizer t = new StringTokenizer(line); + splitLine(line, keys); - final String idx = t.nextToken(); - final String iface = t.nextToken(); + // parse remaining lines + while ((line = reader.readLine()) != null) { + splitLine(line, values); + parseLine(keys, values, parsed); try { - // TODO: kernel currently emits tag in upper half of long; - // eventually switch to directly using int. - final int tag = (int) (Long.parseLong(t.nextToken().substring(2), 16) >> 32); - final int uid = Integer.parseInt(t.nextToken()); - final long rx = Long.parseLong(t.nextToken()); - final long tx = Long.parseLong(t.nextToken()); + final String iface = parsed.get(KEY_IFACE); + final int tag = NetworkManagementSocketTagger.kernelToTag( + parsed.get(KEY_TAG_HEX)); + final int uid = Integer.parseInt(parsed.get(KEY_UID)); + final long rx = Long.parseLong(parsed.get(KEY_RX)); + final long tx = Long.parseLong(parsed.get(KEY_TX)); if (limitUid == UID_ALL || limitUid == uid) { stats.addEntry(iface, uid, tag, rx, tx); } } catch (NumberFormatException e) { - Slog.w(TAG, "problem parsing stats for idx " + idx + ": " + e); + Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); } } } catch (IOException e) { @@ -964,7 +1074,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { private NetworkStats getNetworkStatsDetailUidstat(int limitUid) { final String[] knownUids; if (limitUid == UID_ALL) { - knownUids = STATS_UIDSTAT.list(); + knownUids = mProcStatsUidstat.list(); } else { knownUids = new String[] { String.valueOf(limitUid) }; } @@ -973,7 +1083,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { SystemClock.elapsedRealtime(), knownUids.length); for (String uid : knownUids) { final int uidInt = Integer.parseInt(uid); - final File uidPath = new File(STATS_UIDSTAT, uid); + final File uidPath = new File(mProcStatsUidstat, uid); final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd")); stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx); @@ -1048,6 +1158,32 @@ class NetworkManagementService extends INetworkManagementService.Stub { } /** + * Split given line into {@link ArrayList}. + */ + private static void splitLine(String line, ArrayList<String> outSplit) { + outSplit.clear(); + + final StringTokenizer t = new StringTokenizer(line); + while (t.hasMoreTokens()) { + outSplit.add(t.nextToken()); + } + } + + /** + * Zip the two given {@link ArrayList} as key and value pairs into + * {@link HashMap}. + */ + private static void parseLine( + ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) { + outParsed.clear(); + + final int size = Math.min(keys.size(), values.size()); + for (int i = 0; i < size; i++) { + outParsed.put(keys.get(i), values.get(i)); + } + } + + /** * Utility method to read a single plain-text {@link Long} from the given * {@link File}, usually from a {@code /proc/} filesystem. */ diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cd68c6854153..dbfd145bc65f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -283,7 +283,8 @@ class ServerThread extends Thread { try { Slog.i(TAG, "NetworkPolicy Service"); networkPolicy = new NetworkPolicyManagerService( - context, ActivityManagerService.self(), power, networkStats); + context, ActivityManagerService.self(), power, + networkStats, networkManagement); ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy); } catch (Throwable e) { Slog.e(TAG, "Failure starting NetworkPolicy Service", e); diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index 3ba9c14cf4d7..1356e2a9b23f 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -95,14 +95,14 @@ public class WifiWatchdogService { private static final long MIN_SINGLE_DNS_CHECK_INTERVAL = 10 * 60 * 1000; private static final long MIN_WALLED_GARDEN_INTERVAL = 15 * 60 * 1000; - private static final int MAX_CHECKS_PER_SSID = 7; - private static final int NUM_DNS_PINGS = 5; + private static final int MAX_CHECKS_PER_SSID = 9; + private static final int NUM_DNS_PINGS = 7; private static double MIN_RESPONSE_RATE = 0.50; // TODO : Adjust multiple DNS downward to 250 on repeated failure // private static final int MULTI_DNS_PING_TIMEOUT_MS = 250; - private static final int DNS_PING_TIMEOUT_MS = 1000; + private static final int DNS_PING_TIMEOUT_MS = 800; private static final long DNS_PING_INTERVAL = 250; private static final long BLACKLIST_FOLLOWUP_INTERVAL = 15 * 1000; diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index b4fdc9f3c0ff..293702dfe750 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -446,6 +446,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub { Binder.getCallingPid(), Binder.getCallingUid(), null); } + private void dumpHelp(PrintWriter pw) { + pw.println("Battery stats (batteryinfo) dump options:"); + pw.println(" [--checkin] [--reset] [--write] [-h]"); + pw.println(" --checkin: format output for a checkin report."); + pw.println(" --reset: reset the stats, clearing all current data."); + pw.println(" --write: force write current collected stats to disk."); + pw.println(" -h: print this help text."); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { boolean isCheckin = false; @@ -466,8 +475,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub { pw.println("Battery stats written."); noOutput = true; } + } else if ("-h".equals(arg)) { + dumpHelp(pw); + return; } else { pw.println("Unknown option: " + arg); + dumpHelp(pw); } } } diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index db3b61e5cb17..f5efda9bb8ce 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -27,15 +27,23 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.INetworkManagementEventObserver; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; import android.os.Binder; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; +import android.os.Process; +import android.os.SystemClock; +import android.os.SystemProperties; import android.util.Log; import com.android.internal.R; import com.android.internal.net.VpnConfig; import com.android.server.ConnectivityService.VpnCallback; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charsets; + /** * @hide */ @@ -49,7 +57,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub { private String mPackageName; private String mInterfaceName; - private String mDnsPropertyPrefix; + + private LegacyVpnRunner mLegacyVpnRunner; public Vpn(Context context, VpnCallback callback) { mContext = context; @@ -249,4 +258,195 @@ public class Vpn extends INetworkManagementEventObserver.Stub { private native void nativeReset(String name); private native int nativeCheck(String name); private native void nativeProtect(int fd, String name); + + /** + * Handle legacy VPN requests. This method stops the services and restart + * them if their arguments are not null. Heavy things are offloaded to + * another thread, so callers will not be blocked too long. + * + * @param raoocn The arguments to be passed to racoon. + * @param mtpd The arguments to be passed to mtpd. + */ + public synchronized void doLegacyVpn(String[] racoon, String[] mtpd) { + // Currently only system user is allowed. + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Unauthorized Caller"); + } + + // If the previous runner is still alive, interrupt it. + if (mLegacyVpnRunner != null && mLegacyVpnRunner.isAlive()) { + mLegacyVpnRunner.interrupt(); + } + + // Start a new runner and we are done! + mLegacyVpnRunner = new LegacyVpnRunner( + new String[] {"racoon", "mtpd"}, racoon, mtpd); + mLegacyVpnRunner.start(); + } + + /** + * Bringing up a VPN connection takes time, and that is all this thread + * does. Here we have plenty of time. The only thing we need to take + * care of is responding to interruptions as soon as possible. Otherwise + * requests will be piled up. This can be done in a Handler as a state + * machine, but it is much easier to read in the current form. + */ + private class LegacyVpnRunner extends Thread { + private static final String TAG = "LegacyVpnRunner"; + + private static final String NONE = "--"; + + private final String[] mServices; + private final String[][] mArguments; + private long mTimer = -1; + + public LegacyVpnRunner(String[] services, String[]... arguments) { + super(TAG); + mServices = services; + mArguments = arguments; + } + + @Override + public void run() { + // Wait for the previous thread since it has been interrupted. + Log.v(TAG, "wait"); + synchronized (TAG) { + Log.v(TAG, "run"); + execute(); + Log.v(TAG, "exit"); + } + } + + private void checkpoint(boolean yield) throws InterruptedException { + long now = SystemClock.elapsedRealtime(); + if (mTimer == -1) { + mTimer = now; + Thread.sleep(1); + } else if (now - mTimer <= 30000) { + Thread.sleep(yield ? 200 : 1); + } else { + throw new InterruptedException("timeout"); + } + } + + private void execute() { + // Catch all exceptions so we can clean up few things. + try { + // Initialize the timer. + checkpoint(false); + + // First stop the services. + for (String service : mServices) { + SystemProperties.set("ctl.stop", service); + } + + // Wait for the services to stop. + for (String service : mServices) { + String key = "init.svc." + service; + while (!"stopped".equals(SystemProperties.get(key))) { + checkpoint(true); + } + } + + // Reset the properties. + SystemProperties.set("vpn.dns", NONE); + SystemProperties.set("vpn.via", NONE); + while (!NONE.equals(SystemProperties.get("vpn.dns")) || + !NONE.equals(SystemProperties.get("vpn.via"))) { + checkpoint(true); + } + + // Check if we need to restart some services. + boolean restart = false; + for (String[] arguments : mArguments) { + restart = restart || (arguments != null); + } + if (!restart) { + return; + } + + // Start the service with arguments. + for (int i = 0; i < mServices.length; ++i) { + String[] arguments = mArguments[i]; + if (arguments == null) { + continue; + } + + // Start the service. + String service = mServices[i]; + SystemProperties.set("ctl.start", service); + + // Wait for the service to start. + String key = "init.svc." + service; + while (!"running".equals(SystemProperties.get(key))) { + checkpoint(true); + } + + // Create the control socket. + LocalSocket socket = new LocalSocket(); + LocalSocketAddress address = new LocalSocketAddress( + service, LocalSocketAddress.Namespace.RESERVED); + + // Wait for the socket to connect. + while (true) { + try { + socket.connect(address); + break; + } catch (Exception e) { + // ignore + } + checkpoint(true); + } + socket.setSoTimeout(500); + + // Send over the arguments. + OutputStream output = socket.getOutputStream(); + for (String argument : arguments) { + byte[] bytes = argument.getBytes(Charsets.UTF_8); + if (bytes.length >= 0xFFFF) { + throw new IllegalArgumentException("argument too large"); + } + output.write(bytes.length >> 8); + output.write(bytes.length); + output.write(bytes); + checkpoint(false); + } + + // Send End-Of-Arguments. + output.write(0xFF); + output.write(0xFF); + output.flush(); + socket.close(); + } + + // Now here is the beast from the old days. We check few + // properties to figure out the current status. Ideally we + // can read things back from the sockets and get rid of the + // properties, but we have no time... + while (NONE.equals(SystemProperties.get("vpn.dns")) || + NONE.equals(SystemProperties.get("vpn.via"))) { + + // Check if a running service is dead. + for (int i = 0; i < mServices.length; ++i) { + String service = mServices[i]; + if (mArguments[i] != null && !"running".equals( + SystemProperties.get("init.svc." + service))) { + throw new IllegalArgumentException(service + " is dead"); + } + } + checkpoint(true); + } + + // Great! Now we are connected! + Log.i(TAG, "connected!"); + // TODO: + + } catch (Exception e) { + Log.i(TAG, e.getMessage()); + for (String service : mServices) { + SystemProperties.set("ctl.stop", service); + } + } + } + } } diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 67e73f5fa10c..4fa3bda0b13f 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -132,7 +132,7 @@ public class GpsLocationProvider implements LocationProviderInterface { private static final int GPS_CAPABILITY_MSB = 0x0000002; private static final int GPS_CAPABILITY_MSA = 0x0000004; private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008; - + private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010; // these need to match AGpsType enum in gps.h private static final int AGPS_TYPE_SUPL = 1; @@ -200,6 +200,9 @@ public class GpsLocationProvider implements LocationProviderInterface { private boolean mInjectNtpTimePending = true; private boolean mDownloadXtraDataPending = true; + // set to true if the GPS engine does not do on-demand NTP time requests + private boolean mPeriodicTimeInjection; + // true if GPS is navigating private boolean mNavigating; @@ -549,10 +552,12 @@ public class GpsLocationProvider implements LocationProviderInterface { delay = RETRY_INTERVAL; } - // send delayed message for next NTP injection - // since this is delayed and not urgent we do not hold a wake lock here - mHandler.removeMessages(INJECT_NTP_TIME); - mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay); + if (mPeriodicTimeInjection) { + // send delayed message for next NTP injection + // since this is delayed and not urgent we do not hold a wake lock here + mHandler.removeMessages(INJECT_NTP_TIME); + mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay); + } } private void handleDownloadXtraData() { @@ -1305,6 +1310,11 @@ public class GpsLocationProvider implements LocationProviderInterface { */ private void setEngineCapabilities(int capabilities) { mEngineCapabilities = capabilities; + + if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) { + mPeriodicTimeInjection = true; + requestUtcTime(); + } } /** @@ -1438,6 +1448,14 @@ public class GpsLocationProvider implements LocationProviderInterface { } /** + * Called from native code to request utc time info + */ + + private void requestUtcTime() { + sendMessage(INJECT_NTP_TIME, 0, null); + } + + /** * Called from native code to request reference location info */ diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 584cd03dfcc2..1f2ec2ca8279 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -25,6 +25,7 @@ import static android.Manifest.permission.READ_PHONE_STATE; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.*; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -57,6 +58,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.IConnectivityManager; @@ -71,7 +74,9 @@ import android.net.NetworkTemplate; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; +import android.os.INetworkManagementService; import android.os.IPowerManager; +import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.telephony.TelephonyManager; @@ -108,6 +113,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import libcore.io.IoUtils; @@ -148,10 +154,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS; + private static final int MSG_RULES_CHANGED = 0x1; + private static final int MSG_METERED_IFACES_CHANGED = 0x2; + private final Context mContext; private final IActivityManager mActivityManager; private final IPowerManager mPowerManager; private final INetworkStatsService mNetworkStats; + private final INetworkManagementService mNetworkManagement; private final TrustedTime mTime; private IConnectivityManager mConnManager; @@ -160,6 +170,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final Object mRulesLock = new Object(); private boolean mScreenOn; + private boolean mBackgroundData; /** Current policy for network templates. */ private ArrayList<NetworkPolicy> mNetworkPolicy = Lists.newArrayList(); @@ -188,11 +199,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // TODO: keep whitelist of system-critical services that should never have // rules enforced, such as system, phone, and radio UIDs. + // TODO: watch for package added broadcast to catch new UIDs. + public NetworkPolicyManagerService(Context context, IActivityManager activityManager, - IPowerManager powerManager, INetworkStatsService networkStats) { + IPowerManager powerManager, INetworkStatsService networkStats, + INetworkManagementService networkManagement) { // TODO: move to using cached NtpTrustedTime - this(context, activityManager, powerManager, networkStats, new NtpTrustedTime(), - getSystemDir()); + this(context, activityManager, powerManager, networkStats, networkManagement, + new NtpTrustedTime(), getSystemDir()); } private static File getSystemDir() { @@ -200,17 +214,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } public NetworkPolicyManagerService(Context context, IActivityManager activityManager, - IPowerManager powerManager, INetworkStatsService networkStats, TrustedTime time, - File systemDir) { + IPowerManager powerManager, INetworkStatsService networkStats, + INetworkManagementService networkManagement, + TrustedTime time, File systemDir) { mContext = checkNotNull(context, "missing context"); mActivityManager = checkNotNull(activityManager, "missing activityManager"); mPowerManager = checkNotNull(powerManager, "missing powerManager"); mNetworkStats = checkNotNull(networkStats, "missing networkStats"); + mNetworkManagement = checkNotNull(networkManagement, "missing networkManagement"); mTime = checkNotNull(time, "missing TrustedTime"); mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); + mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback); mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml")); } @@ -231,6 +247,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } updateScreenOn(); + updateBackgroundData(true); try { mActivityManager.registerProcessObserver(mProcessObserver); @@ -256,11 +273,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED); mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler); - // listen for warning polling events; currently dispatched by + // listen for stats update events final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED); mContext.registerReceiver( mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler); + // listen for changes to background data flag + final IntentFilter bgFilter = new IntentFilter(ACTION_BACKGROUND_DATA_SETTING_CHANGED); + mContext.registerReceiver(mBgReceiver, bgFilter, CONNECTIVITY_INTERNAL, mHandler); + } private IProcessObserver mProcessObserver = new IProcessObserver.Stub() { @@ -269,9 +290,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like AMS should only be calling us mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG); - // skip when UID couldn't have any policy - if (!isUidValidForPolicy(mContext, uid)) return; - synchronized (mRulesLock) { // because a uid can have multiple pids running inside, we need to // remember all pid states and summarize foreground at uid level. @@ -292,9 +310,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like AMS should only be calling us mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG); - // skip when UID couldn't have any policy - if (!isUidValidForPolicy(mContext, uid)) return; - synchronized (mRulesLock) { // clear records and recompute, when they exist final SparseBooleanArray pidForeground = mUidPidForeground.get(uid); @@ -349,6 +364,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** + * Receiver that watches for + * {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}. + */ + private BroadcastReceiver mBgReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // on background handler thread, and verified CONNECTIVITY_INTERNAL + // permission above. + + synchronized (mRulesLock) { + updateBackgroundData(false); + } + } + }; + + /** * Check {@link NetworkPolicy} against current {@link INetworkStatsService} * to show visible notifications as needed. */ @@ -561,7 +592,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); - mMeteredIfaces.clear(); + final HashSet<String> newMeteredIfaces = Sets.newHashSet(); // apply each policy that we found ifaces for; compute remaining data // based on current cycle and historical stats, and push to kernel. @@ -591,28 +622,30 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { // remaining "quota" is based on usage in current cycle final long quota = Math.max(0, policy.limitBytes - total); - //kernelSetIfacesQuota(ifaces, quota); + + if (ifaces.length > 1) { + // TODO: switch to shared quota once NMS supports + Slog.w(TAG, "shared quota unsupported; generating rule for each iface"); + } for (String iface : ifaces) { - mMeteredIfaces.add(iface); + removeInterfaceQuota(iface); + setInterfaceQuota(iface, quota); + newMeteredIfaces.add(iface); } } } - // dispatch changed rule to existing listeners - // TODO: dispatch outside of holding lock - final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]); - final int length = mListeners.beginBroadcast(); - for (int i = 0; i < length; i++) { - final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); - if (listener != null) { - try { - listener.onMeteredIfacesChanged(meteredIfaces); - } catch (RemoteException e) { - } + // remove quota on any trailing interfaces + for (String iface : mMeteredIfaces) { + if (!newMeteredIfaces.contains(iface)) { + removeInterfaceQuota(iface); } } - mListeners.finishBroadcast(); + mMeteredIfaces = newMeteredIfaces; + + final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]); + mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget(); } /** @@ -804,32 +837,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mListeners.register(listener); - synchronized (mRulesLock) { - // dispatch any existing rules to new listeners - // TODO: dispatch outside of holding lock - final int size = mUidRules.size(); - for (int i = 0; i < size; i++) { - final int uid = mUidRules.keyAt(i); - final int uidRules = mUidRules.valueAt(i); - if (uidRules != RULE_ALLOW_ALL) { - try { - listener.onUidRulesChanged(uid, uidRules); - } catch (RemoteException e) { - } - } - } - - // dispatch any metered ifaces to new listeners - // TODO: dispatch outside of holding lock - if (mMeteredIfaces.size() > 0) { - final String[] meteredIfaces = mMeteredIfaces.toArray( - new String[mMeteredIfaces.size()]); - try { - listener.onMeteredIfacesChanged(meteredIfaces); - } catch (RemoteException e) { - } - } - } + // TODO: consider dispatching existing rules to new listeners } @Override @@ -963,6 +971,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void updateBackgroundData(boolean systemReady) { + synchronized (mRulesLock) { + try { + mBackgroundData = mConnManager.getBackgroundDataSetting(); + } catch (RemoteException e) { + } + if (systemReady && mBackgroundData) { + // typical behavior of background enabled during systemReady; + // no need to clear rules for all UIDs. + } else { + updateRulesForBackgroundDataLocked(); + } + } + } + /** * Update rules that might be changed by {@link #mScreenOn} value. */ @@ -977,9 +1000,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private void updateRulesForUidLocked(int uid) { - if (!isUidValidForPolicy(mContext, uid)) return; + /** + * Update rules that might be changed by {@link #mBackgroundData} value. + */ + private void updateRulesForBackgroundDataLocked() { + // update rules for all installed applications + final PackageManager pm = mContext.getPackageManager(); + final List<ApplicationInfo> apps = pm.getInstalledApplications(0); + for (ApplicationInfo app : apps) { + updateRulesForUidLocked(app.uid); + } + // and catch system UIDs + // TODO: keep in sync with android_filesystem_config.h + for (int uid = 1000; uid <= 1025; uid++) { + updateRulesForUidLocked(uid); + } + for (int uid = 2000; uid <= 2002; uid++) { + updateRulesForUidLocked(uid); + } + for (int uid = 3000; uid <= 3007; uid++) { + updateRulesForUidLocked(uid); + } + for (int uid = 9998; uid <= 9999; uid++) { + updateRulesForUidLocked(uid); + } + } + + private void updateRulesForUidLocked(int uid) { final int uidPolicy = getUidPolicy(uid); final boolean uidForeground = isUidForeground(uid); @@ -989,6 +1037,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // uid in background, and policy says to block metered data uidRules = RULE_REJECT_METERED; } + if (!uidForeground && !mBackgroundData) { + // uid in background, and global background disabled + uidRules = RULE_REJECT_METERED; + } // TODO: only dispatch when rules actually change @@ -996,21 +1048,82 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidRules.put(uid, uidRules); final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0; - //kernelSetUidRejectPaid(uid, rejectPaid); + setUidNetworkRules(uid, rejectMetered); // dispatch changed rule to existing listeners - // TODO: dispatch outside of holding lock - final int length = mListeners.beginBroadcast(); - for (int i = 0; i < length; i++) { - final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); - if (listener != null) { - try { - listener.onUidRulesChanged(uid, uidRules); - } catch (RemoteException e) { + mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget(); + } + + private Handler.Callback mHandlerCallback = new Handler.Callback() { + /** {@inheritDoc} */ + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_RULES_CHANGED: { + final int uid = msg.arg1; + final int uidRules = msg.arg2; + final int length = mListeners.beginBroadcast(); + for (int i = 0; i < length; i++) { + final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); + if (listener != null) { + try { + listener.onUidRulesChanged(uid, uidRules); + } catch (RemoteException e) { + } + } + } + mListeners.finishBroadcast(); + return true; + } + case MSG_METERED_IFACES_CHANGED: { + final String[] meteredIfaces = (String[]) msg.obj; + final int length = mListeners.beginBroadcast(); + for (int i = 0; i < length; i++) { + final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); + if (listener != null) { + try { + listener.onMeteredIfacesChanged(meteredIfaces); + } catch (RemoteException e) { + } + } + } + mListeners.finishBroadcast(); + return true; + } + default: { + return false; } } } - mListeners.finishBroadcast(); + }; + + private void setInterfaceQuota(String iface, long quota) { + try { + mNetworkManagement.setInterfaceQuota(iface, quota); + } catch (IllegalStateException e) { + Slog.e(TAG, "problem setting interface quota", e); + } catch (RemoteException e) { + Slog.e(TAG, "problem setting interface quota", e); + } + } + + private void removeInterfaceQuota(String iface) { + try { + mNetworkManagement.removeInterfaceQuota(iface); + } catch (IllegalStateException e) { + Slog.e(TAG, "problem removing interface quota", e); + } catch (RemoteException e) { + Slog.e(TAG, "problem removing interface quota", e); + } + } + + private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) { + try { + mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces); + } catch (IllegalStateException e) { + Slog.e(TAG, "problem setting uid rules", e); + } catch (RemoteException e) { + Slog.e(TAG, "problem setting uid rules", e); + } } private String getActiveSubscriberId() { diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 4a79d17ad8e7..7610a1181d60 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -124,8 +124,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private PendingIntent mPollIntent; // TODO: listen for kernel push events through netd instead of polling - // TODO: watch for UID uninstall, and transfer stats into single bucket - // TODO: trim empty history objects entirely private static final long KB_IN_BYTES = 1024; @@ -506,8 +504,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { try { networkSnapshot = mNetworkManager.getNetworkStatsSummary(); uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null; + } catch (IllegalStateException e) { + Slog.w(TAG, "problem reading network stats: " + e); + return; } catch (RemoteException e) { - Slog.w(TAG, "problem reading network stats"); + Slog.w(TAG, "problem reading network stats: " + e); return; } diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java index d10aa97b23ad..11ccd60e566c 100644 --- a/services/java/com/android/server/pm/Installer.java +++ b/services/java/com/android/server/pm/Installer.java @@ -307,7 +307,7 @@ class Installer { } public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, - PackageStats pStats) { + String asecPath, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); @@ -315,17 +315,20 @@ class Installer { builder.append(apkPath); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); + builder.append(' '); + builder.append(asecPath != null ? asecPath : "!"); String s = transaction(builder.toString()); String res[] = s.split(" "); - if ((res == null) || (res.length != 4)) { + if ((res == null) || (res.length != 5)) { return -1; } try { pStats.codeSize = Long.parseLong(res[1]); pStats.dataSize = Long.parseLong(res[2]); pStats.cacheSize = Long.parseLong(res[3]); + pStats.externalCodeSize = Long.parseLong(res[4]); return Integer.parseInt(res[0]); } catch (NumberFormatException e) { return -1; diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 5a9dae9d0ab6..22e2ddeb78a8 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -75,6 +75,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileObserver; import android.os.FileUtils; +import android.os.FileUtils.FileStatus; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -4887,8 +4888,7 @@ public class PackageManagerService extends IPackageManager.Stub { private final IPackageStatsObserver mObserver; - public MeasureParams(PackageStats stats, boolean success, - IPackageStatsObserver observer) { + public MeasureParams(PackageStats stats, boolean success, IPackageStatsObserver observer) { mObserver = observer; mStats = stats; mSuccess = success; @@ -5480,6 +5480,17 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** + * Extract the MountService "container ID" from the full code path of an + * .apk. + */ + static String cidFromCodePath(String fullCodePath) { + int eidx = fullCodePath.lastIndexOf("/"); + String subStr1 = fullCodePath.substring(0, eidx); + int sidx = subStr1.lastIndexOf("/"); + return subStr1.substring(sidx+1, eidx); + } + class SdInstallArgs extends InstallArgs { static final String RES_FILE_NAME = "pkg.apk"; @@ -6831,6 +6842,7 @@ public class PackageManagerService extends IPackageManager.Stub { } PackageParser.Package p; boolean dataOnly = false; + String asecPath = null; synchronized (mPackages) { p = mPackages.get(packageName); if(p == null) { @@ -6842,6 +6854,12 @@ public class PackageManagerService extends IPackageManager.Stub { } p = ps.pkg; } + if (p != null && isExternal(p)) { + String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir); + if (secureContainerId != null) { + asecPath = PackageHelper.getSdFilesystem(secureContainerId); + } + } } String publicSrcDir = null; if(!dataOnly) { @@ -6850,10 +6868,13 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } - publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null; + if (isForwardLocked(p)) { + publicSrcDir = applicationInfo.publicSourceDir; + } } if (mInstaller != null) { - int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, pStats); + int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, + asecPath, pStats); if (res < 0) { return false; } else { diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp index 6d4ad9ac4a88..87ffcba240da 100755 --- a/services/jni/com_android_server_location_GpsLocationProvider.cpp +++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp @@ -42,6 +42,7 @@ static jmethodID method_xtraDownloadRequest; static jmethodID method_reportNiNotification; static jmethodID method_requestRefLocation; static jmethodID method_requestSetID; +static jmethodID method_requestUtcTime; static const GpsInterface* sGpsInterface = NULL; static const GpsXtraInterface* sGpsXtraInterface = NULL; @@ -122,6 +123,13 @@ static void release_wakelock_callback() release_wake_lock(WAKE_LOCK_NAME); } +static void request_utc_time_callback() +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg) { return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg); @@ -137,6 +145,7 @@ GpsCallbacks sGpsCallbacks = { acquire_wakelock_callback, release_wakelock_callback, create_thread_callback, + request_utc_time_callback, }; static void xtra_download_request_callback() @@ -232,6 +241,7 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V"); method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V"); method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V"); + method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V"); err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); if (err == 0) { diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 2bab6a8fe18b..35e29a6315a8 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -101,9 +101,8 @@ Layer::~Layer() } void Layer::onFrameQueued() { - if (android_atomic_or(1, &mQueuedFrames) == 0) { - mFlinger->signalEvent(); - } + android_atomic_inc(&mQueuedFrames); + mFlinger->signalEvent(); } // called with SurfaceFlinger::mStateLock as soon as the layer is entered @@ -406,20 +405,18 @@ bool Layer::isCropped() const { void Layer::lockPageFlip(bool& recomputeVisibleRegions) { - if (android_atomic_and(0, &mQueuedFrames)) { + if (mQueuedFrames > 0) { + // signal another event if we have more frames pending + if (android_atomic_dec(&mQueuedFrames) > 1) { + mFlinger->signalEvent(); + } + if (mSurfaceTexture->updateTexImage() < NO_ERROR) { // something happened! recomputeVisibleRegions = true; return; } - // signal another event if we have more frames waiting - if (mSurfaceTexture->getQueuedCount()) { - if (android_atomic_or(1, &mQueuedFrames) == 0) { - mFlinger->signalEvent(); - } - } - mActiveBuffer = mSurfaceTexture->getCurrentBuffer(); mSurfaceTexture->getTransformMatrix(mTextureMatrix); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index f0b19f26e93e..1c57bc1a5476 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -78,7 +78,6 @@ const String16 sDump("android.permission.DUMP"); SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), Thread(false), mTransactionFlags(0), - mTransactionCount(0), mResizeTransationPending(false), mLayersRemoved(false), mBootTime(systemTime()), @@ -385,13 +384,11 @@ bool SurfaceFlinger::threadLoop() handleConsoleEvents(); } - if (LIKELY(mTransactionCount == 0)) { - // if we're in a global transaction, don't do anything. - const uint32_t mask = eTransactionNeeded | eTraversalNeeded; - uint32_t transactionFlags = peekTransactionFlags(mask); - if (LIKELY(transactionFlags)) { - handleTransaction(transactionFlags); - } + // if we're in a global transaction, don't do anything. + const uint32_t mask = eTransactionNeeded | eTraversalNeeded; + uint32_t transactionFlags = peekTransactionFlags(mask); + if (UNLIKELY(transactionFlags)) { + handleTransaction(transactionFlags); } // post surfaces (if needed) @@ -1176,28 +1173,33 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) return old; } -void SurfaceFlinger::openGlobalTransaction() -{ - android_atomic_inc(&mTransactionCount); -} -void SurfaceFlinger::closeGlobalTransaction() -{ - if (android_atomic_dec(&mTransactionCount) == 1) { - signalEvent(); +void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state) { + Mutex::Autolock _l(mStateLock); - // if there is a transaction with a resize, wait for it to - // take effect before returning. - Mutex::Autolock _l(mStateLock); - while (mResizeTransationPending) { - status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); - if (CC_UNLIKELY(err != NO_ERROR)) { - // just in case something goes wrong in SF, return to the - // called after a few seconds. - LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); - mResizeTransationPending = false; - break; - } + uint32_t flags = 0; + const size_t count = state.size(); + for (size_t i=0 ; i<count ; i++) { + const ComposerState& s(state[i]); + sp<Client> client( static_cast<Client *>(s.client.get()) ); + flags |= setClientStateLocked(client, s.state); + } + if (flags) { + setTransactionFlags(flags); + } + + signalEvent(); + + // if there is a transaction with a resize, wait for it to + // take effect before returning. + while (mResizeTransationPending) { + status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); + if (CC_UNLIKELY(err != NO_ERROR)) { + // just in case something goes wrong in SF, return to the + // called after a few seconds. + LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); + mResizeTransationPending = false; + break; } } } @@ -1393,60 +1395,52 @@ status_t SurfaceFlinger::destroySurface(const wp<LayerBaseClient>& layer) return err; } -status_t SurfaceFlinger::setClientState( +uint32_t SurfaceFlinger::setClientStateLocked( const sp<Client>& client, - int32_t count, - const layer_state_t* states) + const layer_state_t& s) { - Mutex::Autolock _l(mStateLock); uint32_t flags = 0; - for (int i=0 ; i<count ; i++) { - const layer_state_t& s(states[i]); - sp<LayerBaseClient> layer(client->getLayerUser(s.surface)); - if (layer != 0) { - const uint32_t what = s.what; - if (what & ePositionChanged) { - if (layer->setPosition(s.x, s.y)) - flags |= eTraversalNeeded; - } - if (what & eLayerChanged) { - ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); - if (layer->setLayer(s.z)) { - mCurrentState.layersSortedByZ.removeAt(idx); - mCurrentState.layersSortedByZ.add(layer); - // we need traversal (state changed) - // AND transaction (list changed) - flags |= eTransactionNeeded|eTraversalNeeded; - } - } - if (what & eSizeChanged) { - if (layer->setSize(s.w, s.h)) { - flags |= eTraversalNeeded; - mResizeTransationPending = true; - } - } - if (what & eAlphaChanged) { - if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f))) - flags |= eTraversalNeeded; - } - if (what & eMatrixChanged) { - if (layer->setMatrix(s.matrix)) - flags |= eTraversalNeeded; - } - if (what & eTransparentRegionChanged) { - if (layer->setTransparentRegionHint(s.transparentRegion)) - flags |= eTraversalNeeded; + sp<LayerBaseClient> layer(client->getLayerUser(s.surface)); + if (layer != 0) { + const uint32_t what = s.what; + if (what & ePositionChanged) { + if (layer->setPosition(s.x, s.y)) + flags |= eTraversalNeeded; + } + if (what & eLayerChanged) { + ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); + if (layer->setLayer(s.z)) { + mCurrentState.layersSortedByZ.removeAt(idx); + mCurrentState.layersSortedByZ.add(layer); + // we need traversal (state changed) + // AND transaction (list changed) + flags |= eTransactionNeeded|eTraversalNeeded; } - if (what & eVisibilityChanged) { - if (layer->setFlags(s.flags, s.mask)) - flags |= eTraversalNeeded; + } + if (what & eSizeChanged) { + if (layer->setSize(s.w, s.h)) { + flags |= eTraversalNeeded; + mResizeTransationPending = true; } } + if (what & eAlphaChanged) { + if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f))) + flags |= eTraversalNeeded; + } + if (what & eMatrixChanged) { + if (layer->setMatrix(s.matrix)) + flags |= eTraversalNeeded; + } + if (what & eTransparentRegionChanged) { + if (layer->setTransparentRegionHint(s.transparentRegion)) + flags |= eTraversalNeeded; + } + if (what & eVisibilityChanged) { + if (layer->setFlags(s.flags, s.mask)) + flags |= eTraversalNeeded; + } } - if (flags) { - setTransactionFlags(flags); - } - return NO_ERROR; + return flags; } void SurfaceFlinger::screenReleased(int dpy) @@ -1588,8 +1582,7 @@ status_t SurfaceFlinger::onTransact( { switch (code) { case CREATE_CONNECTION: - case OPEN_GLOBAL_TRANSACTION: - case CLOSE_GLOBAL_TRANSACTION: + case SET_TRANSACTION_STATE: case SET_ORIENTATION: case FREEZE_DISPLAY: case UNFREEZE_DISPLAY: @@ -2469,9 +2462,6 @@ sp<ISurface> Client::createSurface( status_t Client::destroySurface(SurfaceID sid) { return mFlinger->removeSurface(this, sid); } -status_t Client::setState(int32_t count, const layer_state_t* states) { - return mFlinger->setClientState(this, count, states); -} // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 45f80aedc24a..b49fa363e62f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -70,14 +70,12 @@ public: sp<LayerBaseClient> getLayerUser(int32_t i) const; private: - // ISurfaceComposerClient interface virtual sp<ISurface> createSurface( surface_data_t* params, const String8& name, DisplayID display, uint32_t w, uint32_t h,PixelFormat format, uint32_t flags); virtual status_t destroySurface(SurfaceID surfaceId); - virtual status_t setState(int32_t count, const layer_state_t* states); virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); @@ -168,8 +166,7 @@ public: virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc(); virtual sp<IMemoryHeap> getCblk() const; virtual void bootFinished(); - virtual void openGlobalTransaction(); - virtual void closeGlobalTransaction(); + virtual void setTransactionState(const Vector<ComposerState>& state); virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags); virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags); virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags); @@ -220,8 +217,7 @@ private: status_t removeSurface(const sp<Client>& client, SurfaceID sid); status_t destroySurface(const wp<LayerBaseClient>& layer); - status_t setClientState(const sp<Client>& client, - int32_t count, const layer_state_t* states); + uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s); class LayerVector : public SortedVector< sp<LayerBase> > { public: @@ -337,7 +333,6 @@ private: mutable Mutex mStateLock; State mCurrentState; volatile int32_t mTransactionFlags; - volatile int32_t mTransactionCount; Condition mTransactionCV; SortedVector< sp<LayerBase> > mLayerPurgatory; bool mResizeTransationPending; diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp index 18c54b3f8419..56b2a8f1cb03 100644 --- a/services/surfaceflinger/tests/resize/resize.cpp +++ b/services/surfaceflinger/tests/resize/resize.cpp @@ -43,9 +43,9 @@ int main(int argc, char** argv) PIXEL_FORMAT_RGB_565); - client->openTransaction(); + SurfaceComposerClient::openGlobalTransaction(); surface->setLayer(100000); - client->closeTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); Surface::SurfaceInfo info; surface->lock(&info); @@ -57,9 +57,9 @@ int main(int argc, char** argv) android_memset16((uint16_t*)info.bits, 0x07E0, bpr*info.h); surface->unlockAndPost(); - client->openTransaction(); + SurfaceComposerClient::openGlobalTransaction(); surface->setSize(320, 240); - client->closeTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); IPCThreadState::self()->joinThreadPool(); diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp index 5265f919536b..8e1c3fe322b4 100644 --- a/services/surfaceflinger/tests/surface/surface.cpp +++ b/services/surfaceflinger/tests/surface/surface.cpp @@ -39,9 +39,9 @@ int main(int argc, char** argv) sp<SurfaceControl> surfaceControl = client->createSurface( getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565); - client->openTransaction(); + SurfaceComposerClient::openGlobalTransaction(); surfaceControl->setLayer(100000); - client->closeTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); // pretend it went cross-process Parcel parcel; diff --git a/services/tests/servicestests/res/raw/xt_qtaguid_extended b/services/tests/servicestests/res/raw/xt_qtaguid_extended new file mode 100644 index 000000000000..5bef3dd75216 --- /dev/null +++ b/services/tests/servicestests/res/raw/xt_qtaguid_extended @@ -0,0 +1,3 @@ +acct_tag_hex uid_tag_int iface rx_bytes rx_packets tx_bytes tx_packets teleported_goats +0x0 1000 test0 1024 10 2048 20 2716057 +0x0000F00D00000000 1000 test0 512 5 512 5 3370318 diff --git a/services/tests/servicestests/res/raw/xt_qtaguid_typical b/services/tests/servicestests/res/raw/xt_qtaguid_typical new file mode 100644 index 000000000000..7c4f04e1fb3a --- /dev/null +++ b/services/tests/servicestests/res/raw/xt_qtaguid_typical @@ -0,0 +1,32 @@ +idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes +1 wlan0 0x0 0 14615 4270 +2 wlan0 0x0 1000 5175 915 +3 wlan0 0x0 1021 3381 903 +4 wlan0 0x0 10004 333821 53558 +5 wlan0 0x0 10010 4888 37363 +6 wlan0 0x0 10013 52 104 +7 wlan0 0x74182ada00000000 10004 18725 1066 +8 rmnet0 0x0 0 301274 30244 +9 rmnet0 0x0 1000 304 441 +10 rmnet0 0x0 1013 2880 2272 +11 rmnet0 0x0 1021 31407 8430 +12 rmnet0 0x0 10003 32665 3814 +13 rmnet0 0x0 10004 2373141 420112 +14 rmnet0 0x0 10010 870370 1111727 +15 rmnet0 0x0 10013 240 240 +16 rmnet0 0x0 10016 16703 13512 +17 rmnet0 0x0 10017 3990 3269 +18 rmnet0 0x0 10018 474504 14516062 +19 rmnet0 0x0 10019 782804 71077 +20 rmnet0 0x0 10022 70671 49684 +21 rmnet0 0x0 10029 5785354 397159 +22 rmnet0 0x0 10033 2102 1686 +23 rmnet0 0x0 10034 15495464 227694 +24 rmnet0 0x0 10037 31184994 684122 +25 rmnet0 0x0 10051 298687 113485 +26 rmnet0 0x0 10056 29504 20669 +27 rmnet0 0x0 10069 683 596 +28 rmnet0 0x0 10072 34051 12453 +29 rmnet0 0x0 10077 7025393 213866 +30 rmnet0 0x0 10081 354 1178 +31 rmnet0 0x74182ada00000000 10037 28507378 437004 diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java new file mode 100644 index 000000000000..ac7cb5afd53a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2011 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.server; + +import static com.android.server.NetworkManagementSocketTagger.kernelToTag; +import static com.android.server.NetworkManagementSocketTagger.tagToKernel; + +import android.content.res.Resources; +import android.net.NetworkStats; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; + +import com.android.frameworks.servicestests.R; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import libcore.io.IoUtils; +import libcore.io.Streams; + +/** + * Tests for {@link NetworkManagementService}. + */ +@LargeTest +public class NetworkManagementServiceTest extends AndroidTestCase { + private File mTestProc; + private NetworkManagementService mService; + + @Override + public void setUp() throws Exception { + super.setUp(); + + mTestProc = getContext().getFilesDir(); + mService = NetworkManagementService.createForTest(mContext, mTestProc); + } + + @Override + public void tearDown() throws Exception { + mService = null; + + super.tearDown(); + } + + public void testNetworkStatsDetail() throws Exception { + stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats")); + + final NetworkStats stats = mService.getNetworkStatsDetail(); + assertEquals(31, stats.size); + assertStatsEntry(stats, "wlan0", 0, 0, 14615L, 4270L); + assertStatsEntry(stats, "wlan0", 10004, 0, 333821L, 53558L); + assertStatsEntry(stats, "wlan0", 10004, 1947740890, 18725L, 1066L); + assertStatsEntry(stats, "rmnet0", 10037, 0, 31184994L, 684122L); + assertStatsEntry(stats, "rmnet0", 10037, 1947740890, 28507378L, 437004L); + } + + public void testNetworkStatsDetailExtended() throws Exception { + stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats")); + + final NetworkStats stats = mService.getNetworkStatsDetail(); + assertEquals(2, stats.size); + assertStatsEntry(stats, "test0", 1000, 0, 1024L, 2048L); + assertStatsEntry(stats, "test0", 1000, 0xF00D, 512L, 512L); + } + + public void testKernelTags() throws Exception { + assertEquals("0", tagToKernel(0x0)); + assertEquals("214748364800", tagToKernel(0x32)); + assertEquals("9223372032559808512", tagToKernel(Integer.MAX_VALUE)); + assertEquals("0", tagToKernel(Integer.MIN_VALUE)); + assertEquals("9223369837831520256", tagToKernel(Integer.MIN_VALUE - 512)); + + assertEquals(0, kernelToTag("0x0000000000000000")); + assertEquals(0x32, kernelToTag("0x0000003200000000")); + assertEquals(2147483647, kernelToTag("0x7fffffff00000000")); + assertEquals(0, kernelToTag("0x0000000000000000")); + assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000")); + + } + + /** + * Copy a {@link Resources#openRawResource(int)} into {@link File} for + * testing purposes. + */ + private void stageFile(int rawId, File file) throws Exception { + new File(file.getParent()).mkdirs(); + InputStream in = null; + OutputStream out = null; + try { + in = getContext().getResources().openRawResource(rawId); + out = new FileOutputStream(file); + Streams.copy(in, out); + } finally { + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(out); + } + } + + private static void assertStatsEntry( + NetworkStats stats, String iface, int uid, int tag, long rx, long tx) { + final int i = stats.findIndex(iface, uid, tag); + assertEquals(rx, stats.rx[i]); + assertEquals(tx, stats.tx[i]); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 07e54250fed8..b4ac987fc541 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -41,7 +41,9 @@ import android.app.IActivityManager; import android.app.INotificationManager; import android.app.IProcessObserver; import android.content.Intent; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.Signature; import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.INetworkPolicyListener; @@ -54,6 +56,7 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkTemplate; import android.os.Binder; +import android.os.INetworkManagementService; import android.os.IPowerManager; import android.test.AndroidTestCase; import android.test.mock.MockPackageManager; @@ -63,12 +66,17 @@ import android.text.format.Time; import android.util.TrustedTime; import com.android.server.net.NetworkPolicyManagerService; +import com.google.common.util.concurrent.AbstractFuture; import org.easymock.Capture; import org.easymock.EasyMock; +import org.easymock.IAnswer; import java.io.File; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * Tests for {@link NetworkPolicyManagerService}. @@ -88,6 +96,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private IActivityManager mActivityManager; private IPowerManager mPowerManager; private INetworkStatsService mStatsService; + private INetworkManagementService mNetworkManagement; private INetworkPolicyListener mPolicyListener; private TrustedTime mTime; private IConnectivityManager mConnManager; @@ -118,22 +127,40 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { public String[] getPackagesForUid(int uid) { return new String[] { "com.example" }; } + + @Override + public PackageInfo getPackageInfo(String packageName, int flags) { + final PackageInfo info = new PackageInfo(); + final Signature signature; + if ("android".equals(packageName)) { + signature = new Signature("F00D"); + } else { + signature = new Signature("DEAD"); + } + info.signatures = new Signature[] { signature }; + return info; + } }; } }; mPolicyDir = getContext().getFilesDir(); + for (File file : mPolicyDir.listFiles()) { + file.delete(); + } mActivityManager = createMock(IActivityManager.class); mPowerManager = createMock(IPowerManager.class); mStatsService = createMock(INetworkStatsService.class); + mNetworkManagement = createMock(INetworkManagementService.class); mPolicyListener = createMock(INetworkPolicyListener.class); mTime = createMock(TrustedTime.class); mConnManager = createMock(IConnectivityManager.class); mNotifManager = createMock(INotificationManager.class); mService = new NetworkPolicyManagerService( - mServiceContext, mActivityManager, mPowerManager, mStatsService, mTime, mPolicyDir); + mServiceContext, mActivityManager, mPowerManager, mStatsService, + mNetworkManagement, mTime, mPolicyDir); mService.bindConnectivityManager(mConnManager); mService.bindNotificationManager(mNotifManager); @@ -152,6 +179,9 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce(); expectTime(System.currentTimeMillis()); + // default behavior is background data enabled + expect(mConnManager.getBackgroundDataSetting()).andReturn(true); + replay(); mService.systemReady(); verifyAndReset(); @@ -228,81 +258,123 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } public void testScreenChangesRules() throws Exception { - // push strict policy for foreground uid, verify ALLOW rule - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + Future<Void> future; + + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); + future.get(); + verifyAndReset(); + + // push strict policy for foreground uid, verify ALLOW rule + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); + replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); + future.get(); verifyAndReset(); // now turn screen off and verify REJECT rule expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce(); - expectRulesChanged(UID_A, RULE_REJECT_METERED); + expectSetUidNetworkRules(UID_A, true); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF)); + future.get(); verifyAndReset(); // and turn screen back on, verify ALLOW rule restored expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce(); - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON)); + future.get(); verifyAndReset(); } public void testPolicyNone() throws Exception { + Future<Void> future; + + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); + replay(); + mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); + future.get(); + verifyAndReset(); + // POLICY_NONE should RULE_ALLOW in foreground - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mService.setUidPolicy(UID_A, POLICY_NONE); - mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); + future.get(); verifyAndReset(); // POLICY_NONE should RULE_ALLOW in background - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); + future.get(); verifyAndReset(); } public void testPolicyReject() throws Exception { + Future<Void> future; + // POLICY_REJECT should RULE_ALLOW in background - expectRulesChanged(UID_A, RULE_REJECT_METERED); + expectSetUidNetworkRules(UID_A, true); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); + future.get(); verifyAndReset(); // POLICY_REJECT should RULE_ALLOW in foreground - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); + future.get(); verifyAndReset(); // POLICY_REJECT should RULE_REJECT in background - expectRulesChanged(UID_A, RULE_REJECT_METERED); + expectSetUidNetworkRules(UID_A, true); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); + future.get(); verifyAndReset(); } public void testPolicyRejectAddRemove() throws Exception { + Future<Void> future; + // POLICY_NONE should have RULE_ALLOW in background - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); - mService.setUidPolicy(UID_A, POLICY_NONE); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); + mService.setUidPolicy(UID_A, POLICY_NONE); + future.get(); verifyAndReset(); // adding POLICY_REJECT should cause RULE_REJECT - expectRulesChanged(UID_A, RULE_REJECT_METERED); + expectSetUidNetworkRules(UID_A, true); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); + future.get(); verifyAndReset(); // removing POLICY_REJECT should return us to RULE_ALLOW - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mService.setUidPolicy(UID_A, POLICY_NONE); + future.get(); verifyAndReset(); } @@ -350,6 +422,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { long elapsedRealtime = 0; NetworkState[] state = null; NetworkStats stats = null; + Future<Void> future; final long TIME_FEB_15 = 1171497600000L; final long TIME_MAR_10 = 1173484800000L; @@ -360,10 +433,11 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { state = new NetworkState[] { buildWifi() }; expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); expectTime(TIME_MAR_10 + elapsedRealtime); - expectMeteredIfacesChanged(); + future = expectMeteredIfacesChanged(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + future.get(); verifyAndReset(); // now change cycle to be on 15th, and test in early march, to verify we @@ -377,30 +451,38 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10)) .andReturn(stats).atLeastOnce(); - // expect that quota remaining should be 1536 bytes - // TODO: write up NetworkManagementService mock + // TODO: consider making strongly ordered mock + expectRemoveInterfaceQuota(TEST_IFACE); + expectSetInterfaceQuota(TEST_IFACE, 1536L); expectClearNotifications(); - expectMeteredIfacesChanged(TEST_IFACE); + future = expectMeteredIfacesChanged(TEST_IFACE); replay(); setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L)); + future.get(); verifyAndReset(); } public void testUidRemovedPolicyCleared() throws Exception { + Future<Void> future; + // POLICY_REJECT should RULE_REJECT in background - expectRulesChanged(UID_A, RULE_REJECT_METERED); + expectSetUidNetworkRules(UID_A, true); + future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); + future.get(); verifyAndReset(); // uninstall should clear RULE_REJECT - expectRulesChanged(UID_A, RULE_ALLOW_ALL); + expectSetUidNetworkRules(UID_A, false); + future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); final Intent intent = new Intent(ACTION_UID_REMOVED); intent.putExtra(EXTRA_UID, UID_A); mServiceContext.sendBroadcast(intent); + future.get(); verifyAndReset(); } @@ -435,25 +517,62 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expectLastCall().anyTimes(); } - private void expectRulesChanged(int uid, int policy) throws Exception { - mPolicyListener.onUidRulesChanged(eq(uid), eq(policy)); + private void expectSetInterfaceQuota(String iface, long quota) throws Exception { + mNetworkManagement.setInterfaceQuota(iface, quota); expectLastCall().atLeastOnce(); } - private void expectMeteredIfacesChanged(String... ifaces) throws Exception { - mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces)); + private void expectRemoveInterfaceQuota(String iface) throws Exception { + mNetworkManagement.removeInterfaceQuota(iface); expectLastCall().atLeastOnce(); } + private void expectSetUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) + throws Exception { + mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces); + expectLastCall().atLeastOnce(); + } + + private Future<Void> expectRulesChanged(int uid, int policy) throws Exception { + final FutureAnswer future = new FutureAnswer(); + mPolicyListener.onUidRulesChanged(eq(uid), eq(policy)); + expectLastCall().andAnswer(future); + return future; + } + + private Future<Void> expectMeteredIfacesChanged(String... ifaces) throws Exception { + final FutureAnswer future = new FutureAnswer(); + mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces)); + expectLastCall().andAnswer(future); + return future; + } + + private static class FutureAnswer extends AbstractFuture<Void> implements IAnswer<Void> { + @Override + public Void get() throws InterruptedException, ExecutionException { + try { + return get(5, TimeUnit.SECONDS); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + + @Override + public Void answer() { + set(null); + return null; + } + } + private void replay() { - EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime, - mConnManager, mNotifManager); + EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener, + mNetworkManagement, mTime, mConnManager, mNotifManager); } private void verifyAndReset() { - EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime, - mConnManager, mNotifManager); - EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime, - mConnManager, mNotifManager); + EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener, + mNetworkManagement, mTime, mConnManager, mNotifManager); + EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener, + mNetworkManagement, mTime, mConnManager, mNotifManager); } } diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index 636d0593e9f8..903f2b0b6f66 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -59,7 +59,6 @@ import android.os.INetworkManagementService; import android.telephony.TelephonyManager; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; -import android.util.Log; import android.util.TrustedTime; import com.android.server.net.NetworkStatsService; @@ -611,6 +610,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mAlarmManager.setInexactRepeating( eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class)); expectLastCall().atLeastOnce(); + + mNetManager.setBandwidthControlEnabled(true); + expectLastCall().atLeastOnce(); } private void expectNetworkState(NetworkState... state) throws Exception { @@ -631,6 +633,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory) throws Exception { + expect(mSettings.getEnabled()).andReturn(true).anyTimes(); expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes(); expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes(); expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes(); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 184d665d0579..66120a171dd9 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -191,10 +191,6 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ public String getDeviceId() { - if (!isVoiceCapable()) { - return null; - } - try { return getSubscriberInfo().getDeviceId(); } catch (RemoteException ex) { diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java index c4a6f53dc34a..6e9d0f9da62d 100644 --- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java @@ -502,16 +502,17 @@ public class PhoneNumberUtilsTest extends AndroidTestCase { @SmallTest public void testFormatNumberToE164() { - assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "us")); - assertNull(PhoneNumberUtils.formatNumberToE164("1234567", "us")); - assertEquals("+18004664114", PhoneNumberUtils.formatNumberToE164("800-GOOG-114", "us")); + // Note: ISO 3166-1 only allows upper case country codes. + assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "US")); + assertNull(PhoneNumberUtils.formatNumberToE164("1234567", "US")); + assertEquals("+18004664114", PhoneNumberUtils.formatNumberToE164("800-GOOG-114", "US")); } @SmallTest public void testFormatNumber() { - assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "us")); - assertEquals("123-4567", PhoneNumberUtils.formatNumber("1234567", "us")); - assertEquals("(800) 466-4114", PhoneNumberUtils.formatNumber("800-GOOG-114", "us")); + assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "US")); + assertEquals("123-4567", PhoneNumberUtils.formatNumber("1234567", "US")); + assertEquals("(800) 466-4114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US")); } diff --git a/tests/BiDiTests/res/layout/textview_direction_ltr.xml b/tests/BiDiTests/res/layout/textview_direction_ltr.xml new file mode 100644 index 000000000000..f7b7b8e26564 --- /dev/null +++ b/tests/BiDiTests/res/layout/textview_direction_ltr.xml @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/textview_direction_ltr" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layoutDirection="ltr"> + + <LinearLayout android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textDirection="ltr"> + + <LinearLayout android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="inherit" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="firstStrong" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="anyRtl" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="ltr" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="rtl" + /> + </LinearLayout> + + <LinearLayout android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="inherit" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="firstStrong" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="anyRtl" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="ltr" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="rtl" + /> + </LinearLayout> + + </LinearLayout> + +</FrameLayout>
\ No newline at end of file diff --git a/tests/BiDiTests/res/layout/textview_direction_rtl.xml b/tests/BiDiTests/res/layout/textview_direction_rtl.xml new file mode 100644 index 000000000000..81c5411ef574 --- /dev/null +++ b/tests/BiDiTests/res/layout/textview_direction_rtl.xml @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/textview_direction_rtl" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layoutDirection="rtl"> + + <LinearLayout android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textDirection="rtl"> + + <LinearLayout android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="inherit" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="firstStrong" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="anyRtl" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="ltr" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_text" + android:textDirection="rtl" + /> + </LinearLayout> + + <LinearLayout android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="inherit" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="firstStrong" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="anyRtl" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="ltr" + /> + <TextView android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="24dip" + android:text="@string/textview_hebrew_text" + android:textDirection="rtl" + /> + </LinearLayout> + + </LinearLayout> + +</FrameLayout>
\ No newline at end of file diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java index b1e494a6c454..c0338796400e 100644 --- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java @@ -122,9 +122,6 @@ public class BiDiTestActivity extends Activity { addItem(result, "Table RTL", BiDiTestTableLayoutRtl.class, R.id.table_layout_rtl); addItem(result, "Table LOC", BiDiTestTableLayoutLocale.class, R.id.table_layout_locale); - addItem(result, "ViewPadding", BiDiTestViewPadding.class, R.id.view_padding); - addItem(result, "ViewPadding MIXED", BiDiTestViewPaddingMixed.class, R.id.view_padding_mixed); - addItem(result, "Padding", BiDiTestViewPadding.class, R.id.view_padding); addItem(result, "Padding MIXED", BiDiTestViewPaddingMixed.class, R.id.view_padding_mixed); @@ -134,6 +131,9 @@ public class BiDiTestActivity extends Activity { addItem(result, "TextView RTL", BiDiTestTextViewRtl.class, R.id.textview_rtl); addItem(result, "TextView LOC", BiDiTestTextViewLocale.class, R.id.textview_locale); + addItem(result, "TextDirection LTR", BiDiTestTextViewDirectionLtr.class, R.id.textview_direction_ltr); + addItem(result, "TextDirection RTL", BiDiTestTextViewDirectionRtl.class, R.id.textview_direction_rtl); + return result; } diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionLtr.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionLtr.java new file mode 100644 index 000000000000..882ed1f963ec --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionLtr.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 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.bidi; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +public class BiDiTestTextViewDirectionLtr extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.textview_direction_ltr, container, false); + } +} diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionRtl.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionRtl.java new file mode 100644 index 000000000000..e63ee3585d39 --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionRtl.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 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.bidi; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +public class BiDiTestTextViewDirectionRtl extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.textview_direction_rtl, container, false); + } +} diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 7aa0617a702a..873414338fd3 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -31,6 +31,15 @@ android:hardwareAccelerated="true"> <activity + android:name="TimeDialogActivity" + android:label="_TimeDialog"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name="OpaqueActivity" android:label="_Opaque"> <intent-filter> @@ -494,8 +503,7 @@ <activity android:name="Animated3dActivity" - android:label="_Animated 3d" - android:theme="@android:style/Theme.Translucent.NoTitleBar"> + android:label="_Animated 3d"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java new file mode 100644 index 000000000000..9e3e950f0850 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 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.test.hwui; + +import android.app.Activity; +import android.app.TimePickerDialog; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.widget.Button; +import android.widget.FrameLayout; + +@SuppressWarnings({"UnusedDeclaration"}) +public class TimeDialogActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FrameLayout layout = new FrameLayout(this); + Button b = new Button(this); + b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); + b.setText("Show dialog"); + b.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + new TimePickerDialog(TimeDialogActivity.this, null, 12, 12, true).show(); + } + }); + layout.addView(b); + + setContentView(layout); + } +} diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java index 47950e30afa8..c031bc147edd 100644 --- a/voip/java/com/android/server/sip/SipHelper.java +++ b/voip/java/com/android/server/sip/SipHelper.java @@ -150,9 +150,17 @@ class SipHelper { private ContactHeader createContactHeader(SipProfile profile) throws ParseException, SipException { - ListeningPoint lp = getListeningPoint(); - SipURI contactURI = - createSipUri(profile.getUserName(), profile.getProtocol(), lp); + return createContactHeader(profile, null, 0); + } + + private ContactHeader createContactHeader(SipProfile profile, + String ip, int port) throws ParseException, + SipException { + SipURI contactURI = (ip == null) + ? createSipUri(profile.getUserName(), profile.getProtocol(), + getListeningPoint()) + : createSipUri(profile.getUserName(), profile.getProtocol(), + ip, port); Address contactAddress = mAddressFactory.createAddress(contactURI); contactAddress.setDisplayName(profile.getDisplayName()); @@ -168,9 +176,14 @@ class SipHelper { private SipURI createSipUri(String username, String transport, ListeningPoint lp) throws ParseException { - SipURI uri = mAddressFactory.createSipURI(username, lp.getIPAddress()); + return createSipUri(username, transport, lp.getIPAddress(), lp.getPort()); + } + + private SipURI createSipUri(String username, String transport, + String ip, int port) throws ParseException { + SipURI uri = mAddressFactory.createSipURI(username, ip); try { - uri.setPort(lp.getPort()); + uri.setPort(port); uri.setTransportParam(transport); } catch (InvalidArgumentException e) { throw new RuntimeException(e); @@ -353,13 +366,14 @@ class SipHelper { */ public ServerTransaction sendInviteOk(RequestEvent event, SipProfile localProfile, String sessionDescription, - ServerTransaction inviteTransaction) - throws SipException { + ServerTransaction inviteTransaction, String externalIp, + int externalPort) throws SipException { try { Request request = event.getRequest(); Response response = mMessageFactory.createResponse(Response.OK, request); - response.addHeader(createContactHeader(localProfile)); + response.addHeader(createContactHeader(localProfile, externalIp, + externalPort)); response.setContent(sessionDescription, mHeaderFactory.createContentTypeHeader( "application", "sdp")); diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java index 3b0f54606233..47863bd46dfe 100644 --- a/voip/java/com/android/server/sip/SipService.java +++ b/voip/java/com/android/server/sip/SipService.java @@ -756,21 +756,20 @@ public final class SipService extends ISipService.Stub { } } - private class IntervalMeasurementProcess implements + private class IntervalMeasurementProcess implements Runnable, SipSessionGroup.KeepAliveProcessCallback { private static final String TAG = "SipKeepAliveInterval"; private static final int MAX_INTERVAL = 120; // in seconds - private static final int MIN_INTERVAL = 10; // in seconds + private static final int MIN_INTERVAL = 5; // in seconds private static final int PASS_THRESHOLD = 10; private static final int MAX_RETRY_COUNT = 5; + private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds private SipSessionGroupExt mGroup; private SipSessionGroup.SipSessionImpl mSession; - private boolean mRunning; - private int mMinInterval = 10; // in seconds + private int mMinInterval = DEFAULT_KEEPALIVE_INTERVAL; // in seconds private int mMaxInterval; private int mInterval; private int mPassCount = 0; - private int mErrorCount = 0; public IntervalMeasurementProcess(SipProfile localProfile, int maxInterval) { mMaxInterval = (maxInterval < 0) ? MAX_INTERVAL : maxInterval; @@ -788,8 +787,6 @@ public final class SipService extends ISipService.Stub { // TODO: remove this line once SipWakeupTimer can better handle // variety of timeout values mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor)); - mSession = (SipSessionGroup.SipSessionImpl) - mGroup.createSession(null); } catch (Exception e) { Log.w(TAG, "start interval measurement error: " + e); } @@ -797,6 +794,11 @@ public final class SipService extends ISipService.Stub { public void start() { synchronized (SipService.this) { + Log.d(TAG, "start measurement w interval=" + mInterval); + if (mSession == null) { + mSession = (SipSessionGroup.SipSessionImpl) + mGroup.createSession(null); + } try { mSession.startKeepAliveProcess(mInterval, this); } catch (SipException e) { @@ -807,14 +809,23 @@ public final class SipService extends ISipService.Stub { public void stop() { synchronized (SipService.this) { - mSession.stopKeepAliveProcess(); + if (mSession != null) { + mSession.stopKeepAliveProcess(); + mSession = null; + } + mTimer.cancel(this); } } private void restart() { synchronized (SipService.this) { + // Return immediately if the measurement process is stopped + if (mSession == null) return; + + Log.d(TAG, "restart measurement w interval=" + mInterval); try { mSession.stopKeepAliveProcess(); + mPassCount = 0; mSession.startKeepAliveProcess(mInterval, this); } catch (SipException e) { Log.e(TAG, "restart()", e); @@ -826,8 +837,6 @@ public final class SipService extends ISipService.Stub { @Override public void onResponse(boolean portChanged) { synchronized (SipService.this) { - mErrorCount = 0; - if (!portChanged) { if (++mPassCount != PASS_THRESHOLD) return; // update the interval, since the current interval is good to @@ -853,7 +862,6 @@ public final class SipService extends ISipService.Stub { } else { // calculate the new interval and continue. mInterval = (mMaxInterval + mMinInterval) / 2; - mPassCount = 0; if (DEBUG) { Log.d(TAG, "current interval: " + mKeepAliveInterval + ", test new interval: " + mInterval); @@ -866,22 +874,32 @@ public final class SipService extends ISipService.Stub { // SipSessionGroup.KeepAliveProcessCallback @Override public void onError(int errorCode, String description) { + Log.w(TAG, "interval measurement error: " + description); + restartLater(); + } + + // timeout handler + @Override + public void run() { + mTimer.cancel(this); + restart(); + } + + private void restartLater() { synchronized (SipService.this) { - Log.w(TAG, "interval measurement error: " + description); - if (++mErrorCount < MAX_RETRY_COUNT) { - Log.w(TAG, " retry count = " + mErrorCount); - mPassCount = 0; - restart(); - } else { - Log.w(TAG, " max retry count reached; measurement aborted"); - } + int interval = NAT_MEASUREMENT_RETRY_INTERVAL; + Log.d(TAG, "Retry measurement " + interval + "s later."); + mTimer.cancel(this); + mTimer.set(interval * 1000, this); } } } private class AutoRegistrationProcess extends SipSessionAdapter implements Runnable, SipSessionGroup.KeepAliveProcessCallback { + private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10; private String TAG = "SipAudoReg"; + private SipSessionGroup.SipSessionImpl mSession; private SipSessionGroup.SipSessionImpl mKeepAliveSession; private SipSessionListenerProxy mProxy = new SipSessionListenerProxy(); @@ -892,6 +910,8 @@ public final class SipService extends ISipService.Stub { private String mErrorMessage; private boolean mRunning = false; + private int mKeepAliveSuccessCount = 0; + private String getAction() { return toString(); } @@ -915,18 +935,56 @@ public final class SipService extends ISipService.Stub { } } + private void startKeepAliveProcess(int interval) { + Log.d(TAG, "start keepalive w interval=" + interval); + if (mKeepAliveSession == null) { + mKeepAliveSession = mSession.duplicate(); + } else { + mKeepAliveSession.stopKeepAliveProcess(); + } + try { + mKeepAliveSession.startKeepAliveProcess(interval, this); + } catch (SipException e) { + Log.e(TAG, "failed to start keepalive w interval=" + interval, + e); + } + } + + private void stopKeepAliveProcess() { + if (mKeepAliveSession != null) { + mKeepAliveSession.stopKeepAliveProcess(); + mKeepAliveSession = null; + } + mKeepAliveSuccessCount = 0; + } + // SipSessionGroup.KeepAliveProcessCallback @Override public void onResponse(boolean portChanged) { synchronized (SipService.this) { if (portChanged) { - restartPortMappingLifetimeMeasurement( - mSession.getLocalProfile(), getKeepAliveInterval()); + int interval = getKeepAliveInterval(); + if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) { + Log.i(TAG, "keepalive doesn't work with interval " + + interval + ", past success count=" + + mKeepAliveSuccessCount); + if (interval > DEFAULT_KEEPALIVE_INTERVAL) { + restartPortMappingLifetimeMeasurement( + mSession.getLocalProfile(), interval); + mKeepAliveSuccessCount = 0; + } + } else { + Log.i(TAG, "keep keepalive going with interval " + + interval + ", past success count=" + + mKeepAliveSuccessCount); + mKeepAliveSuccessCount /= 2; + } } else { // Start keep-alive interval measurement on the first // successfully kept-alive SipSessionGroup startPortMappingLifetimeMeasurement( mSession.getLocalProfile()); + mKeepAliveSuccessCount++; } if (!mRunning || !portChanged) return; @@ -960,10 +1018,7 @@ public final class SipService extends ISipService.Stub { } mTimer.cancel(this); - if (mKeepAliveSession != null) { - mKeepAliveSession.stopKeepAliveProcess(); - mKeepAliveSession = null; - } + stopKeepAliveProcess(); mRegistered = false; setListener(mProxy.getListener()); @@ -975,12 +1030,8 @@ public final class SipService extends ISipService.Stub { if (DEBUGV) { Log.v(TAG, "restart keepalive w interval=" + newInterval); } - mKeepAliveSession.stopKeepAliveProcess(); - try { - mKeepAliveSession.startKeepAliveProcess(newInterval, this); - } catch (SipException e) { - Log.e(TAG, "onKeepAliveIntervalChanged()", e); - } + mKeepAliveSuccessCount = 0; + startKeepAliveProcess(newInterval); } } @@ -1105,14 +1156,7 @@ public final class SipService extends ISipService.Stub { SipProfile localProfile = mSession.getLocalProfile(); if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp) || localProfile.getSendKeepAlive())) { - mKeepAliveSession = mSession.duplicate(); - Log.d(TAG, "start keepalive"); - try { - mKeepAliveSession.startKeepAliveProcess( - getKeepAliveInterval(), this); - } catch (SipException e) { - Log.e(TAG, "AutoRegistrationProcess", e); - } + startKeepAliveProcess(getKeepAliveInterval()); } } mMyWakeLock.release(session); diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java index c0c1b28567bf..047eb8df8f3a 100644 --- a/voip/java/com/android/server/sip/SipSessionGroup.java +++ b/voip/java/com/android/server/sip/SipSessionGroup.java @@ -119,6 +119,10 @@ class SipSessionGroup implements SipListener { private Map<String, SipSessionImpl> mSessionMap = new HashMap<String, SipSessionImpl>(); + // external address observed from any response + private String mExternalIp; + private int mExternalPort; + /** * @param myself the local profile with password crossed out * @param password the password of the profile @@ -175,6 +179,8 @@ class SipSessionGroup implements SipListener { mCallReceiverSession = null; mSessionMap.clear(); + + resetExternalAddress(); } synchronized void onConnectivityChanged() { @@ -190,6 +196,12 @@ class SipSessionGroup implements SipListener { } } + synchronized void resetExternalAddress() { + Log.d(TAG, " reset external addr on " + mSipStack); + mExternalIp = null; + mExternalPort = 0; + } + public SipProfile getLocalProfile() { return mLocalProfile; } @@ -363,6 +375,21 @@ class SipSessionGroup implements SipListener { return null; } + private void extractExternalAddress(ResponseEvent evt) { + Response response = evt.getResponse(); + ViaHeader viaHeader = (ViaHeader)(response.getHeader( + SIPHeaderNames.VIA)); + if (viaHeader == null) return; + int rport = viaHeader.getRPort(); + String externalIp = viaHeader.getReceived(); + if ((rport > 0) && (externalIp != null)) { + mExternalIp = externalIp; + mExternalPort = rport; + Log.d(TAG, " got external addr " + externalIp + ":" + rport + + " on " + mSipStack); + } + } + private class SipSessionCallReceiverImpl extends SipSessionImpl { public SipSessionCallReceiverImpl(ISipSessionListener listener) { super(listener); @@ -682,6 +709,7 @@ class SipSessionGroup implements SipListener { dialog = ((RequestEvent) evt).getDialog(); } else if (evt instanceof ResponseEvent) { dialog = ((ResponseEvent) evt).getDialog(); + extractExternalAddress((ResponseEvent) evt); } if (dialog != null) mDialog = dialog; @@ -984,7 +1012,8 @@ class SipSessionGroup implements SipListener { mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived, mLocalProfile, ((MakeCallCommand) evt).getSessionDescription(), - mServerTransaction); + mServerTransaction, + mExternalIp, mExternalPort); startSessionTimer(((MakeCallCommand) evt).getTimeout()); return true; } else if (END_CALL == evt) { @@ -1376,6 +1405,7 @@ class SipSessionGroup implements SipListener { if (evt instanceof ResponseEvent) { if (parseOptionsResult(evt)) { if (mPortChanged) { + resetExternalAddress(); stop(); } else { cancelSessionTimer(); @@ -1405,8 +1435,11 @@ class SipSessionGroup implements SipListener { if (!mRunning) return; if (DEBUG_PING) { + String peerUri = (mPeerProfile == null) + ? "null" + : mPeerProfile.getUriString(); Log.d(TAG, "keepalive: " + mLocalProfile.getUriString() - + " --> " + mPeerProfile + ", interval=" + mInterval); + + " --> " + peerUri + ", interval=" + mInterval); } try { sendKeepAlive(); |