Merge "Initial upscaled progress spinners from Gingerbread in xhdpi"
diff --git a/api/current.txt b/api/current.txt
index f6bfd73..f21a4f3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -975,6 +975,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 0acba8b..ccd668d 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -213,9 +213,10 @@
// 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 d45ac192..26b9113 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -288,8 +288,9 @@
}
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 @@
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 @@
}
}
+ /* 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 @@
*_codesize = codesize;
*_datasize = datasize;
*_cachesize = cachesize;
+ *_asecsize = asecsize;
return 0;
}
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index c062d36..feb6b92 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -77,16 +77,18 @@
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 @@
{ "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 e5f6739..c5872b8 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -143,7 +143,8 @@
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 289665f..6fa66cf 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 @@
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 @@
AString mURI;
bool mDecodeAudio;
sp<Surface> mSurface;
+ bool mRenderToSurface;
sp<ACodec> mCodec;
sp<MediaSource> mSource;
@@ -451,7 +456,7 @@
inBuffer->release();
inBuffer = NULL;
- // break; // Don't coalesce
+ break; // Don't coalesce
}
LOGV("coalesced %d input buffers", n);
@@ -479,6 +484,10 @@
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
+ if (mRenderToSurface) {
+ reply->setInt32("render", 1);
+ }
+
reply->post();
}
@@ -491,7 +500,8 @@
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 @@
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 @@
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 656f5fd..1a5b7f3 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -865,10 +865,10 @@
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 f780afb..ee91c29 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() {
+ close(mFd);
+ mFd = -1;
}
void MyStreamSource::setListener(const sp<IStreamListener> &listener) {
@@ -99,6 +106,143 @@
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 @@
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 @@
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 @@
CHECK(service.get() != NULL);
- int fd = open(argv[1], O_RDONLY);
-
- if (fd < 0) {
- fprintf(stderr, "Failed to open file '%s'.", argv[1]);
- return 1;
- }
-
sp<MyClient> client = new MyClient;
+ sp<IStreamSource> source;
+
+ size_t len = strlen(argv[1]);
+ if (len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) {
+ int fd = open(argv[1], O_RDONLY);
+
+ 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 @@
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 11068e5..1205da7 100755
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -40,6 +40,12 @@
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 @@
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 @@
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 @@
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 @@
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 df8cf9a..e10f218 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -47,6 +47,7 @@
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 @@
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 @@
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 feb246e..9481a88 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -488,7 +488,15 @@
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 @@
}
/**
+ * 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 91af16d..593b2b7 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 @@
/** 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 @@
* 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 cb47193..2b59dba 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 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 @@
* 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 @@
* {@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 @@
* 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 e1b9deb..1e1cb49 100644
--- a/core/java/android/net/http/SslError.java
+++ b/core/java/android/net/http/SslError.java
@@ -59,36 +59,97 @@
/**
* 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 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 1b09242..c9b6121 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -210,6 +210,21 @@
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 c2dc8ae..9302060 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -655,6 +655,26 @@
}
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 @@
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 @@
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 @@
* 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 02faf49..39c6f57 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -24,6 +24,8 @@
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 @@
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 @@
}
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 cb96bfd2..ad71061 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -174,6 +174,15 @@
*/
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
old mode 100755
new mode 100644
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index 49f3bbe..9b081b2 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -239,7 +239,7 @@
break;
}
} finally {
- finishEvent(false);
+ finishEvent();
}
}
@@ -302,7 +302,7 @@
problem("Source was not SOURCE_CLASS_TRACKBALL.");
}
} finally {
- finishEvent(false);
+ finishEvent();
}
}
@@ -328,7 +328,9 @@
mTouchEventStreamUnhandled = false;
mTouchEventStreamPointers = 0;
}
- final boolean wasTainted = mTouchEventStreamIsTainted;
+ if (mTouchEventStreamIsTainted) {
+ event.setTainted(true);
+ }
try {
ensureMetaStateIsNormalized(event.getMetaState());
@@ -441,7 +443,7 @@
problem("Source was not SOURCE_CLASS_POINTER.");
}
} finally {
- finishEvent(wasTainted);
+ finishEvent();
}
}
@@ -499,7 +501,7 @@
}
}
} finally {
- finishEvent(false);
+ finishEvent();
}
}
@@ -591,9 +593,9 @@
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 @@
}
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 3436cd1..88f59d4 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1130,14 +1130,14 @@
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 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 @@
"BUTTON_TERTIARY",
"BUTTON_BACK",
"BUTTON_FORWARD",
- "BUTTON_ERASER",
+ "0x00000020",
"0x00000040",
"0x00000080",
"0x00000100",
@@ -2176,7 +2169,6 @@
* @see #BUTTON_TERTIARY
* @see #BUTTON_FORWARD
* @see #BUTTON_BACK
- * @see #BUTTON_ERASER
*/
public final int getButtonState() {
return nativeGetButtonState(mNativePtr);
@@ -2893,7 +2885,7 @@
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 5743134..bf7f359 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2493,6 +2493,84 @@
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 @@
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 @@
}
/**
+ * 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 @@
resetLayoutDirectionResolution();
resolveLayoutDirectionIfNeeded();
resolvePadding();
+ resetResolvedTextDirection();
+ resolveTextDirection();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
@@ -9043,7 +9134,7 @@
* @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 @@
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 6405398..2a90dde 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Locale;
/**
* <p>
@@ -5016,6 +5017,51 @@
}
/**
+ * 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 47f5e4c..a1a7281 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1126,7 +1126,7 @@
if (mServedView == mNextServedView && !mNextServedNeedsStart) {
return;
}
-
+
InputConnection ic = null;
synchronized (mH) {
if (mServedView == mNextServedView && !mNextServedNeedsStart) {
@@ -1242,6 +1242,27 @@
}
/**
+ * 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 bb03afa..ea6f5ee 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -63,6 +63,15 @@
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 2f4774f..79a5aff 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -1150,11 +1150,12 @@
* {@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 4c534f9..a2325c3 100644
--- a/core/java/android/webkit/CertTool.java
+++ b/core/java/android/webkit/CertTool.java
@@ -21,31 +21,27 @@
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 @@
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 4cae9d8..11ab0d7 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -104,9 +104,13 @@
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 @@
// 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 883656b..6c6974b 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1107,7 +1107,8 @@
}
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 2947ebe..aa23ad0 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1201,18 +1201,15 @@
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 85e7eec..2a70ac8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4948,7 +4948,10 @@
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 @@
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 @@
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 b57046c..ec64552 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -135,6 +135,16 @@
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 12687a1..7b65964 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.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 @@
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 @@
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 @@
}
}
- 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 @@
return;
}
- readHistory(in);
- readOldHistory(in);
+ readHistory(in, true);
mStartCount = in.readInt();
mBatteryUptime = in.readLong();
@@ -5136,8 +5168,7 @@
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 @@
throw new ParcelFormatException("Bad magic number");
}
- readHistory(in);
+ readHistory(in, false);
mStartCount = in.readInt();
mBatteryUptime = in.readLong();
@@ -5461,7 +5492,7 @@
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 5e9cd23..f13e770 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.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 @@
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 338dcaa..f875cbd 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -36,7 +36,9 @@
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 3e7b976..5b35104 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 @@
/**
* 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 @@
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 0cff00a..bc8a62f 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 @@
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 @@
// 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 @@
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 0000000..306d223
--- /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 dd8b378..2de0932 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -233,12 +233,6 @@
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 @@
{"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 47902a8..1a32060 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
deleted file mode 100644
index 1f7aeee..0000000
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
+++ /dev/null
Binary files differ
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
deleted file mode 100755
index 4c1d89d..0000000
--- a/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 6963a0e..0000000
--- a/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_holo_dark.9.png b/core/res/res/drawable-hdpi/cab_holo_dark.9.png
deleted file mode 100755
index 64e2052..0000000
--- a/core/res/res/drawable-hdpi/cab_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png
deleted file mode 100644
index 3a84de9..0000000
--- a/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png b/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png
index e21a87c..0f4bfe6 100644
--- a/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png b/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png
index 3283f99..995705d 100644
--- a/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png
Binary files differ
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
deleted file mode 100644
index 48cc017..0000000
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
+++ /dev/null
Binary files differ
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
deleted file mode 100755
index 66cbe48..0000000
--- a/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index b6508fc..0000000
--- a/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_holo_dark.9.png b/core/res/res/drawable-mdpi/cab_holo_dark.9.png
deleted file mode 100755
index 7daae1f..0000000
--- a/core/res/res/drawable-mdpi/cab_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png
deleted file mode 100644
index cb3d0f2..0000000
--- a/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png b/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png
index c10344f..754d7bc 100644
--- a/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png b/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png
index 08c6cfe..0187a02 100644
--- a/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png
new file mode 100644
index 0000000..544924e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png
new file mode 100644
index 0000000..2d28009
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png b/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png
new file mode 100644
index 0000000..cdc0afb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png b/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png
new file mode 100644
index 0000000..36b0448
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png b/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png
new file mode 100644
index 0000000..3a7a8b3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_center_bright.9.png b/core/res/res/drawable-xhdpi/popup_center_bright.9.png
new file mode 100644
index 0000000..b1a8e3e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_center_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_center_dark.9.png b/core/res/res/drawable-xhdpi/popup_center_dark.9.png
new file mode 100644
index 0000000..87378e1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_center_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_center_medium.9.png b/core/res/res/drawable-xhdpi/popup_center_medium.9.png
new file mode 100644
index 0000000..ea29ed4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_center_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_full_bright.9.png b/core/res/res/drawable-xhdpi/popup_full_bright.9.png
new file mode 100644
index 0000000..114faa0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_full_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_full_dark.9.png b/core/res/res/drawable-xhdpi/popup_full_dark.9.png
new file mode 100644
index 0000000..996beaa
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_full_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_top_bright.9.png b/core/res/res/drawable-xhdpi/popup_top_bright.9.png
new file mode 100644
index 0000000..64e4139
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_top_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_top_dark.9.png b/core/res/res/drawable-xhdpi/popup_top_dark.9.png
new file mode 100644
index 0000000..902bc29
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_top_dark.9.png
Binary files differ
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 0000000..004d52a
--- /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 0000000..807daf2
--- /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 004d52a..db8f311 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 026cbfb..8627637 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 807daf2..f2f524c 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 d6a0cdd..13bbac6 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 37e6027..fd61cfd 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 0ad3184..827153e 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 7bbfa9c..db6f98f 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 cd52a5a..88ed9c6b 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 d8d145c..a37f1a3 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -22,6 +22,7 @@
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 @@
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 b9c0d80..0b8d40f 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
new file mode 100644
index 0000000..c1acb38
--- /dev/null
+++ b/data/fonts/DroidSansHebrew-Bold.ttf
Binary files differ
diff --git a/data/fonts/DroidSansHebrew-Regular.ttf b/data/fonts/DroidSansHebrew-Regular.ttf
new file mode 100644
index 0000000..af6a58d
--- /dev/null
+++ b/data/fonts/DroidSansHebrew-Regular.ttf
Binary files differ
diff --git a/data/fonts/DroidSansHebrew.ttf b/data/fonts/DroidSansHebrew.ttf
deleted file mode 100644
index 8d77e3e..0000000
--- a/data/fonts/DroidSansHebrew.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 692ce34..d222c0b 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -18,7 +18,8 @@
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 3d132a3..88bc1ee 100644
--- a/docs/html/sdk/android-3.1-highlights.jd
+++ b/docs/html/sdk/android-3.1-highlights.jd
@@ -143,8 +143,8 @@
<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 adb6eac..90a7ac2 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -144,10 +144,6 @@
*/
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 e558dfd..c82fb9b 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -46,11 +46,14 @@
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 @@
// 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 f2c6505..e4c1c49 100644
--- a/include/media/stagefright/MPEG2TSWriter.h
+++ b/include/media/stagefright/MPEG2TSWriter.h
@@ -31,6 +31,10 @@
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 @@
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 @@
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 589cefd..92331a1 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -79,6 +79,13 @@
// 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 @@
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 d7fe572e..d2fed41 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 @@
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 03fd01b..dba98a3 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -34,6 +34,7 @@
// ----------------------------------------------------------------------------
class IMemoryHeap;
+class ComposerState;
class ISurfaceComposer : public IInterface
{
@@ -105,8 +106,7 @@
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 @@
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 2e75a0e..6e9a654 100644
--- a/include/surfaceflinger/ISurfaceComposerClient.h
+++ b/include/surfaceflinger/ISurfaceComposerClient.h
@@ -37,8 +37,6 @@
// ----------------------------------------------------------------------------
-class layer_state_t;
-
class ISurfaceComposerClient : public IInterface
{
public:
@@ -69,11 +67,6 @@
* 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 140b9f8..7fbbfb24 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -37,10 +37,12 @@
// ---------------------------------------------------------------------------
class DisplayInfo;
+class Composer;
class IMemoryHeap;
class ISurfaceComposer;
class Region;
class surface_flinger_cblk_t;
+struct layer_state_t;
// ---------------------------------------------------------------------------
@@ -59,8 +61,11 @@
// ---------------------------------------------------------------------------
+class Composer;
+
class SurfaceComposerClient : public RefBase
{
+ friend class Composer;
public:
SurfaceComposerClient();
virtual ~SurfaceComposerClient();
@@ -101,13 +106,7 @@
// 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 @@
private:
virtual void onFirstRef();
- inline layer_state_t* get_state_l(SurfaceID id);
- layer_state_t* lockLayerState(SurfaceID id);
- inline void unlockLayerState();
+ Composer& getComposer();
- mutable Mutex mLock;
- SortedVector<layer_state_t> mStates;
- int32_t mTransactionOpen;
- layer_state_t* mPrebuiltLayerState;
-
- // 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 0000000..04cb355
--- /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 8beec57..0e98aeb 100644
--- a/include/utils/SortedVector.h
+++ b/include/utils/SortedVector.h
@@ -32,6 +32,8 @@
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 f1e87e6..b908e2a 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 @@
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 @@
}
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 @@
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 ab4b9e0..f75208d 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -60,16 +60,10 @@
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 @@
}
}
- 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 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 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 18011e6..b567207 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -89,31 +89,117 @@
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 @@
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 40450a3..c1156d5 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 @@
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 @@
sp<IBinder> b = createGraphicBufferAlloc()->asBinder();
reply->writeStrongBinder(b);
} break;
- case OPEN_GLOBAL_TRANSACTION: {
+ case SET_TRANSACTION_STATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- openGlobalTransaction();
- } break;
- case CLOSE_GLOBAL_TRANSACTION: {
- 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 8d83392..bc97cac 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -51,8 +51,7 @@
enum {
CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
- DESTROY_SURFACE,
- SET_STATE
+ DESTROY_SURFACE
};
class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
@@ -92,17 +91,6 @@
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 @@
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 01c4c7e..87901e8 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 @@
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 1678711..8cead80 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -74,75 +74,52 @@
// ---------------------------------------------------------------------------
+// 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;
-
- Composer() : Singleton<Composer>() {
- }
-
- void addClientImpl(const sp<SurfaceComposerClient>& client) {
- Mutex::Autolock _l(mLock);
- mActiveConnections.add(client);
- }
-
- void removeClientImpl(const sp<SurfaceComposerClient>& client) {
- Mutex::Autolock _l(mLock);
- mActiveConnections.remove(client);
- }
-
- 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
- }
- }
- }
- }
-
- 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();
- }
-
friend class Singleton<Composer>;
+ mutable Mutex mLock;
+ SortedVector<ComposerState> mStates;
+
+ Composer() : Singleton<Composer>() { }
+
+ void closeGlobalTransactionImpl();
+
+ layer_state_t* getLayerStateLocked(
+ const sp<SurfaceComposerClient>& client, SurfaceID id);
+
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();
- }
+
+ 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);
+
static void closeGlobalTransaction() {
Composer::getInstance().closeGlobalTransactionImpl();
}
@@ -152,69 +129,306 @@
// ---------------------------------------------------------------------------
-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;
}
+sp<SurfaceControl> SurfaceComposerClient::createSurface(
+ DisplayID display,
+ uint32_t w,
+ uint32_t h,
+ PixelFormat format,
+ uint32_t flags)
+{
+ String8 name;
+ const size_t SIZE = 128;
+ char buffer[SIZE];
+ snprintf(buffer, SIZE, "<pid_%d>", getpid());
+ name.append(buffer);
+
+ return SurfaceComposerClient::createSurface(name, display,
+ w, h, format, flags);
+}
+
+sp<SurfaceControl> SurfaceComposerClient::createSurface(
+ const String8& name,
+ DisplayID display,
+ uint32_t w,
+ uint32_t h,
+ PixelFormat format,
+ uint32_t flags)
+{
+ sp<SurfaceControl> result;
+ if (mStatus == NO_ERROR) {
+ ISurfaceComposerClient::surface_data_t data;
+ sp<ISurface> surface = mClient->createSurface(&data, name,
+ display, w, h, format, flags);
+ if (surface != 0) {
+ result = new SurfaceControl(this, surface, data, w, h, format, flags);
+ }
+ }
+ return result;
+}
+
+status_t SurfaceComposerClient::destroySurface(SurfaceID sid) {
+ if (mStatus != NO_ERROR)
+ return mStatus;
+ status_t err = mClient->destroySurface(sid);
+ return err;
+}
+
+inline Composer& SurfaceComposerClient::getComposer() {
+ return mComposer;
+}
+
+// ----------------------------------------------------------------------------
+
+void SurfaceComposerClient::openGlobalTransaction() {
+ // Currently a no-op
+}
+
+void SurfaceComposerClient::closeGlobalTransaction() {
+ Composer::closeGlobalTransaction();
+}
+
+// ----------------------------------------------------------------------------
+
+status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) {
+ return getComposer().setFreezeTint(this, id, tint);
+}
+
+status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) {
+ return getComposer().setPosition(this, id, x, y);
+}
+
+status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) {
+ return getComposer().setSize(this, id, w, h);
+}
+
+status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) {
+ return getComposer().setLayer(this, id, z);
+}
+
+status_t SurfaceComposerClient::hide(SurfaceID id) {
+ return getComposer().setFlags(this, id,
+ ISurfaceComposer::eLayerHidden,
+ ISurfaceComposer::eLayerHidden);
+}
+
+status_t SurfaceComposerClient::show(SurfaceID id, int32_t) {
+ return getComposer().setFlags(this, id,
+ 0,
+ ISurfaceComposer::eLayerHidden);
+}
+
+status_t SurfaceComposerClient::freeze(SurfaceID id) {
+ return getComposer().setFlags(this, id,
+ ISurfaceComposer::eLayerFrozen,
+ ISurfaceComposer::eLayerFrozen);
+}
+
+status_t SurfaceComposerClient::unfreeze(SurfaceID id) {
+ return getComposer().setFlags(this, id,
+ 0,
+ ISurfaceComposer::eLayerFrozen);
+}
+
+status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags,
+ uint32_t mask) {
+ return getComposer().setFlags(this, id, flags, mask);
+}
+
+status_t SurfaceComposerClient::setTransparentRegionHint(SurfaceID id,
+ const Region& transparentRegion) {
+ return getComposer().setTransparentRegionHint(this, id, transparentRegion);
+}
+
+status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) {
+ return getComposer().setAlpha(this, id, alpha);
+}
+
+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::getDisplayInfo(
DisplayID dpy, DisplayInfo* info)
{
@@ -273,70 +487,7 @@
return n;
}
-sp<SurfaceControl> SurfaceComposerClient::createSurface(
- DisplayID display,
- uint32_t w,
- uint32_t h,
- PixelFormat format,
- uint32_t flags)
-{
- String8 name;
- const size_t SIZE = 128;
- char buffer[SIZE];
- snprintf(buffer, SIZE, "<pid_%d>", getpid());
- name.append(buffer);
-
- return SurfaceComposerClient::createSurface(name, display,
- w, h, format, flags);
-}
-
-sp<SurfaceControl> SurfaceComposerClient::createSurface(
- const String8& name,
- DisplayID display,
- uint32_t w,
- uint32_t h,
- PixelFormat format,
- uint32_t flags)
-{
- sp<SurfaceControl> result;
- if (mStatus == NO_ERROR) {
- ISurfaceComposerClient::surface_data_t data;
- sp<ISurface> surface = mClient->createSurface(&data, name,
- display, w, h, format, flags);
- if (surface != 0) {
- result = new SurfaceControl(this, surface, data, w, h, format, flags);
- }
- }
- return result;
-}
-
-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();
-}
-
-void SurfaceComposerClient::closeGlobalTransaction()
-{
- Composer::closeGlobalTransaction();
-}
+// ----------------------------------------------------------------------------
status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags)
{
@@ -350,199 +501,13 @@
return sm->unfreezeDisplay(dpy, flags);
}
-int SurfaceComposerClient::setOrientation(DisplayID dpy,
+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::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;
- }
-
- if (mTransactionOpen >= 2) {
- mTransactionOpen--;
- return NO_ERROR;
- }
-
- mTransactionOpen = 0;
- const ssize_t count = mStates.size();
- if (count) {
- mClient->setState(count, mStates.array());
- mStates.clear();
- }
- return NO_ERROR;
-}
-
-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;
- }
-
- // 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;
-}
-
-layer_state_t* SurfaceComposerClient::lockLayerState(SurfaceID id)
-{
- layer_state_t* s;
- mLock.lock();
- s = get_state_l(id);
- if (!s) mLock.unlock();
- return s;
-}
-
-void SurfaceComposerClient::unlockLayerState()
-{
- mLock.unlock();
-}
-
-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::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::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::hide(SurfaceID id)
-{
- return setFlags(id, ISurfaceComposer::eLayerHidden,
- ISurfaceComposer::eLayerHidden);
-}
-
-status_t SurfaceComposerClient::show(SurfaceID id, int32_t)
-{
- return setFlags(id, 0, ISurfaceComposer::eLayerHidden);
-}
-
-status_t SurfaceComposerClient::freeze(SurfaceID id)
-{
- return setFlags(id, ISurfaceComposer::eLayerFrozen,
- ISurfaceComposer::eLayerFrozen);
-}
-
-status_t SurfaceComposerClient::unfreeze(SurfaceID id)
-{
- return setFlags(id, 0, ISurfaceComposer::eLayerFrozen);
-}
-
-status_t SurfaceComposerClient::setFlags(SurfaceID id,
- uint32_t flags, uint32_t mask)
-{
- 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;
-}
-
-status_t SurfaceComposerClient::setTransparentRegionHint(
- SurfaceID id, const Region& transparentRegion)
-{
- layer_state_t* s = lockLayerState(id);
- if (!s) return BAD_INDEX;
- s->what |= ISurfaceComposer::eTransparentRegionChanged;
- s->transparentRegion = transparentRegion;
- unlockLayerState();
- return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha)
-{
- layer_state_t* s = lockLayerState(id);
- if (!s) return BAD_INDEX;
- s->what |= ISurfaceComposer::eAlphaChanged;
- s->alpha = alpha;
- unlockLayerState();
- return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::setMatrix(
- SurfaceID id,
- float dsdx, float dtdx,
- float dsdy, float dtdy )
-{
- 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;
-}
-
-status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint)
-{
- layer_state_t* s = lockLayerState(id);
- if (!s) return BAD_INDEX;
- s->what |= ISurfaceComposer::eFreezeTintChanged;
- s->tint = tint;
- unlockLayerState();
- return NO_ERROR;
-}
-
// ----------------------------------------------------------------------------
ScreenshotClient::ScreenshotClient()
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 37e6d11..0925001 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -417,17 +417,22 @@
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::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 @@
}
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 @@
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 f219639..c06400e 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -84,10 +84,10 @@
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 @@
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 @@
}
}
+// 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 35c8640..450cdf1 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -36,10 +36,10 @@
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 e43f6e5..8b1caeee 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -699,9 +699,10 @@
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 @@
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 @@
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 ffdfe66..c46d6f4 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -443,7 +443,8 @@
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 093189c..774e8c9 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -27,6 +27,7 @@
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 0000000..d752415
--- /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 3c05403..939bd4a 100755
--- a/location/java/android/location/Country.java
+++ b/location/java/android/location/Country.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Locale;
+
/**
* This class wraps the country information.
*
@@ -74,7 +76,7 @@
|| 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 6f42596..e3cbd57 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -720,8 +720,9 @@
*
* <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 29c4b89..0d2bcd5 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -957,12 +957,8 @@
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 d43a562..277e16c 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.cpp
+++ b/media/jni/mediaeditor/VideoEditorClasses.cpp
@@ -378,9 +378,7 @@
{
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 53e7de1..a8c08ac 100755
--- a/media/jni/mediaeditor/VideoEditorOsal.cpp
+++ b/media/jni/mediaeditor/VideoEditorOsal.cpp
@@ -166,7 +166,6 @@
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 d628301..513eda8 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 @@
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 @@
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 @@
AString mime;
CHECK(msg->findString("mime", &mime));
- 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();
- }
+ 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;
- IOMX::node_id node;
- CHECK_EQ(omx->allocateNode(componentName.c_str(), observer, &node),
- (status_t)OK);
+ AString componentName;
+
+ for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
+ ++matchIndex) {
+ componentName = matchingCodecs.itemAt(matchIndex).string();
+
+ status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
+
+ 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 bdffce9..ed8149a 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -158,9 +158,12 @@
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 @@
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 @@
// 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 @@
if (strstr(supportedFrameRates, buf) == NULL) {
LOGE("Requested frame rate (%d) is not supported: %s",
frameRate, supportedFrameRates);
- releaseCamera();
return BAD_VALUE;
}
@@ -463,7 +463,6 @@
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 @@
}
}
- IPCThreadState::self()->restoreCallingIdentity(token);
-
int64_t glitchDurationUs = (1000000LL / mVideoFrameRate);
if (glitchDurationUs > mGlitchDurationThresholdUs) {
mGlitchDurationThresholdUs = glitchDurationUs;
@@ -573,13 +570,21 @@
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();
}
- mCamera.clear();
- mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier);
- mCameraRecordingProxy.clear();
+ if (mCameraRecordingProxy != 0) {
+ mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier);
+ mCameraRecordingProxy.clear();
+ }
mCameraFlags = 0;
}
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 4e4f289..02eeb40 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -466,6 +466,8 @@
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(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 @@
}
void MPEG2TSWriter::init() {
- CHECK(mFile != NULL);
+ CHECK(mFile != NULL || mWriteFunc != NULL);
mLooper = new ALooper;
mLooper->setName("MPEG2TSWriter");
@@ -502,8 +519,10 @@
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 @@
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 @@
*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 @@
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 @@
}
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 @@
}
}
+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 cd97302..e36b01f 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1838,7 +1838,7 @@
}
}
- 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 ed6846c..967f126 100644
--- a/media/libstagefright/chromium_http/support.cpp
+++ b/media/libstagefright/chromium_http/support.cpp
@@ -41,6 +41,7 @@
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 @@
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 26cac50..0d8ea28 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -404,7 +404,6 @@
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 eb2f6f8..63c2de2 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 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 a9aa31b..9469601 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 @@
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 bc2f7ee..51e7d97 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 3944c203..da28e1e 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 22181b8..550fc57 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.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 @@
}
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 void onFinishInflate() {
- mBackground = findViewById(R.id.background);
-
mRotatedViews[Surface.ROTATION_0] =
mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
@@ -121,6 +120,10 @@
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 eea30407..b60bae7 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 @@
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 4a50d8a..85ce38a 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -569,9 +569,9 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- // 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 @@
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 @@
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 @@
resetTargetsLocked();
mTouchState.reset();
+ mLastHoverWindow = NULL;
}
void InputDispatcher::logDispatchStateLocked() {
@@ -4125,111 +4190,186 @@
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;
+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;
+}
- case EventEntry::TYPE_MOTION:
- trackMotion(static_cast<const MotionEntry*>(entry), action);
- break;
+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;
+ }
+
+ case AKEY_EVENT_ACTION_DOWN: {
+ ssize_t index = findKeyMemento(entry);
+ if (index >= 0) {
+ mKeyMementos.removeAt(index);
+ }
+ addKeyMemento(entry, flags);
+ return true;
+ }
+
+ default:
+ return true;
}
}
-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::trackMotion(const MotionEntry* entry,
+ int32_t action, int32_t flags) {
+ int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
+ 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: {
+ 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: {
+ 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;
+ }
+
+ 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;
+ }
+
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ 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++) {
- KeyMemento& memento = mKeyMementos.editItemAt(i);
+ const KeyMemento& memento = mKeyMementos.itemAt(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;
- }
+ return i;
}
}
-
-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;
- }
+ return -1;
}
-void InputDispatcher::InputState::trackMotion(const MotionEntry* entry, int32_t action) {
- int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
+ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry,
+ bool hovering) const {
for (size_t i = 0; i < mMotionMementos.size(); i++) {
- MotionMemento& memento = mMotionMementos.editItemAt(i);
+ const MotionMemento& memento = mMotionMementos.itemAt(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;
-
- case AMOTION_EVENT_ACTION_DOWN:
- mMotionMementos.removeAt(i);
- goto Found;
-
- case AMOTION_EVENT_ACTION_POINTER_UP:
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- case AMOTION_EVENT_ACTION_MOVE:
- memento.setPointers(entry);
- return;
-
- default:
- return;
- }
+ && memento.source == entry->source
+ && memento.hovering == hovering) {
+ return i;
}
}
+ return -1;
+}
-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;
- }
+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) {
@@ -4243,20 +4383,17 @@
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 @@
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 676d162..bdd1922 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -522,6 +522,10 @@
// 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 @@
// 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 @@
struct MotionMemento {
int32_t deviceId;
uint32_t source;
+ int32_t flags;
float xPrecision;
float yPrecision;
nsecs_t downTime;
@@ -771,6 +781,12 @@
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 3e4c666..82c3af3 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 @@
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 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 @@
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 @@
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 @@
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 08cc75e..0ae2ab8 100644
--- a/services/input/SpriteController.cpp
+++ b/services/input/SpriteController.cpp
@@ -252,11 +252,7 @@
| 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 @@
}
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 05de53a..a7324d9 100644
--- a/services/java/com/android/server/DnsPinger.java
+++ b/services/java/com/android/server/DnsPinger.java
@@ -20,7 +20,9 @@
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 @@
* 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 @@
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;
-
- Collection<InetAddress> dnses = linkProperties.getDnses();
- if (dnses == null || dnses.size() == 0)
- return null;
-
- return dnses.iterator().next();
- }
-
- private LinkProperties getCurLinkProperties() {
if (mConnectivityManager == null) {
mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
}
- return mConnectivityManager.getLinkProperties(mConnectionType);
+
+ 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 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;
+ }
}
/**
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index b03bd4d..14abf80 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1641,7 +1641,7 @@
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 @@
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 @@
}
}
}
- 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 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 @@
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 d3244ec..54e5432 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1626,6 +1626,30 @@
}
}
+ 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 e730d0d..adc6570 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -37,6 +37,11 @@
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.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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
@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 @@
*/
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));
+ reader = new BufferedReader(new FileReader(mProcStatsNetfilter));
- // assumes format from kernel:
- // idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes
-
- // 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 @@
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 @@
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 @@
}
/**
+ * 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 cd68c68..dbfd145 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -283,7 +283,8 @@
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 3ba9c14..1356e2a 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -95,14 +95,14 @@
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 b4fdc9f..293702d 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -446,6 +446,15 @@
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 @@
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 db3b61e..f5efda9 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.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 @@
private String mPackageName;
private String mInterfaceName;
- private String mDnsPropertyPrefix;
+
+ private LegacyVpnRunner mLegacyVpnRunner;
public Vpn(Context context, VpnCallback callback) {
mContext = context;
@@ -249,4 +258,195 @@
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 67e73f5f..4fa3bda 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -132,7 +132,7 @@
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 @@
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 @@
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 @@
*/
private void setEngineCapabilities(int capabilities) {
mEngineCapabilities = capabilities;
+
+ if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) {
+ mPeriodicTimeInjection = true;
+ requestUtcTime();
+ }
}
/**
@@ -1438,6 +1448,14 @@
}
/**
+ * 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 584cd03..1f2ec2c 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.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.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.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.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import libcore.io.IoUtils;
@@ -148,10 +154,14 @@
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 @@
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 @@
// 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 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 @@
}
updateScreenOn();
+ updateBackgroundData(true);
try {
mActivityManager.registerProcessObserver(mProcessObserver);
@@ -256,11 +273,15 @@
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 @@
// 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 @@
// 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 @@
};
/**
+ * 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 @@
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 @@
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 @@
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 @@
}
}
+ 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 @@
}
}
- 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 @@
// 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 @@
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 4a79d17..7610a11 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -124,8 +124,6 @@
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 @@
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 d10aa97..11ccd60 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -307,7 +307,7 @@
}
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 @@
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 5a9dae9..22e2dde 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.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 @@
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 @@
}
}
+ /**
+ * 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 @@
}
PackageParser.Package p;
boolean dataOnly = false;
+ String asecPath = null;
synchronized (mPackages) {
p = mPackages.get(packageName);
if(p == null) {
@@ -6842,6 +6854,12 @@
}
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 @@
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 6d4ad9a..87ffcba 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_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 @@
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 @@
acquire_wakelock_callback,
release_wakelock_callback,
create_thread_callback,
+ request_utc_time_callback,
};
static void xtra_download_request_callback()
@@ -232,6 +241,7 @@
"(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 2bab6a8..35e29a6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -101,9 +101,8 @@
}
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 @@
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 f0b19f2..1c57bc1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -78,7 +78,6 @@
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(), Thread(false),
mTransactionFlags(0),
- mTransactionCount(0),
mResizeTransationPending(false),
mLayersRemoved(false),
mBootTime(systemTime()),
@@ -385,13 +384,11 @@
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 @@
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 @@
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;
- }
- if (what & eVisibilityChanged) {
- if (layer->setFlags(s.flags, s.mask))
- 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 & 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 @@
{
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 @@
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 45f80ae..b49fa36 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -70,14 +70,12 @@
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 @@
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 @@
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 @@
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 18c54b3..56b2a8f 100644
--- a/services/surfaceflinger/tests/resize/resize.cpp
+++ b/services/surfaceflinger/tests/resize/resize.cpp
@@ -43,9 +43,9 @@
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 @@
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 5265f91..8e1c3fe 100644
--- a/services/surfaceflinger/tests/surface/surface.cpp
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -39,9 +39,9 @@
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 0000000..5bef3dd
--- /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 0000000..7c4f04e
--- /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 0000000..ac7cb5a
--- /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 07e5425..b4ac987 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.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.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.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 @@
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 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 @@
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 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 @@
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 @@
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 @@
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 @@
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 636d059..903f2b0 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.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 @@
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 @@
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 184d665..66120a1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -191,10 +191,6 @@
* {@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 c4a6f53..6e9d0f9d 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 @@
@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 0000000..f7b7b8e
--- /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 0000000..81c5411
--- /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 b1e494a..c033879 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -122,9 +122,6 @@
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 @@
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 0000000..882ed1f
--- /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 0000000..e63ee35
--- /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 7aa0617..8734143 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 0000000..9e3e950
--- /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 47950e3..c031bc1 100644
--- a/voip/java/com/android/server/sip/SipHelper.java
+++ b/voip/java/com/android/server/sip/SipHelper.java
@@ -150,9 +150,17 @@
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 @@
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 @@
*/
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 3b0f5460..47863bd 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -756,21 +756,20 @@
}
}
- 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 @@
// 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 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 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 @@
@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 @@
} 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 @@
// 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 @@
private String mErrorMessage;
private boolean mRunning = false;
+ private int mKeepAliveSuccessCount = 0;
+
private String getAction() {
return toString();
}
@@ -915,18 +935,56 @@
}
}
+ 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 @@
}
mTimer.cancel(this);
- if (mKeepAliveSession != null) {
- mKeepAliveSession.stopKeepAliveProcess();
- mKeepAliveSession = null;
- }
+ stopKeepAliveProcess();
mRegistered = false;
setListener(mProxy.getListener());
@@ -975,12 +1030,8 @@
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 @@
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 c0c1b28..047eb8d 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -119,6 +119,10 @@
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 @@
mCallReceiverSession = null;
mSessionMap.clear();
+
+ resetExternalAddress();
}
synchronized void onConnectivityChanged() {
@@ -190,6 +196,12 @@
}
}
+ synchronized void resetExternalAddress() {
+ Log.d(TAG, " reset external addr on " + mSipStack);
+ mExternalIp = null;
+ mExternalPort = 0;
+ }
+
public SipProfile getLocalProfile() {
return mLocalProfile;
}
@@ -363,6 +375,21 @@
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 @@
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 @@
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 @@
if (evt instanceof ResponseEvent) {
if (parseOptionsResult(evt)) {
if (mPortChanged) {
+ resetExternalAddress();
stop();
} else {
cancelSessionTimer();
@@ -1405,8 +1435,11 @@
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();