diff options
282 files changed, 7375 insertions, 3020 deletions
diff --git a/api/current.txt b/api/current.txt index c23b28b27c5d..aa08bb4b54b2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -596,10 +596,10 @@ package android { field public static final int layout_centerInParent = 16843151; // 0x101018f field public static final int layout_centerVertical = 16843153; // 0x1010191 field public static final int layout_column = 16843084; // 0x101014c - field public static final int layout_columnSpan = 16843646; // 0x101037e - field public static final int layout_columnWeight = 16843647; // 0x101037f + field public static final int layout_columnSpan = 16843645; // 0x101037d field public static final int layout_gravity = 16842931; // 0x10100b3 field public static final int layout_height = 16842997; // 0x10100f5 + field public static final int layout_heightSpec = 16843647; // 0x101037f field public static final int layout_margin = 16842998; // 0x10100f6 field public static final int layout_marginBottom = 16843002; // 0x10100fa field public static final int layout_marginEnd = 16843675; // 0x101039b @@ -609,13 +609,13 @@ package android { field public static final int layout_marginTop = 16843000; // 0x10100f8 field public static final int layout_row = 16843643; // 0x101037b field public static final int layout_rowSpan = 16843644; // 0x101037c - field public static final int layout_rowWeight = 16843645; // 0x101037d field public static final int layout_scale = 16843155; // 0x1010193 field public static final int layout_span = 16843085; // 0x101014d field public static final int layout_toLeftOf = 16843138; // 0x1010182 field public static final int layout_toRightOf = 16843139; // 0x1010183 field public static final int layout_weight = 16843137; // 0x1010181 field public static final int layout_width = 16842996; // 0x10100f4 + field public static final int layout_widthSpec = 16843646; // 0x101037e field public static final int layout_x = 16843135; // 0x101017f field public static final int layout_y = 16843136; // 0x1010180 field public static final int left = 16843181; // 0x10101ad @@ -3371,6 +3371,7 @@ package android.app { method public void send(android.content.Context, int, android.content.Intent) throws android.app.PendingIntent.CanceledException; method public void send(int, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException; method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException; + method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String) throws android.app.PendingIntent.CanceledException; method public static void writePendingIntentOrNullToParcel(android.app.PendingIntent, android.os.Parcel); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -3452,6 +3453,7 @@ package android.app { field public static final java.lang.String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data"; field public static final java.lang.String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id"; field public static final java.lang.String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; + field public static final java.lang.String SUGGEST_COLUMN_LAST_ACCESS_HINT = "suggest_last_access_hint"; field public static final java.lang.String SUGGEST_COLUMN_QUERY = "suggest_intent_query"; field public static final java.lang.String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id"; field public static final java.lang.String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING = "suggest_spinner_while_refreshing"; @@ -5297,6 +5299,7 @@ package android.content { method public java.lang.String getTargetPackage(); method public static android.content.IntentSender readIntentSenderOrNullFromParcel(android.os.Parcel); method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler) throws android.content.IntentSender.SendIntentException; + method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler, java.lang.String) throws android.content.IntentSender.SendIntentException; method public static void writeIntentSenderOrNullToParcel(android.content.IntentSender, android.os.Parcel); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -5897,6 +5900,7 @@ package android.content.pm { field public long codeSize; field public long dataSize; field public long externalCacheSize; + field public long externalCodeSize; field public long externalDataSize; field public long externalMediaSize; field public long externalObbSize; @@ -9398,6 +9402,7 @@ package android.inputmethodservice { method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo); method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo); method public void onUpdateSelection(int, int, int, int, int, int); + method public void onViewClicked(boolean); method public void onWindowHidden(); method public void onWindowShown(); method public void requestHideSelf(int); @@ -9441,6 +9446,7 @@ package android.inputmethodservice { method public void updateCursor(android.graphics.Rect); method public void updateExtractedText(int, android.view.inputmethod.ExtractedText); method public void updateSelection(int, int, int, int, int, int); + method public void viewClicked(boolean); } public static final class InputMethodService.Insets { @@ -22259,6 +22265,7 @@ package android.view { method public static deprecated int getTouchSlop(); method public static deprecated int getWindowTouchSlop(); method public static long getZoomControlsTimeout(); + method public boolean hasPermanentMenuKey(); } public class ViewDebug { @@ -22393,6 +22400,7 @@ package android.view { method public void requestDisallowInterceptTouchEvent(boolean); method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); method public void requestTransparentRegion(android.view.View); + method protected void resetLayoutDirectionResolution(); method public void scheduleLayoutAnimation(); method public void setAddStatesFromChildren(boolean); method public void setAlwaysDrawnWithCacheEnabled(boolean); @@ -23517,6 +23525,7 @@ package android.view.inputmethod { method public void updateCursor(android.view.View, int, int, int, int); method public void updateExtractedText(android.view.View, int, android.view.inputmethod.ExtractedText); method public void updateSelection(android.view.View, int, int, int, int); + method public void viewClicked(android.view.View); field public static final int HIDE_IMPLICIT_ONLY = 1; // 0x1 field public static final int HIDE_NOT_ALWAYS = 2; // 0x2 field public static final int RESULT_HIDDEN = 3; // 0x3 @@ -23537,6 +23546,7 @@ package android.view.inputmethod { method public abstract void updateCursor(android.graphics.Rect); method public abstract void updateExtractedText(int, android.view.inputmethod.ExtractedText); method public abstract void updateSelection(int, int, int, int, int, int); + method public abstract void viewClicked(boolean); } public static abstract interface InputMethodSession.EventCallback { @@ -24883,9 +24893,9 @@ package android.widget { } public class GridLayout extends android.view.ViewGroup { - ctor public GridLayout(android.content.Context); ctor public GridLayout(android.content.Context, android.util.AttributeSet, int); ctor public GridLayout(android.content.Context, android.util.AttributeSet); + ctor public GridLayout(android.content.Context); method public int getAlignmentMode(); method public int getColumnCount(); method public int getOrientation(); @@ -24905,8 +24915,11 @@ package android.widget { field public static final int ALIGN_MARGINS = 1; // 0x1 field public static final android.widget.GridLayout.Alignment BASELINE; field public static final android.widget.GridLayout.Alignment BOTTOM; + field public static final android.widget.GridLayout.Spec CAN_SHRINK; + field public static final android.widget.GridLayout.Spec CAN_STRETCH; field public static final android.widget.GridLayout.Alignment CENTER; field public static final android.widget.GridLayout.Alignment FILL; + field public static final android.widget.GridLayout.Spec FIXED; field public static final int HORIZONTAL = 0; // 0x0 field public static final android.widget.GridLayout.Alignment LEFT; field public static final android.widget.GridLayout.Alignment RIGHT; @@ -24933,9 +24946,13 @@ package android.widget { ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet); method public void setGravity(int); field public android.widget.GridLayout.Group columnGroup; - field public float columnWeight; + field public android.widget.GridLayout.Spec heightSpec; field public android.widget.GridLayout.Group rowGroup; - field public float rowWeight; + field public android.widget.GridLayout.Spec widthSpec; + } + + public static abstract class GridLayout.Spec { + ctor public GridLayout.Spec(); } public class GridView extends android.widget.AbsListView { @@ -26059,6 +26076,7 @@ package android.widget { method protected void onTextChanged(java.lang.CharSequence, int, int, int); method public boolean onTextContextMenuItem(int); method public void removeTextChangedListener(android.text.TextWatcher); + method protected void resetLayoutDirectionResolution(); method public final void setAutoLinkMask(int); method public void setCompoundDrawablePadding(int); method public void setCompoundDrawables(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 0acba8bc516b..ccd668d08192 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -213,9 +213,10 @@ status_t BootAnimation::readyToRun() { // create the native surface sp<SurfaceControl> control = session()->createSurface( 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); - session()->openTransaction(); + + SurfaceComposerClient::openGlobalTransaction(); control->setLayer(0x40000000); - session()->closeTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); sp<Surface> s = control->getSurface(); diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index d45ac1925a24..26b911356699 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -288,8 +288,9 @@ int protect(char *pkgname, gid_t gid) } int get_size(const char *pkgname, const char *apkpath, - const char *fwdlock_apkpath, - int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize) + const char *fwdlock_apkpath, const char *asecpath, + int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, + int64_t* _asecsize) { DIR *d; int dfd; @@ -300,6 +301,7 @@ int get_size(const char *pkgname, const char *apkpath, int64_t codesize = 0; int64_t datasize = 0; int64_t cachesize = 0; + int64_t asecsize = 0; /* count the source apk as code -- but only if it's not * on the /system partition and its not on the sdcard. @@ -324,6 +326,14 @@ int get_size(const char *pkgname, const char *apkpath, } } + /* compute asec size if it is given + */ + if (asecpath != NULL && asecpath[0] != '!') { + if (stat(asecpath, &s) == 0) { + asecsize += stat_size(&s); + } + } + if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, 0)) { goto done; } @@ -370,6 +380,7 @@ done: *_codesize = codesize; *_datasize = datasize; *_cachesize = cachesize; + *_asecsize = asecsize; return 0; } diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c index c062d36ec93b..feb6b927dd9b 100644 --- a/cmds/installd/installd.c +++ b/cmds/installd/installd.c @@ -77,16 +77,18 @@ static int do_get_size(char **arg, char reply[REPLY_MAX]) int64_t codesize = 0; int64_t datasize = 0; int64_t cachesize = 0; + int64_t asecsize = 0; int res = 0; /* pkgdir, apkpath */ - res = get_size(arg[0], arg[1], arg[2], &codesize, &datasize, &cachesize); + res = get_size(arg[0], arg[1], arg[2], arg[3], &codesize, &datasize, &cachesize, &asecsize); /* * Each int64_t can take up 22 characters printed out. Make sure it * doesn't go over REPLY_MAX in the future. */ - snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64, codesize, datasize, cachesize); + snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64, + codesize, datasize, cachesize, asecsize); return res; } @@ -137,7 +139,7 @@ struct cmdinfo cmds[] = { { "freecache", 1, do_free_cache }, { "rmcache", 1, do_rm_cache }, { "protect", 2, do_protect }, - { "getsize", 3, do_get_size }, + { "getsize", 4, do_get_size }, { "rmuserdata", 2, do_rm_user_data }, { "movefiles", 0, do_movefiles }, { "linklib", 2, do_linklib }, diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index e5f6739d9081..c5872b8efa09 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -143,7 +143,8 @@ int move_dex(const char *src, const char *dst); int rm_dex(const char *path); int protect(char *pkgname, gid_t gid); int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath, - int64_t *codesize, int64_t *datasize, int64_t *cachesize); + const char *asecpath, int64_t *codesize, int64_t *datasize, int64_t *cachesize, + int64_t *asecsize); int free_cache(int64_t free_size); int dexopt(const char *apk_path, uid_t uid, int is_public); int movefiles(); diff --git a/cmds/keystore/keystore.cpp b/cmds/keystore/keystore.cpp index 1c1f37a34c7b..4b4b9b9d4adb 100644 --- a/cmds/keystore/keystore.cpp +++ b/cmds/keystore/keystore.cpp @@ -712,7 +712,6 @@ static struct user { {AID_VPN, AID_SYSTEM, GET}, {AID_WIFI, AID_SYSTEM, GET}, {AID_ROOT, AID_SYSTEM, GET}, - {AID_KEYCHAIN, AID_SYSTEM, TEST | GET | SAW}, {~0, ~0, TEST | GET | INSERT | DELETE | EXIST | SAW}, }; diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp index ddd64ecf0445..6fa66cf3524e 100644 --- a/cmds/stagefright/sf2.cpp +++ b/cmds/stagefright/sf2.cpp @@ -568,10 +568,10 @@ int main(int argc, char **argv) { CHECK(control != NULL); CHECK(control->isValid()); - CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + SurfaceComposerClient::openGlobalTransaction(); CHECK_EQ(control->setLayer(30000), (status_t)OK); CHECK_EQ(control->show(), (status_t)OK); - CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); surface = control->getSurface(); CHECK(surface != NULL); diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 656f5fdb4805..1a5b7f3ab8b1 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -865,10 +865,10 @@ int main(int argc, char **argv) { CHECK(control != NULL); CHECK(control->isValid()); - CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + SurfaceComposerClient::openGlobalTransaction(); CHECK_EQ(control->setLayer(30000), (status_t)OK); CHECK_EQ(control->show(), (status_t)OK); - CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); gSurface = control->getSurface(); CHECK(gSurface != NULL); diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp index f780afb80a0a..ee91c291c08d 100644 --- a/cmds/stagefright/stream.cpp +++ b/cmds/stagefright/stream.cpp @@ -20,6 +20,11 @@ #include <media/mediaplayer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MPEG2TSWriter.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> #include <binder/IServiceManager.h> #include <media/IMediaPlayerService.h> @@ -31,7 +36,7 @@ using namespace android; struct MyStreamSource : public BnStreamSource { - // Caller retains ownership of fd. + // Object assumes ownership of fd. MyStreamSource(int fd); virtual void setListener(const sp<IStreamListener> &listener); @@ -64,6 +69,8 @@ MyStreamSource::MyStreamSource(int fd) } MyStreamSource::~MyStreamSource() { + close(mFd); + mFd = -1; } void MyStreamSource::setListener(const sp<IStreamListener> &listener) { @@ -99,6 +106,143 @@ void MyStreamSource::onBufferAvailable(size_t index) { mListener->queueBuffer(index, n); } } +//////////////////////////////////////////////////////////////////////////////// + +struct MyConvertingStreamSource : public BnStreamSource { + MyConvertingStreamSource(const char *filename); + + virtual void setListener(const sp<IStreamListener> &listener); + virtual void setBuffers(const Vector<sp<IMemory> > &buffers); + + virtual void onBufferAvailable(size_t index); + +protected: + virtual ~MyConvertingStreamSource(); + +private: + Mutex mLock; + Condition mCondition; + + sp<IStreamListener> mListener; + Vector<sp<IMemory> > mBuffers; + + sp<MPEG2TSWriter> mWriter; + + ssize_t mCurrentBufferIndex; + size_t mCurrentBufferOffset; + + List<size_t> mBufferQueue; + + static ssize_t WriteDataWrapper(void *me, const void *data, size_t size); + ssize_t writeData(const void *data, size_t size); + + DISALLOW_EVIL_CONSTRUCTORS(MyConvertingStreamSource); +}; + +//////////////////////////////////////////////////////////////////////////////// + +MyConvertingStreamSource::MyConvertingStreamSource(const char *filename) + : mCurrentBufferIndex(-1), + mCurrentBufferOffset(0) { + sp<DataSource> dataSource = DataSource::CreateFromURI(filename); + CHECK(dataSource != NULL); + + sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); + CHECK(extractor != NULL); + + mWriter = new MPEG2TSWriter( + this, &MyConvertingStreamSource::WriteDataWrapper); + + for (size_t i = 0; i < extractor->countTracks(); ++i) { + const sp<MetaData> &meta = extractor->getTrackMetaData(i); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (strncasecmp("video/", mime, 6) && strncasecmp("audio/", mime, 6)) { + continue; + } + + CHECK_EQ(mWriter->addSource(extractor->getTrack(i)), (status_t)OK); + } + + CHECK_EQ(mWriter->start(), (status_t)OK); +} + +MyConvertingStreamSource::~MyConvertingStreamSource() { +} + +void MyConvertingStreamSource::setListener( + const sp<IStreamListener> &listener) { + mListener = listener; +} + +void MyConvertingStreamSource::setBuffers( + const Vector<sp<IMemory> > &buffers) { + mBuffers = buffers; +} + +ssize_t MyConvertingStreamSource::WriteDataWrapper( + void *me, const void *data, size_t size) { + return static_cast<MyConvertingStreamSource *>(me)->writeData(data, size); +} + +ssize_t MyConvertingStreamSource::writeData(const void *data, size_t size) { + size_t totalWritten = 0; + + while (size > 0) { + Mutex::Autolock autoLock(mLock); + + if (mCurrentBufferIndex < 0) { + while (mBufferQueue.empty()) { + mCondition.wait(mLock); + } + + mCurrentBufferIndex = *mBufferQueue.begin(); + mCurrentBufferOffset = 0; + + mBufferQueue.erase(mBufferQueue.begin()); + } + + sp<IMemory> mem = mBuffers.itemAt(mCurrentBufferIndex); + + size_t copy = size; + if (copy + mCurrentBufferOffset > mem->size()) { + copy = mem->size() - mCurrentBufferOffset; + } + + memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy); + mCurrentBufferOffset += copy; + + if (mCurrentBufferOffset == mem->size()) { + mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); + mCurrentBufferIndex = -1; + } + + data = (const uint8_t *)data + copy; + size -= copy; + + totalWritten += copy; + } + + return (ssize_t)totalWritten; +} + +void MyConvertingStreamSource::onBufferAvailable(size_t index) { + Mutex::Autolock autoLock(mLock); + + mBufferQueue.push_back(index); + mCondition.signal(); + + if (mWriter->reachedEOS()) { + if (mCurrentBufferIndex >= 0) { + mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); + mCurrentBufferIndex = -1; + } + + mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); + } +} //////////////////////////////////////////////////////////////////////////////// @@ -139,6 +283,8 @@ private: int main(int argc, char **argv) { android::ProcessState::self()->startThreadPool(); + DataSource::RegisterDefaultSniffers(); + if (argc != 2) { fprintf(stderr, "Usage: %s filename\n", argv[0]); return 1; @@ -159,10 +305,10 @@ int main(int argc, char **argv) { CHECK(control != NULL); CHECK(control->isValid()); - CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + SurfaceComposerClient::openGlobalTransaction(); CHECK_EQ(control->setLayer(30000), (status_t)OK); CHECK_EQ(control->show(), (status_t)OK); - CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); sp<Surface> surface = control->getSurface(); CHECK(surface != NULL); @@ -173,17 +319,28 @@ int main(int argc, char **argv) { CHECK(service.get() != NULL); - int fd = open(argv[1], O_RDONLY); + sp<MyClient> client = new MyClient; + + sp<IStreamSource> source; - if (fd < 0) { - fprintf(stderr, "Failed to open file '%s'.", argv[1]); - return 1; - } + size_t len = strlen(argv[1]); + if (len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) { + int fd = open(argv[1], O_RDONLY); - sp<MyClient> client = new MyClient; + if (fd < 0) { + fprintf(stderr, "Failed to open file '%s'.", argv[1]); + return 1; + } + + source = new MyStreamSource(fd); + } else { + printf("Converting file to transport stream for streaming...\n"); + + source = new MyConvertingStreamSource(argv[1]); + } sp<IMediaPlayer> player = - service->create(getpid(), client, new MyStreamSource(fd), 0); + service->create(getpid(), client, source, 0); if (player != NULL) { player->setVideoSurface(surface); @@ -196,9 +353,6 @@ int main(int argc, char **argv) { fprintf(stderr, "failed to instantiate player.\n"); } - close(fd); - fd = -1; - composerClient->dispose(); return 0; diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 164acbcdf71f..68c992671f97 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -31,82 +31,151 @@ import android.view.accessibility.AccessibilityNodeInfo; * An accessibility service runs in the background and receives callbacks by the system * when {@link AccessibilityEvent}s are fired. Such events denote some state transition * in the user interface, for example, the focus has changed, a button has been clicked, - * etc. + * etc. Such a service can optionally request the capability for querying the content + * of the active window. Development of an accessibility service requires extends this + * class and implements its abstract methods. * <p> - * An accessibility service extends this class and implements its abstract methods. Such - * a service is declared as any other service in an AndroidManifest.xml but it must also - * specify that it handles the "android.accessibilityservice.AccessibilityService" - * {@link android.content.Intent}. Following is an example of such a declaration: - * <p> - * <code> - * <service android:name=".MyAccessibilityService"><br> - * <intent-filter><br> - * <action android:name="android.accessibilityservice.AccessibilityService" /><br> - * </intent-filter><br> - * </service><br> - * </code> + * <strong>Lifecycle</strong> * </p> * <p> - * The lifecycle of an accessibility service is managed exclusively by the system. Starting - * or stopping an accessibility service is triggered by an explicit user action through + * The lifecycle of an accessibility service is managed exclusively by the system and + * follows the established service life cycle. Additionally, starting or stopping an + * accessibility service is triggered exclusively by an explicit user action through * enabling or disabling it in the device settings. After the system binds to a service it * calls {@link AccessibilityService#onServiceConnected()}. This method can be * overriden by clients that want to perform post binding setup. * </p> * <p> + * <strong>Declaration</strong> + * </p> + * <p> + * An accessibility is declared as any other service in an AndroidManifest.xml but it + * must also specify that it handles the "android.accessibilityservice.AccessibilityService" + * {@link android.content.Intent}. Failure to declare this intent will cause the system to + * ignore the accessibility service. Following is an example declaration: + * </p> + * <p> + * <code> + * <pre> + * <service android:name=".MyAccessibilityService"> + * <intent-filter> + * <action android:name="android.accessibilityservice.AccessibilityService" /> + * </intent-filter> + * . . . + * </service> + * </pre> + * </code> + * </p> + * <p> + * <strong>Configuration</strong> + * </p> + * <p> * An accessibility service can be configured to receive specific types of accessibility events, * listen only to specific packages, get events from each type only once in a given time frame, * retrieve window content, specify a settings activity, etc. * </p> + * <p> * There are two approaches for configuring an accessibility service: + * </p> * <ul> - * <li> - * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring - * the service. A service declaration with a meta-data tag is presented below: - * <p> - * <code> - * <service android:name=".MyAccessibilityService"><br> - * <intent-filter><br> - * <action android:name="android.accessibilityservice.AccessibilityService" /><br> - * </intent-filter><br> - * <meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /><br> - * </service><br> - * </code> - * </p> - * <p> - * <strong> - * This approach enables setting all accessibility service properties. - * </strong> - * </p> - * <p> - * For more details refer to {@link #SERVICE_META_DATA}. - * </p> - * </li> - * <li> - * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note - * that this method can be called any time to change the service configuration.<br> - * <p> - * <strong> - * This approach enables setting only dynamically configurable accessibility - * service properties: - * {@link AccessibilityServiceInfo#eventTypes}, - * {@link AccessibilityServiceInfo#feedbackType}, - * {@link AccessibilityServiceInfo#flags}, - * {@link AccessibilityServiceInfo#notificationTimeout}, - * {@link AccessibilityServiceInfo#packageNames} - * </strong> - * </p> - * <p> - * For more details refer to {@link AccessibilityServiceInfo}. - * </p> - * </li> + * <li> + * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring + * the service. A service declaration with a meta-data tag is presented below: + * <p> + * <code> + * <pre> + * <service android:name=".MyAccessibilityService"> + * <intent-filter> + * <action android:name="android.accessibilityservice.AccessibilityService" /> + * </intent-filter> + * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> + * </service> + * </pre> + * </code> + * </p> + * <p> + * <strong>Note:</strong>This approach enables setting all properties. + * </p> + * <p> + * For more details refer to {@link #SERVICE_META_DATA} and + * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>.. + * </p> + * </li> + * <li> + * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note + * that this method can be called any time to dynamically change the service configuration. + * <p> + * <strong>Note:</strong> This approach enables setting only dynamically configurable properties: + * {@link AccessibilityServiceInfo#eventTypes}, + * {@link AccessibilityServiceInfo#feedbackType}, + * {@link AccessibilityServiceInfo#flags}, + * {@link AccessibilityServiceInfo#notificationTimeout}, + * {@link AccessibilityServiceInfo#packageNames} + * </p> + * <p> + * For more details refer to {@link AccessibilityServiceInfo}. + * </p> + * </li> * </ul> * <p> - * An accessibility service can be registered for events in specific packages to provide a - * specific type of feedback and is notified with a certain timeout after the last event - * of interest has been fired. + * <strong>Retrieving window content</strong> + * </p> + * <p> + * An service can specify in its declaration that it can retrieve the active window + * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that + * declaring this capability requires that the service declares its configuration via + * an XML resource referenced by {@link #SERVICE_META_DATA}. + * </p> + * <p> + * For security purposes an accessibility service can retrieve only the content of the + * currently active window. The currently active window is defined as the window from + * which was fired the last event of the following types: + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}, + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}, + * {@link AccessibilityEvent#TYPE_VIEW_CLICKED}, + * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}, + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}, + * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}, + * {@link AccessibilityEvent#TYPE_VIEW_SELECTED}, + * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}, + * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}, + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED}, + * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}, + * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}. + * In other words, the active window is the one where the user interaction is taking place. + * </p> + * <p> + * The entry point for retrieving window content is through calling + * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received + * event of the above types or a previous event from the same window + * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking + * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the + * window content which represented as a tree of such objects. + * </p> + * <p> + * <strong>Note</strong>An accessibility service may have requested to be notified for + * a subset of the event types, thus be unaware that the active window has changed. Therefore + * accessibility service that would like to retrieve window content should: + * <ul> + * <li> + * Register for all event types with no notification timeout and keep track for the active + * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and + * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval + * methods on the latter. + * </li> + * <li> + * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the + * active window has changed and the service did not get the accessibility event yet. Note + * that it is possible to have a retrieval method failing event adopting the strategy + * specified in the previous bullet because the accessibility event dispatch is asynchronous + * and crosses process boundaries. + * </li> + * </ul> + * </p> * <p> * <b>Notification strategy</b> + * </p> * <p> * For each feedback type only one accessibility service is notified. Services are notified * in the order of registration. Hence, if two services are registered for the same @@ -117,9 +186,10 @@ import android.view.accessibility.AccessibilityNodeInfo; * registration order. This enables "generic" accessibility services that work reasonably * well with most applications to coexist with "polished" ones that are targeted for * specific applications. + * </p> * <p> * <b>Event types</b> - * <p> + * </p> * {@link AccessibilityEvent#TYPE_VIEW_CLICKED} * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED} @@ -127,9 +197,16 @@ import android.view.accessibility.AccessibilityNodeInfo; * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} * {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} - * <p> - * <b>Feedback types</b> - * <p> + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START} + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END} + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER} + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT} + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} + * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} + * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} + * <p> + * <b>Feedback types</b> + * <p> * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} * {@link AccessibilityServiceInfo#FEEDBACK_HAPTIC} * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} @@ -140,10 +217,10 @@ import android.view.accessibility.AccessibilityNodeInfo; * @see AccessibilityServiceInfo * @see android.view.accessibility.AccessibilityManager * - * Note: The event notification timeout is useful to avoid propagating events to the client - * too frequently since this is accomplished via an expensive interprocess call. - * One can think of the timeout as a criteria to determine when event generation has - * settled down. + * <strong>Note:</strong> The event notification timeout is useful to avoid propagating + * events to the client too frequently since this is accomplished via an expensive + * interprocess call. One can think of the timeout as a criteria to determine when + * event generation has settled down. */ public abstract class AccessibilityService extends Service { /** @@ -154,57 +231,25 @@ public abstract class AccessibilityService extends Service { /** * Name under which an AccessibilityService component publishes information - * about itself. This meta-data must reference an XML resource containing - * an + * about itself. This meta-data must reference an XML resource containing an * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code> * tag. This is a a sample XML file configuring an accessibility service: * <p> * <code> - * <?xml version="1.0" encoding="utf-8"?><br> - * <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"<br> - * android:accessibilityEventTypes="typeViewClicked|typeViewFocused"<br> - * android:packageNames="foo.bar, foo.baz"<br> - * android:accessibilityFeedbackType="feedbackSpoken"<br> - * android:notificationTimeout="100"<br> - * android:accessibilityFlags="flagDefault"<br> - * android:settingsActivity="foo.bar.TestBackActivity"<br> - * . . .<br> + * <pre> + * <accessibility-service + * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" + * android:packageNames="foo.bar, foo.baz" + * android:accessibilityFeedbackType="feedbackSpoken" + * android:notificationTimeout="100" + * android:accessibilityFlags="flagDefault" + * android:settingsActivity="foo.bar.TestBackActivity" + * android:canRetrieveWindowContent="true" + * . . . * /> + * </pre> * </code> * </p> - * <p> - * <strong>Note:</strong> A service can retrieve only the content of the active window. - * An active window is the source of the most recent event of type - * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}, - * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}, - * {@link AccessibilityEvent#TYPE_VIEW_CLICKED}, - * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}, - * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, - * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}, - * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}, - * {@link AccessibilityEvent#TYPE_VIEW_SELECTED}, - * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}, - * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}. - * Therefore the service should: - * <ul> - * <li> - * Register for all event types with no notification timeout and keep track - * for the active window by calling - * {@link AccessibilityEvent#getWindowId()} of the last received - * event and compare this with the - * {@link AccessibilityNodeInfo#getWindowId()} before calling - * retrieval methods on the latter. - * </li> - * <li> - * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail - * since the active window has changed and the service did not get the - * accessibility event. Note that it is possible to have a retrieval method - * failing event adopting the strategy specified in the previous bullet - * because the accessibility event dispatch is asynchronous and crosses - * process boundaries. - * </li> - * <ul> - * </p> */ public static final String SERVICE_META_DATA = "android.accessibilityservice"; diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index b9878cd9c97a..ef4adca97a82 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -37,13 +37,13 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; /** - * This class describes an {@link AccessibilityService}. The system - * notifies an {@link AccessibilityService} for - * {@link android.view.accessibility.AccessibilityEvent}s + * This class describes an {@link AccessibilityService}. The system notifies an + * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s * according to the information encapsulated in this class. * * @see AccessibilityService * @see android.view.accessibility.AccessibilityEvent + * @see android.view.accessibility.AccessibilityManager */ public class AccessibilityServiceInfo implements Parcelable { @@ -93,12 +93,19 @@ public class AccessibilityServiceInfo implements Parcelable { * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED + * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START + * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED + * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED */ public int eventTypes; /** * The package names an {@link AccessibilityService} is interested in. Setting - * to null is equivalent to all packages. + * to <code>null</code> is equivalent to all packages. * <p> * <strong>Can be dynamically set at runtime.</strong> * </p> @@ -125,10 +132,10 @@ public class AccessibilityServiceInfo implements Parcelable { * <strong>Can be dynamically set at runtime.</strong>. * </p> * <p> - * Note: The event notification timeout is useful to avoid propagating events to the client - * too frequently since this is accomplished via an expensive interprocess call. - * One can think of the timeout as a criteria to determine when event generation has - * settled down + * <strong>Note:</strong> The event notification timeout is useful to avoid propagating + * events to the client too frequently since this is accomplished via an expensive + * interprocess call. One can think of the timeout as a criteria to determine when + * event generation has settled down. */ public long notificationTimeout; @@ -159,7 +166,7 @@ public class AccessibilityServiceInfo implements Parcelable { private String mSettingsActivityName; /** - * Flag whether this accessibility service can retrieve screen content. + * Flag whether this accessibility service can retrieve window content. */ private boolean mCanRetrieveWindowContent; @@ -296,12 +303,12 @@ public class AccessibilityServiceInfo implements Parcelable { } /** - * Whether this service can retrieve the currently focused window content. + * Whether this service can retrieve the current window's content. * <p> * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> - * @return True screen content is retrieved. + * @return True window content can be retrieved. */ public boolean getCanRetrieveWindowContent() { return mCanRetrieveWindowContent; diff --git a/core/java/android/accessibilityservice/package.html b/core/java/android/accessibilityservice/package.html new file mode 100644 index 000000000000..0c640d14368f --- /dev/null +++ b/core/java/android/accessibilityservice/package.html @@ -0,0 +1,22 @@ +<html> +<body> +<p> + The classes in this package are used for development of accessibility service that + provide alternative or augmented feedback to the user. +</p> +<p> + An {@link android.accessibilityservice.AccessibilityService} runs in the background and + receives callbacks by the system when {@link android.view.accessibility.AccessibilityEvent}s + are fired. Such events denote some state transition in the user interface, for example, the + focus has changed, a button has been clicked, etc. Such a service can optionally request the + capability for querying the content of the active window. Development of an accessibility + service requires extends this class and implements its abstract methods. +</p> +<p> + An {@link android.accessibilityservice.AccessibilityServiceInfo} describes an + {@link android.accessibilityservice.AccessibilityService}. The system notifies an + AccessibilityService for {@link android.view.accessibility.AccessibilityEvent}s + according to the information encapsulated in this class. +</p> +</body> +</html> diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 85f40c92963a..fdf4a3af906d 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -251,12 +251,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM IBinder b = data.readStrongBinder(); IApplicationThread app = b != null ? ApplicationThreadNative.asInterface(b) : null; + String packageName = data.readString(); b = data.readStrongBinder(); IIntentReceiver rec = b != null ? IIntentReceiver.Stub.asInterface(b) : null; IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data); String perm = data.readString(); - Intent intent = registerReceiver(app, rec, filter, perm); + Intent intent = registerReceiver(app, packageName, rec, filter, perm); reply.writeNoException(); if (intent != null) { reply.writeInt(1); @@ -1503,6 +1504,16 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IIntentSender r = IIntentSender.Stub.asInterface( + data.readStrongBinder()); + boolean res = isIntentSenderTargetedToPackage(r); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -1702,7 +1713,7 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return res; } - public Intent registerReceiver(IApplicationThread caller, + public Intent registerReceiver(IApplicationThread caller, String packageName, IIntentReceiver receiver, IntentFilter filter, String perm) throws RemoteException { @@ -1710,6 +1721,7 @@ class ActivityManagerProxy implements IActivityManager Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); + data.writeString(packageName); data.writeStrongBinder(receiver != null ? receiver.asBinder() : null); filter.writeToParcel(data, 0); data.writeString(perm); @@ -3385,5 +3397,18 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(sender.asBinder()); + mRemote.transact(IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION, data, reply, 0); + reply.readException(); + boolean res = reply.readInt() != 0; + data.recycle(); + reply.recycle(); + return res; + } + private IBinder mRemote; } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 94a4afaf5629..8749d3e17e0e 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -61,7 +61,6 @@ import android.net.wifi.IWifiManager; import android.net.wifi.WifiManager; import android.nfc.NfcManager; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.DropBoxManager; import android.os.Environment; @@ -81,7 +80,6 @@ import android.content.ClipboardManager; import android.util.AndroidRuntimeException; import android.util.Log; import android.view.ContextThemeWrapper; -import android.view.Display; import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.InputMethodManager; @@ -142,6 +140,7 @@ class ContextImpl extends Context { new HashMap<String, SharedPreferencesImpl>(); /*package*/ LoadedApk mPackageInfo; + private String mBasePackageName; private Resources mResources; /*package*/ ActivityThread mMainThread; private Context mOuterContext; @@ -1030,7 +1029,7 @@ class ContextImpl extends Context { } try { return ActivityManagerNative.getDefault().registerReceiver( - mMainThread.getApplicationThread(), + mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission); } catch (RemoteException e) { return null; @@ -1397,7 +1396,7 @@ class ContextImpl extends Context { if (pi != null) { ContextImpl c = new ContextImpl(); c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; - c.init(pi, null, mMainThread, mResources); + c.init(pi, null, mMainThread, mResources, mBasePackageName); if (c.mResources != null) { return c; } @@ -1450,6 +1449,7 @@ class ContextImpl extends Context { */ public ContextImpl(ContextImpl context) { mPackageInfo = context.mPackageInfo; + mBasePackageName = context.mBasePackageName; mResources = context.mResources; mMainThread = context.mMainThread; mContentResolver = context.mContentResolver; @@ -1458,13 +1458,14 @@ class ContextImpl extends Context { final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) { - init(packageInfo, activityToken, mainThread, null); + init(packageInfo, activityToken, mainThread, null, null); } final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread, - Resources container) { + Resources container, String basePackageName) { mPackageInfo = packageInfo; + mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName; mResources = mPackageInfo.getResources(mainThread); if (mResources != null && container != null @@ -1485,6 +1486,7 @@ class ContextImpl extends Context { final void init(Resources resources, ActivityThread mainThread) { mPackageInfo = null; + mBasePackageName = null; mResources = resources; mMainThread = mainThread; mContentResolver = new ApplicationContentResolver(this, mainThread); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index e2588cfb5eb1..9e207646a9a2 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -103,7 +103,7 @@ public interface IActivityManager extends IInterface { throws RemoteException; public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException; public boolean willActivityBeVisible(IBinder token) throws RemoteException; - public Intent registerReceiver(IApplicationThread caller, + public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String requiredPermission) throws RemoteException; public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException; @@ -361,6 +361,8 @@ public interface IActivityManager extends IInterface { public void registerProcessObserver(IProcessObserver observer) throws RemoteException; public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException; + public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -587,4 +589,5 @@ public interface IActivityManager extends IInterface { int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131; int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+132; int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+133; + int IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+134; } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 5b43b650abb1..b4827cbb7a65 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -365,7 +365,7 @@ public final class PendingIntent implements Parcelable { * is no longer allowing more intents to be sent through it. */ public void send() throws CanceledException { - send(null, 0, null, null, null); + send(null, 0, null, null, null, null); } /** @@ -379,7 +379,7 @@ public final class PendingIntent implements Parcelable { * is no longer allowing more intents to be sent through it. */ public void send(int code) throws CanceledException { - send(null, code, null, null, null); + send(null, code, null, null, null, null); } /** @@ -399,7 +399,7 @@ public final class PendingIntent implements Parcelable { */ public void send(Context context, int code, Intent intent) throws CanceledException { - send(context, code, intent, null, null); + send(context, code, intent, null, null, null); } /** @@ -420,7 +420,7 @@ public final class PendingIntent implements Parcelable { */ public void send(int code, OnFinished onFinished, Handler handler) throws CanceledException { - send(null, code, null, onFinished, handler); + send(null, code, null, onFinished, handler, null); } /** @@ -449,20 +449,64 @@ public final class PendingIntent implements Parcelable { * @see #send(int) * @see #send(Context, int, Intent) * @see #send(int, android.app.PendingIntent.OnFinished, Handler) + * @see #send(Context, int, Intent, OnFinished, Handler, String) * * @throws CanceledException Throws CanceledException if the PendingIntent * is no longer allowing more intents to be sent through it. */ public void send(Context context, int code, Intent intent, OnFinished onFinished, Handler handler) throws CanceledException { + send(context, code, intent, onFinished, handler, null); + } + + /** + * Perform the operation associated with this PendingIntent, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * <p>For the intent parameter, a PendingIntent + * often has restrictions on which fields can be supplied here, based on + * how the PendingIntent was retrieved in {@link #getActivity}, + * {@link #getBroadcast}, or {@link #getService}. + * + * @param context The Context of the caller. This may be null if + * <var>intent</var> is also null. + * @param code Result code to supply back to the PendingIntent's target. + * @param intent Additional Intent data. See {@link Intent#fillIn + * Intent.fillIn()} for information on how this is applied to the + * original Intent. Use null to not modify the original Intent. + * @param onFinished The object to call back on when the send has + * completed, or null for no callback. + * @param handler Handler identifying the thread on which the callback + * should happen. If null, the callback will happen from the thread + * pool of the process. + * @param requiredPermission Name of permission that a recipient of the PendingIntent + * is required to hold. This is only valid for broadcast intents, and + * corresponds to the permission argument in + * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}. + * If null, no permission is required. + * + * @see #send() + * @see #send(int) + * @see #send(Context, int, Intent) + * @see #send(int, android.app.PendingIntent.OnFinished, Handler) + * @see #send(Context, int, Intent, OnFinished, Handler) + * + * @throws CanceledException Throws CanceledException if the PendingIntent + * is no longer allowing more intents to be sent through it. + */ + public void send(Context context, int code, Intent intent, + OnFinished onFinished, Handler handler, String requiredPermission) + throws CanceledException { try { String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) : null; int res = mTarget.send(code, intent, resolvedType, onFinished != null - ? new FinishedDispatcher(this, onFinished, handler) - : null); + ? new FinishedDispatcher(this, onFinished, handler) + : null, + requiredPermission); if (res < 0) { throw new CanceledException(); } @@ -491,6 +535,20 @@ public final class PendingIntent implements Parcelable { } /** + * @hide + * Check to verify that this PendingIntent targets a specific package. + */ + public boolean isTargetedToPackage() { + try { + return ActivityManagerNative.getDefault() + .isIntentSenderTargetedToPackage(mTarget); + } catch (RemoteException e) { + // Should never happen. + return false; + } + } + + /** * Comparison operator on two PendingIntent objects, such that true * is returned then they both represent the same operation from the * same package. This allows you to use {@link #getActivity}, diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 41eea2e83b31..42eda0212e01 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -188,8 +188,9 @@ public class SearchDialog extends Dialog { mSearchPlate = mSearchView.findViewById(com.android.internal.R.id.search_plate); mWorkingSpinner = getContext().getResources(). getDrawable(com.android.internal.R.drawable.search_spinner); - mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( - null, null, mWorkingSpinner, null); + // TODO: Restore the spinner for slow suggestion lookups + // mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( + // null, null, mWorkingSpinner, null); setWorking(false); // pre-hide all the extraneous elements diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 85a2fa831f65..7274362e77b5 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -329,6 +329,15 @@ public class SearchManager public final static String SUGGEST_COLUMN_FLAGS = "suggest_flags"; /** + * Column name for suggestions cursor. <i>Optional.</i> This column may be + * used to specify the time in (@link System#currentTimeMillis + * System.currentTImeMillis()} (wall time in UTC) when an item was last + * accessed within the results-providing application. If set, this may be + * used to show more-recently-used items first. + */ + public final static String SUGGEST_COLUMN_LAST_ACCESS_HINT = "suggest_last_access_hint"; + + /** * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion * should not be stored as a shortcut in global search. */ diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index aecec66b9f30..fed6d815c9f2 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1029,6 +1029,12 @@ public abstract class Context { * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * + * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers + * registered with this method will correctly respect the + * {@link Intent#setPackage(String)} specified for an Intent being broadcast. + * Prior to that, it would be ignored and delivered to all matching registered + * receivers. Be careful if using this for security.</p> + * * <p class="note">Note: this method <em>cannot be called from a * {@link BroadcastReceiver} component;</em> that is, from a BroadcastReceiver * that is declared in an application's manifest. It is okay, however, to call @@ -1059,6 +1065,12 @@ public abstract class Context { * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * + * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers + * registered with this method will correctly respect the + * {@link Intent#setPackage(String)} specified for an Intent being broadcast. + * Prior to that, it would be ignored and delivered to all matching registered + * receivers. Be careful if using this for security.</p> + * * @param receiver The BroadcastReceiver to handle the broadcast. * @param filter Selects the Intent broadcasts to be received. * @param broadcastPermission String naming a permissions that a diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl index b7da47219ce4..7dbd6f2ab92d 100644 --- a/core/java/android/content/IIntentSender.aidl +++ b/core/java/android/content/IIntentSender.aidl @@ -22,5 +22,5 @@ import android.content.Intent; /** @hide */ interface IIntentSender { int send(int code, in Intent intent, String resolvedType, - IIntentReceiver finishedReceiver); + IIntentReceiver finishedReceiver, String requiredPermission); } diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index 007a71515214..4db4bdca3657 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -154,14 +154,47 @@ public class IntentSender implements Parcelable { */ public void sendIntent(Context context, int code, Intent intent, OnFinished onFinished, Handler handler) throws SendIntentException { + sendIntent(context, code, intent, onFinished, handler, null); + } + + /** + * Perform the operation associated with this IntentSender, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * @param context The Context of the caller. This may be null if + * <var>intent</var> is also null. + * @param code Result code to supply back to the IntentSender's target. + * @param intent Additional Intent data. See {@link Intent#fillIn + * Intent.fillIn()} for information on how this is applied to the + * original Intent. Use null to not modify the original Intent. + * @param onFinished The object to call back on when the send has + * completed, or null for no callback. + * @param handler Handler identifying the thread on which the callback + * should happen. If null, the callback will happen from the thread + * pool of the process. + * @param requiredPermission Name of permission that a recipient of the PendingIntent + * is required to hold. This is only valid for broadcast intents, and + * corresponds to the permission argument in + * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}. + * If null, no permission is required. + * + * + * @throws SendIntentException Throws CanceledIntentException if the IntentSender + * is no longer allowing more intents to be sent through it. + */ + public void sendIntent(Context context, int code, Intent intent, + OnFinished onFinished, Handler handler, String requiredPermission) + throws SendIntentException { try { String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) : null; int res = mTarget.send(code, intent, resolvedType, onFinished != null - ? new FinishedDispatcher(this, onFinished, handler) - : null); + ? new FinishedDispatcher(this, onFinished, handler) + : null, + requiredPermission); if (res < 0) { throw new SendIntentException(); } diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java index 11068e5d164e..1205da7cdcc6 100755 --- a/core/java/android/content/pm/PackageStats.java +++ b/core/java/android/content/pm/PackageStats.java @@ -40,6 +40,12 @@ public class PackageStats implements Parcelable { public long cacheSize; /** + * Size of the secure container on external storage holding the + * application's code. + */ + public long externalCodeSize; + + /** * Size of the external data used by the application (e.g., * <sdcard>/Android/data/<app>) */ @@ -80,6 +86,8 @@ public class PackageStats implements Parcelable { sb.append(dataSize); sb.append(",cacheSize="); sb.append(cacheSize); + sb.append(",externalCodeSize="); + sb.append(externalCodeSize); sb.append(",externalDataSize="); sb.append(externalDataSize); sb.append(",externalCacheSize="); @@ -100,6 +108,7 @@ public class PackageStats implements Parcelable { codeSize = source.readLong(); dataSize = source.readLong(); cacheSize = source.readLong(); + externalCodeSize = source.readLong(); externalDataSize = source.readLong(); externalCacheSize = source.readLong(); externalMediaSize = source.readLong(); @@ -111,6 +120,7 @@ public class PackageStats implements Parcelable { codeSize = pStats.codeSize; dataSize = pStats.dataSize; cacheSize = pStats.cacheSize; + externalCodeSize = pStats.externalCodeSize; externalDataSize = pStats.externalDataSize; externalCacheSize = pStats.externalCacheSize; externalMediaSize = pStats.externalMediaSize; @@ -126,6 +136,7 @@ public class PackageStats implements Parcelable { dest.writeLong(codeSize); dest.writeLong(dataSize); dest.writeLong(cacheSize); + dest.writeLong(externalCodeSize); dest.writeLong(externalDataSize); dest.writeLong(externalCacheSize); dest.writeLong(externalMediaSize); diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index df8cf9a8164f..e10f2184508b 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -47,6 +47,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_APP_PRIVATE_COMMAND = 100; private static final int DO_TOGGLE_SOFT_INPUT = 105; private static final int DO_FINISH_SESSION = 110; + private static final int DO_VIEW_CLICKED = 115; HandlerCaller mCaller; InputMethodSession mInputMethodSession; @@ -133,6 +134,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession = null; return; } + case DO_VIEW_CLICKED: { + mInputMethodSession.viewClicked(msg.arg1 == 1); + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -167,7 +172,11 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd)); } - + + public void viewClicked(boolean focusChanged) { + mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0)); + } + public void updateCursor(Rect newCursor) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor)); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index feb246ed7125..9481a880fb08 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -488,7 +488,15 @@ public class InputMethodService extends AbstractInputMethodService { InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd); } - + + @Override + public void viewClicked(boolean focusChanged) { + if (!isEnabled()) { + return; + } + InputMethodService.this.onViewClicked(focusChanged); + } + /** * Call {@link InputMethodService#onUpdateCursor * InputMethodService.onUpdateCursor()}. @@ -1609,6 +1617,16 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Called when the user tapped or clicked a text view. + * IMEs can't rely on this method being called because this was not part of the original IME + * protocol, so applications with custom text editing written before this method appeared will + * not call to inform the IME of this interaction. + * @param focusChanged true if the user changed the focused view by this click. + */ + public void onViewClicked(boolean focusChanged) { + } + + /** * Called when the application has reported a new location of its text * cursor. This is only called if explicitly requested by the input method. * The default implementation does nothing. diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index ae9aa05e0bce..054825024b9a 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -33,4 +33,7 @@ interface INetworkStatsService { /** Return usage summary per UID for traffic that matches template. */ NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags); + /** Force update of statistics. */ + void forceUpdate(); + } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 21fad2c1d91e..593b2b74e301 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -49,6 +49,8 @@ public class NetworkPolicyManager { /** Reject traffic on metered networks. */ public static final int RULE_REJECT_METERED = 0x1; + private static final boolean ALLOW_PLATFORM_APP_POLICY = true; + /** * {@link Intent} action launched when user selects {@link NetworkPolicy} * warning notification. @@ -223,25 +225,27 @@ public class NetworkPolicyManager { return false; } - 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); - } + 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 system 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; + 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) { } - } catch (NameNotFoundException e) { } // nothing found above; we can apply policy to UID diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index ff6e220cac41..dd2945ca18af 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -279,10 +279,17 @@ public class NetworkStatsHistory implements Parcelable { return (long) (start + (r.nextFloat() * (end - start))); } - public void dump(String prefix, PrintWriter pw) { + public void dump(String prefix, PrintWriter pw, boolean fullHistory) { pw.print(prefix); pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration); - for (int i = 0; i < bucketCount; i++) { + + final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32); + if (start > 0) { + pw.print(prefix); + pw.print(" (omitting "); pw.print(start); pw.println(" buckets)"); + } + + for (int i = start; i < bucketCount; i++) { pw.print(prefix); pw.print(" bucketStart="); pw.print(bucketStart[i]); pw.print(" rx="); pw.print(rx[i]); @@ -293,7 +300,7 @@ public class NetworkStatsHistory implements Parcelable { @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); - dump("", new PrintWriter(writer)); + dump("", new PrintWriter(writer), false); return writer.toString(); } diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 040489e664f4..2b59dba15c0e 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -25,8 +25,9 @@ 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; @@ -92,7 +93,7 @@ public class TrafficStats { * {@link #tagSocket(Socket)}. */ public static void setThreadStatsTag(int tag) { - BlockGuard.setThreadSocketStatsTag(tag); + NetworkManagementSocketTagger.setThreadSocketStatsTag(tag); } /** @@ -104,7 +105,7 @@ public class TrafficStats { } public static void clearThreadStatsTag() { - BlockGuard.setThreadSocketStatsTag(-1); + NetworkManagementSocketTagger.setThreadSocketStatsTag(-1); } /** @@ -121,12 +122,12 @@ public class TrafficStats { * {@hide} */ public static void setThreadStatsUid(int uid) { - BlockGuard.setThreadSocketStatsUid(uid); + NetworkManagementSocketTagger.setThreadSocketStatsUid(uid); } /** {@hide} */ public static void clearThreadStatsUid() { - BlockGuard.setThreadSocketStatsUid(-1); + NetworkManagementSocketTagger.setThreadSocketStatsUid(-1); } /** @@ -139,14 +140,14 @@ public class TrafficStats { * @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/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java index 5ade9eb9fbc5..0eb8cd81bcdd 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/core/java/android/nfc/NdefRecord.java @@ -338,7 +338,15 @@ public final class NdefRecord implements Parcelable { * @hide */ public static NdefRecord createUri(Uri uri) { - String uriString = uri.toString(); + return createUri(uri.toString()); + } + + /** + * Creates an NDEF record of well known type URI. + * TODO: Make a public API + * @hide + */ + public static NdefRecord createUri(String uriString) { byte prefix = 0x0; for (int i = 1; i < URI_PREFIX_MAP.length; i++) { if (uriString.startsWith(URI_PREFIX_MAP[i])) { diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java index 54583d61f4f9..a73067a5f709 100644 --- a/core/java/android/nfc/Tag.java +++ b/core/java/android/nfc/Tag.java @@ -313,14 +313,16 @@ public final class Tag implements Parcelable { */ @Override public String toString() { - StringBuilder sb = new StringBuilder("TAG ") - .append("uid = ") - .append(mId) - .append(" Tech ["); - for (int i : mTechList) { - sb.append(i) - .append(", "); + StringBuilder sb = new StringBuilder("TAG: Tech ["); + String[] techList = getTechList(); + int length = techList.length; + for (int i = 0; i < length; i++) { + sb.append(techList[i]); + if (i < length - 1) { + sb.append(", "); + } } + sb.append("]"); return sb.toString(); } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 1b09242d4c03..fcf479631b03 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -210,6 +210,21 @@ interface INetworkManagementService NetworkStats getNetworkStatsUidDetail(int uid); /** + * Set quota for an interface. + */ + void setInterfaceQuota(String iface, long quota); + + /** + * Remove quota for an interface. + */ + void removeInterfaceQuota(String iface); + + /** + * Control network activity of a UID over interfaces with a quota limit. + */ + void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces); + + /** * Configures bandwidth throttling on an interface. */ void setInterfaceThrottle(String iface, int rxKbps, int txKbps); @@ -226,6 +241,4 @@ interface INetworkManagementService */ int getInterfaceTxThrottle(String iface); - void setBandwidthControlEnabled(boolean enabled); - } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index d475f36df9bb..05e39ac55a89 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -92,12 +92,6 @@ public class Process { public static final int SDCARD_RW_GID = 1015; /** - * Defines the UID for the KeyChain service. - * @hide - */ - public static final int KEYCHAIN_UID = 1020; - - /** * Defines the UID/GID for the NFC service process. * @hide */ diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index c2dc8aeefc43..93020601b29c 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -655,6 +655,26 @@ public interface IMountService extends IInterface { } return _result; } + + /* + * Returns the filesystem path of a mounted secure container. + */ + public String getSecureContainerFilesystemPath(String id) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + String _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(id); + mRemote.transact(Stub.TRANSACTION_getSecureContainerFilesystemPath, _data, _reply, 0); + _reply.readException(); + _result = _reply.readString(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } } private static final String DESCRIPTOR = "IMountService"; @@ -719,6 +739,8 @@ public interface IMountService extends IInterface { static final int TRANSACTION_getVolumeList = IBinder.FIRST_CALL_TRANSACTION + 29; + static final int TRANSACTION_getSecureContainerFilesystemPath = IBinder.FIRST_CALL_TRANSACTION + 30; + /** * Cast an IBinder object into an IMountService interface, generating a * proxy if needed. @@ -1031,6 +1053,15 @@ public interface IMountService extends IInterface { reply.writeParcelableArray(result, 0); return true; } + case TRANSACTION_getSecureContainerFilesystemPath: { + data.enforceInterface(DESCRIPTOR); + String id; + id = data.readString(); + String path = getSecureContainerFilesystemPath(id); + reply.writeNoException(); + reply.writeString(path); + return true; + } } return super.onTransact(code, data, reply, flags); } @@ -1210,4 +1241,6 @@ public interface IMountService extends IInterface { * Returns list of all mountable volumes. */ public Parcelable[] getVolumeList() throws RemoteException; + + public String getSecureContainerFilesystemPath(String id) throws RemoteException; } diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index 3971045f3451..b492615a50c2 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -17,9 +17,6 @@ package android.provider; -import com.android.internal.util.ArrayUtils; - -import android.accounts.Account; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.ContentProviderClient; @@ -35,13 +32,10 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.net.Uri; import android.os.RemoteException; -import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.Time; import android.util.Log; -import java.util.Arrays; - /** * <p> * The contract between the calendar provider and applications. Contains @@ -97,6 +91,8 @@ public final class CalendarContract { /** * Broadcast Action: This is the intent that gets fired when an alarm * notification needs to be posted for a reminder. + * + * @SdkConstant */ public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER"; @@ -122,8 +118,7 @@ public final class CalendarContract { /** * The content:// style URL for the top-level calendar authority */ - public static final Uri CONTENT_URI = - Uri.parse("content://" + AUTHORITY); + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); /** * An optional insert, update or delete URI parameter that allows the caller @@ -572,29 +567,6 @@ public final class CalendarContract { * </ul> */ public static class Calendars implements BaseColumns, SyncColumns, CalendarColumns { - private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?" - + " AND " - + Calendars.ACCOUNT_TYPE + "=?"; - - /** - * Helper function for generating a calendars query. This is blocking - * and should not be used on the UI thread. See - * {@link ContentResolver#query(Uri, String[], String, String[], String)} - * for more details about using the parameters. - * - * @param cr The ContentResolver to query with - * @param projection A list of columns to return - * @param selection A formatted selection string - * @param selectionArgs arguments to the selection string - * @param orderBy How to order the returned rows - * @return - */ - public static final Cursor query(ContentResolver cr, String[] projection, String selection, - String[] selectionArgs, String orderBy) { - return cr.query(CONTENT_URI, projection, selection, selectionArgs, - orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - /** * The content:// style URL for accessing Calendars */ @@ -622,7 +594,9 @@ public final class CalendarContract { * These fields are only writable by a sync adapter. To modify them the * caller must include {@link #CALLER_IS_SYNCADAPTER}, * {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the Uri's query - * parameters. + * parameters. TODO move to provider + * + * @hide */ public static final String[] SYNC_WRITABLE_COLUMNS = new String[] { ACCOUNT_NAME, @@ -737,7 +711,7 @@ public final class CalendarContract { /** * the projection used by the attendees query */ - private static final String[] PROJECTION = new String[] { + public static final String[] PROJECTION = new String[] { _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,}; private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?"; @@ -1420,37 +1394,6 @@ public final class CalendarContract { CalendarColumns { /** - * Queries all events with the given projection. This is a blocking call - * and should not be done on the UI thread. - * - * @param cr The content resolver to use for the query - * @param projection The columns to return - * @return A Cursor containing all events in the db - */ - public static final Cursor query(ContentResolver cr, String[] projection) { - return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER); - } - - /** - * Queries events using the given projection, selection filter, and - * ordering. This is a blocking call and should not be done on the UI - * thread. For selection and selectionArgs usage see - * {@link ContentResolver#query(Uri, String[], String, String[], String)} - * - * @param cr The content resolver to use for the query - * @param projection The columns to return - * @param selection Filter on the query as an SQL WHERE statement - * @param selectionArgs Args to replace any '?'s in the selection - * @param orderBy How to order the rows as an SQL ORDER BY statement - * @return A Cursor containing the matching events - */ - public static final Cursor query(ContentResolver cr, String[] projection, String selection, - String[] selectionArgs, String orderBy) { - return cr.query(CONTENT_URI, projection, selection, null, - orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - - /** * The content:// style URL for interacting with events. Appending an * event id using {@link ContentUris#withAppendedId(Uri, long)} will * specify a single event. @@ -1464,7 +1407,7 @@ public final class CalendarContract { * appended event ID. Deletion of exceptions requires both the original event ID and * the exception event ID (see {@link Uri.Builder#appendPath}). */ - public static final Uri EXCEPTION_CONTENT_URI = + public static final Uri CONTENT_EXCEPTION_URI = Uri.parse("content://" + AUTHORITY + "/exception"); /** @@ -1475,7 +1418,9 @@ public final class CalendarContract { /** * These are columns that should only ever be updated by the provider, * either because they are views mapped to another table or because they - * are used for provider only functionality. + * are used for provider only functionality. TODO move to provider + * + * @hide */ public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] { ACCOUNT_NAME, @@ -1505,7 +1450,9 @@ public final class CalendarContract { /** * These fields are only writable by a sync adapter. To modify them the * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and - * _SYNC_ACCOUNT_TYPE in the query parameters. + * _SYNC_ACCOUNT_TYPE in the query parameters. TODO move to provider. + * + * @hide */ public static final String[] SYNC_WRITABLE_COLUMNS = new String[] { _SYNC_ID, @@ -1672,11 +1619,6 @@ public final class CalendarContract { public static final String END_MINUTE = "endMinute"; } - /** - * CalendarCache stores some settings for calendar including the current - * time zone for the instaces. These settings are stored using a key/value - * scheme. - */ protected interface CalendarCacheColumns { /** * The key for the setting. Keys are defined in {@link CalendarCache}. @@ -1689,6 +1631,11 @@ public final class CalendarContract { public static final String VALUE = "value"; } + /** + * CalendarCache stores some settings for calendar including the current + * time zone for the instances. These settings are stored using a key/value + * scheme. A {@link #KEY} must be specified when updating these values. + */ public static class CalendarCache implements CalendarCacheColumns { /** * The URI to use for retrieving the properties from the Calendar db. @@ -1697,22 +1644,11 @@ public final class CalendarContract { Uri.parse("content://" + AUTHORITY + "/properties"); /** - * If updating a property, this must be provided as the selection. All - * other selections will fail. For queries this field can be omitted to - * retrieve all properties or used to query a single property. Valid - * keys include {@link #TIMEZONE_KEY_TYPE}, - * {@link #TIMEZONE_KEY_INSTANCES}, and - * {@link #TIMEZONE_KEY_INSTANCES_PREVIOUS}, though the last one can - * only be read, not written. - */ - public static final String WHERE = "key=?"; - - /** * They key for updating the use of auto/home time zones in Calendar. * Valid values are {@link #TIMEZONE_TYPE_AUTO} or * {@link #TIMEZONE_TYPE_HOME}. */ - public static final String TIMEZONE_KEY_TYPE = "timezoneType"; + public static final String KEY_TIMEZONE_TYPE = "timezoneType"; /** * The key for updating the time zone used by the provider when it @@ -1720,24 +1656,24 @@ public final class CalendarContract { * type is set to {@link #TIMEZONE_TYPE_HOME}. A valid time zone id * should be written to this field. */ - public static final String TIMEZONE_KEY_INSTANCES = "timezoneInstances"; + public static final String KEY_TIMEZONE_INSTANCES = "timezoneInstances"; /** * The key for reading the last time zone set by the user. This should * only be read by apps and it will be automatically updated whenever - * {@link #TIMEZONE_KEY_INSTANCES} is updated with + * {@link #KEY_TIMEZONE_INSTANCES} is updated with * {@link #TIMEZONE_TYPE_HOME} set. */ - public static final String TIMEZONE_KEY_INSTANCES_PREVIOUS = "timezoneInstancesPrevious"; + public static final String KEY_TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious"; /** - * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider + * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider * should stay in sync with the device's time zone. */ public static final String TIMEZONE_TYPE_AUTO = "auto"; /** - * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider + * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider * should use a fixed time zone set by the user. */ public static final String TIMEZONE_TYPE_HOME = "home"; @@ -1814,7 +1750,7 @@ public final class CalendarContract { /** * The projection used by the EventDays query. */ - private static final String[] PROJECTION = { + public static final String[] PROJECTION = { STARTDAY, ENDDAY }; private static final String SELECTION = "selected=1"; @@ -1900,7 +1836,7 @@ public final class CalendarContract { /** * The projection used by the reminders query. */ - private static final String[] PROJECTION = new String[] { + public static final String[] PROJECTION = new String[] { _ID, MINUTES, METHOD,}; @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders"); @@ -1967,17 +1903,28 @@ public final class CalendarContract { public static final String NOTIFY_TIME = "notifyTime"; /** - * The state of this alert. It starts out as {@link #SCHEDULED}, then - * when the alarm goes off, it changes to {@link #FIRED}, and then when - * the user dismisses the alarm it changes to {@link #DISMISSED}. Column + * The state of this alert. It starts out as {@link #STATE_SCHEDULED}, then + * when the alarm goes off, it changes to {@link #STATE_FIRED}, and then when + * the user dismisses the alarm it changes to {@link #STATE_DISMISSED}. Column * name. * <P>Type: INTEGER</P> */ public static final String STATE = "state"; - public static final int SCHEDULED = 0; - public static final int FIRED = 1; - public static final int DISMISSED = 2; + /** + * An alert begins in this state when it is first created. + */ + public static final int STATE_SCHEDULED = 0; + /** + * After a notification for an alert has been created it should be + * updated to fired. + */ + public static final int STATE_FIRED = 1; + /** + * Once the user has dismissed the notification the alert's state should + * be set to dismissed so it is not fired again. + */ + public static final int STATE_DISMISSED = 2; /** * The number of minutes that this alarm precedes the start time. Column @@ -2024,7 +1971,7 @@ public final class CalendarContract { private static final String WHERE_FINDNEXTALARMTIME = ALARM_TIME + ">=?"; private static final String SORT_ORDER_ALARMTIME_ASC = ALARM_TIME + " ASC"; - private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + SCHEDULED + private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + STATE_SCHEDULED + " AND " + ALARM_TIME + "<?" + " AND " + ALARM_TIME + ">?" + " AND " + END + ">=?"; @@ -2038,10 +1985,11 @@ public final class CalendarContract { public static final Uri CONTENT_URI_BY_INSTANCE = Uri.parse("content://" + AUTHORITY + "/calendar_alerts/by_instance"); - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; /** - * Helper for inserting an alarm time associated with an event + * Helper for inserting an alarm time associated with an event TODO move + * to Provider * * @hide */ @@ -2056,51 +2004,32 @@ public final class CalendarContract { values.put(CalendarAlerts.CREATION_TIME, currentTime); values.put(CalendarAlerts.RECEIVED_TIME, 0); values.put(CalendarAlerts.NOTIFY_TIME, 0); - values.put(CalendarAlerts.STATE, SCHEDULED); + values.put(CalendarAlerts.STATE, STATE_SCHEDULED); values.put(CalendarAlerts.MINUTES, minutes); return cr.insert(CONTENT_URI, values); } /** - * Queries alerts info using the given projection, selection filter, and - * ordering. This is a blocking call and should not be done on the UI - * thread. For selection and selectionArgs usage see - * {@link ContentResolver#query(Uri, String[], String, String[], String)} - * - * @param cr The content resolver to use for the query - * @param projection The columns to return - * @param selection Filter on the query as an SQL WHERE statement - * @param selectionArgs Args to replace any '?'s in the selection - * @param sortOrder How to order the rows as an SQL ORDER BY statement - * @return A Cursor containing the matching alerts - */ - public static final Cursor query(ContentResolver cr, String[] projection, - String selection, String[] selectionArgs, String sortOrder) { - return cr.query(CONTENT_URI, projection, selection, selectionArgs, - sortOrder); - } - - /** * Finds the next alarm after (or equal to) the given time and returns * the time of that alarm or -1 if no such alarm exists. This is a - * blocking call and should not be done on the UI thread. + * blocking call and should not be done on the UI thread. TODO move to + * provider * * @param cr the ContentResolver * @param millis the time in UTC milliseconds * @return the next alarm time greater than or equal to "millis", or -1 * if no such alarm exists. + * @hide */ public static final long findNextAlarmTime(ContentResolver cr, long millis) { String selection = ALARM_TIME + ">=" + millis; // TODO: construct an explicit SQL query so that we can add // "LIMIT 1" to the end and get just one result. String[] projection = new String[] { ALARM_TIME }; - Cursor cursor = query(cr, projection, - WHERE_FINDNEXTALARMTIME, - new String[] { + Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_FINDNEXTALARMTIME, + (new String[] { Long.toString(millis) - }, - SORT_ORDER_ALARMTIME_ASC); + }), SORT_ORDER_ALARMTIME_ASC); long alarmTime = -1; try { if (cursor != null && cursor.moveToFirst()) { @@ -2116,13 +2045,14 @@ public final class CalendarContract { /** * Searches the CalendarAlerts table for alarms that should have fired - * but have not and then reschedules them. This method can be called - * at boot time to restore alarms that may have been lost due to a - * phone reboot. + * but have not and then reschedules them. This method can be called at + * boot time to restore alarms that may have been lost due to a phone + * reboot. TODO move to provider * * @param cr the ContentResolver * @param context the Context * @param manager the AlarmManager + * @hide */ public static final void rescheduleMissedAlarms(ContentResolver cr, Context context, AlarmManager manager) { @@ -2136,15 +2066,10 @@ public final class CalendarContract { // TODO: construct an explicit SQL query so that we can add // "GROUPBY" instead of doing a sort and de-dup - Cursor cursor = CalendarAlerts.query(cr, - projection, - WHERE_RESCHEDULE_MISSED_ALARMS, - new String[] { - Long.toString(now), - Long.toString(ancient), - Long.toString(now) - }, - SORT_ORDER_ALARMTIME_ASC); + Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection, + WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] { + Long.toString(now), Long.toString(ancient), Long.toString(now) + }), SORT_ORDER_ALARMTIME_ASC); if (cursor == null) { return; } @@ -2177,12 +2102,13 @@ public final class CalendarContract { * keep scheduled reminders up to date but apps may use this to * implement snooze functionality without modifying the reminders table. * Scheduled alarms will generate an intent using - * {@link #ACTION_EVENT_REMINDER}. + * {@link #ACTION_EVENT_REMINDER}. TODO Move to provider * * @param context A context for referencing system resources * @param manager The AlarmManager to use or null * @param alarmTime The time to fire the intent in UTC millis since * epoch + * @hide */ public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) { if (DEBUG) { @@ -2204,31 +2130,28 @@ public final class CalendarContract { } /** - * Searches for an entry in the CalendarAlerts table that matches - * the given event id, begin time and alarm time. If one is found - * then this alarm already exists and this method returns true. - * + * Searches for an entry in the CalendarAlerts table that matches the + * given event id, begin time and alarm time. If one is found then this + * alarm already exists and this method returns true. TODO Move to + * provider + * * @param cr the ContentResolver * @param eventId the event id to match * @param begin the start time of the event in UTC millis * @param alarmTime the alarm time of the event in UTC millis - * @return true if there is already an alarm for the given event - * with the same start time and alarm time. + * @return true if there is already an alarm for the given event with + * the same start time and alarm time. + * @hide */ public static final boolean alarmExists(ContentResolver cr, long eventId, long begin, long alarmTime) { // TODO: construct an explicit SQL query so that we can add // "LIMIT 1" to the end and get just one result. String[] projection = new String[] { ALARM_TIME }; - Cursor cursor = query(cr, - projection, - WHERE_ALARM_EXISTS, - new String[] { - Long.toString(eventId), - Long.toString(begin), - Long.toString(alarmTime) - }, - null); + Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_ALARM_EXISTS, + (new String[] { + Long.toString(eventId), Long.toString(begin), Long.toString(alarmTime) + }), null); boolean found = false; try { if (cursor != null && cursor.getCount() > 0) { diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 09331939f616..39c6f576b5d7 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -57,6 +57,29 @@ public class CallLog { Uri.parse("content://call_log/calls/filter"); /** + * An optional URI parameter which instructs the provider to allow the operation to be + * applied to voicemail records as well. + * <p> + * TYPE: Boolean + * <p> + * Using this parameter with a value of {@code true} will result in a security error if the + * calling package does not have appropriate permissions to access voicemails. + * + * @hide + */ + public static final String ALLOW_VOICEMAILS_PARAM_KEY = "allow_voicemails"; + + /** + * Content uri with {@link #ALLOW_VOICEMAILS_PARAM_KEY} set. This can directly be used to + * access call log entries that includes voicemail records. + * + * @hide + */ + public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon() + .appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true") + .build(); + + /** * The default sort order for this table */ public static final String DEFAULT_SORT_ORDER = "date DESC"; diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index ab0cb505eb2d..d99c7605bebe 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -52,18 +52,27 @@ public class VoicemailContract { /** The authority used by the voicemail provider. */ public static final String AUTHORITY = "com.android.voicemail"; - - /** URI to insert/retrieve all voicemails. */ + /** + * URI to insert/retrieve all voicemails. + * @deprecated + */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/voicemail"); - /** URI to insert/retrieve voicemails by a given voicemail source. */ + /** + * URI to insert/retrieve voicemails by a given voicemail source. + * @deprecated + */ public static final Uri CONTENT_URI_SOURCE = Uri.parse("content://" + AUTHORITY + "/voicemail/source/"); + /** + * Parameter key used in the URI to specify the voicemail source package name. + * <p> This field must be set in all requests that originate from a voicemail source. + */ + public static final String PARAM_KEY_SOURCE_PACKAGE = "source_package"; // TODO: Move ACTION_NEW_VOICEMAIL to the Intent class. /** Broadcast intent when a new voicemail record is inserted. */ public static final String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL"; - /** * Extra included in {@value Intent#ACTION_PROVIDER_CHANGED} and * {@value #ACTION_NEW_VOICEMAIL} broadcast intents to indicate if the receiving @@ -71,15 +80,27 @@ public class VoicemailContract { */ public static final String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE"; - /** The mime type for a collection of voicemails. */ - public static final String DIR_TYPE = - "vnd.android.cursor.dir/voicemails"; + /** + * The mime type for a collection of voicemails. + * @deprecated */ + public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemails"; + /** Defines fields exposed through the /voicemail path of this content provider. */ public static final class Voicemails implements BaseColumns { /** Not instantiable. */ private Voicemails() { } + /** URI to insert/retrieve voicemails by a given voicemail source. */ + public static final Uri CONTENT_URI = + Uri.parse("content://" + AUTHORITY + "/voicemail"); + /** URI to insert/retrieve voicemails by a given voicemail source. */ + public static final Uri CONTENT_URI_SOURCE = + Uri.parse("content://" + AUTHORITY + "/voicemail/source/"); + + /** The mime type for a collection of voicemails. */ + public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemails"; + /** * Phone number of the voicemail sender. * <P>Type: TEXT</P> @@ -143,5 +164,101 @@ public class VoicemailContract { * @hide */ public static final String _DATA = "_data"; + + /** + * A convenience method to build voicemail URI specific to a source package by appending + * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI. + */ + public static Uri buildSourceUri(String packageName) { + return Voicemails.CONTENT_URI.buildUpon() + .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build(); + } + } + + /** Defines fields exposed through the /status path of this content provider. */ + public static final class Status implements BaseColumns { + /** URI to insert/retrieve status of voicemail source. */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/status"); + /** The mime type for a collection of voicemail source statuses. */ + public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemail.source.status"; + /** The mime type for a collection of voicemails. */ + public static final String ITEM_TYPE = "vnd.android.cursor.item/voicemail.source.status"; + + /** Not instantiable. */ + private Status() { + } + /** + * The package name of the voicemail source. There can only be a one entry per source. + * <P>Type: TEXT</P> + */ + public static final String SOURCE_PACKAGE = "source_package"; + /** + * The URI to call to invoke source specific voicemail settings screen. On a user request + * to setup voicemail an intent with action VIEW with this URI will be fired by the system. + * <P>Type: TEXT</P> + */ + public static final String SETTINGS_URI = "settings_uri"; + /** + * The URI to call when the user requests to directly access the voicemail from the remote + * server. In case of an IVR voicemail system this is typically set to the the voicemail + * number specified using a tel:/ URI. + * <P>Type: TEXT</P> + */ + public static final String VOICEMAIL_ACCESS_URI = "voicemail_access_uri"; + /** + * The configuration state of the voicemail source. + * <P> Possible values: + * {@link #CONFIGURATION_STATE_OK}, + * {@link #CONFIGURATION_STATE_NOT_CONFIGURED}, + * {@link #CONFIGURATION_STATE_CAN_BE_CONFIGURED} + * <P>Type: INTEGER</P> + */ + public static final String CONFIGURATION_STATE = "configuration_state"; + public static final int CONFIGURATION_STATE_OK = 0; + public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; + /** + * This state must be used when the source has verified that the current user can be + * upgraded to visual voicemail and would like to show a set up invitation message. + */ + public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; + /** + * The data channel state of the voicemail source. This the channel through which the source + * pulls voicemail data from a remote server. + * <P> Possible values: + * {@link #DATA_CHANNEL_STATE_OK}, + * {@link #DATA_CHANNEL_STATE_NO_CONNECTION} + * </P> + * <P>Type: INTEGER</P> + */ + public static final String DATA_CHANNEL_STATE = "data_channel_state"; + public static final int DATA_CHANNEL_STATE_OK = 0; + public static final int DATA_CHANNEL_STATE_NO_CONNECTION = 1; + /** + * The notification channel state of the voicemail source. This is the channel through which + * the source gets notified of new voicemails on the remote server. + * <P> Possible values: + * {@link #NOTIFICATION_CHANNEL_STATE_OK}, + * {@link #NOTIFICATION_CHANNEL_STATE_NO_CONNECTION}, + * {@link #NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING} + * </P> + * <P>Type: INTEGER</P> + */ + public static final String NOTIFICATION_CHANNEL_STATE = "notification_channel_state"; + public static final int NOTIFICATION_CHANNEL_STATE_OK = 0; + public static final int NOTIFICATION_CHANNEL_STATE_NO_CONNECTION = 1; + /** + * Use this state when the notification can only tell that there are pending messages on + * the server but no details of the sender/time etc are known. + */ + public static final int NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING = 2; + + /** + * A convenience method to build status URI specific to a source package by appending + * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI. + */ + public static Uri buildSourceUri(String packageName) { + return Status.CONTENT_URI.buildUpon() + .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build(); + } } } diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index ca2212cda581..8a6fdb4fb325 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -517,6 +517,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, state); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, BLUETOOTH_PERM); if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state); @@ -530,6 +531,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, state); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, BLUETOOTH_PERM); if (DBG) log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index b5ae298f36e6..b5ae298f36e6 100755..100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 4107c5a791d6..aae9ccffc309 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1,4 +1,4 @@ -/* + /* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -266,7 +266,7 @@ public abstract class Layout { } } - Alignment align = mAlignment; + Alignment paraAlign = mAlignment; TabStops tabStops = null; boolean tabStopsIsInitialized = false; @@ -310,10 +310,10 @@ public abstract class Layout { ParagraphStyle.class); spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class); - align = mAlignment; + paraAlign = mAlignment; for (int n = spans.length-1; n >= 0; n--) { if (spans[n] instanceof AlignmentSpan) { - align = ((AlignmentSpan) spans[n]).getAlignment(); + paraAlign = ((AlignmentSpan) spans[n]).getAlignment(); break; } } @@ -360,6 +360,16 @@ public abstract class Layout { tabStopsIsInitialized = true; } + // Determine whether the line aligns to normal, opposite, or center. + Alignment align = paraAlign; + if (align == Alignment.ALIGN_LEFT) { + align = (dir == DIR_LEFT_TO_RIGHT) ? + Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; + } else if (align == Alignment.ALIGN_RIGHT) { + align = (dir == DIR_LEFT_TO_RIGHT) ? + Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; + } + int x; if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_LEFT_TO_RIGHT) { @@ -411,7 +421,9 @@ public abstract class Layout { int dir = getParagraphDirection(line); int x; - if (align == Alignment.ALIGN_NORMAL) { + if (align == Alignment.ALIGN_LEFT) { + x = left; + } else if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_LEFT_TO_RIGHT) { x = left; } else { @@ -430,7 +442,9 @@ public abstract class Layout { } } int max = (int)getLineExtent(line, tabStops, false); - if (align == Alignment.ALIGN_OPPOSITE) { + if (align == Alignment.ALIGN_RIGHT) { + x = right - max; + } else if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_LEFT_TO_RIGHT) { x = right - max; } else { @@ -738,11 +752,15 @@ public abstract class Layout { int dir = getParagraphDirection(line); Alignment align = getParagraphAlignment(line); - if (align == Alignment.ALIGN_NORMAL) { + if (align == Alignment.ALIGN_LEFT) { + return 0; + } else if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_RIGHT_TO_LEFT) return getParagraphRight(line) - getLineMax(line); else return 0; + } else if (align == Alignment.ALIGN_RIGHT) { + return mWidth - getLineMax(line); } else if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_RIGHT_TO_LEFT) return 0; @@ -765,11 +783,15 @@ public abstract class Layout { int dir = getParagraphDirection(line); Alignment align = getParagraphAlignment(line); - if (align == Alignment.ALIGN_NORMAL) { + if (align == Alignment.ALIGN_LEFT) { + return getParagraphLeft(line) + getLineMax(line); + } else if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_RIGHT_TO_LEFT) return mWidth; else return getParagraphLeft(line) + getLineMax(line); + } else if (align == Alignment.ALIGN_RIGHT) { + return mWidth; } else if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_RIGHT_TO_LEFT) return getLineMax(line); @@ -1765,8 +1787,10 @@ public abstract class Layout { ALIGN_NORMAL, ALIGN_OPPOSITE, ALIGN_CENTER, - // XXX ALIGN_LEFT, - // XXX ALIGN_RIGHT, + /** @hide */ + ALIGN_LEFT, + /** @hide */ + ALIGN_RIGHT, } private static final int TAB_INCREMENT = 20; diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java index 49f3bbe1bcd7..9b081b207dff 100644 --- a/core/java/android/view/InputEventConsistencyVerifier.java +++ b/core/java/android/view/InputEventConsistencyVerifier.java @@ -239,7 +239,7 @@ public final class InputEventConsistencyVerifier { break; } } finally { - finishEvent(false); + finishEvent(); } } @@ -302,7 +302,7 @@ public final class InputEventConsistencyVerifier { problem("Source was not SOURCE_CLASS_TRACKBALL."); } } finally { - finishEvent(false); + finishEvent(); } } @@ -328,7 +328,9 @@ public final class InputEventConsistencyVerifier { mTouchEventStreamUnhandled = false; mTouchEventStreamPointers = 0; } - final boolean wasTainted = mTouchEventStreamIsTainted; + if (mTouchEventStreamIsTainted) { + event.setTainted(true); + } try { ensureMetaStateIsNormalized(event.getMetaState()); @@ -441,7 +443,7 @@ public final class InputEventConsistencyVerifier { problem("Source was not SOURCE_CLASS_POINTER."); } } finally { - finishEvent(wasTainted); + finishEvent(); } } @@ -499,7 +501,7 @@ public final class InputEventConsistencyVerifier { } } } finally { - finishEvent(false); + finishEvent(); } } @@ -591,9 +593,9 @@ public final class InputEventConsistencyVerifier { return true; } - private void finishEvent(boolean tainted) { + private void finishEvent() { if (mViolationMessage != null && mViolationMessage.length() != 0) { - if (!tainted) { + if (!mCurrentEvent.isTainted()) { // Write a log message only if the event was not already tainted. mViolationMessage.append("\n in ").append(mCaller); mViolationMessage.append("\n "); @@ -614,17 +616,14 @@ public final class InputEventConsistencyVerifier { } Log.d(mLogTag, mViolationMessage.toString()); - tainted = true; + + // Taint the event so that we do not generate additional violations from it + // further downstream. + mCurrentEvent.setTainted(true); } mViolationMessage.setLength(0); } - if (tainted) { - // Taint the event so that we do not generate additional violations from it - // further downstream. - mCurrentEvent.setTainted(true); - } - if (RECENT_EVENTS_TO_LOG != 0) { if (mRecentEvents == null) { mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG]; diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index f45e78bfef10..88f59d4bdd21 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -2885,7 +2885,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { toolTypeToString(getToolType(i))); } - msg.append(", buttonState=").append(KeyEvent.metaStateToString(getButtonState())); + msg.append(", buttonState=").append(MotionEvent.buttonStateToString(getButtonState())); msg.append(", metaState=").append(KeyEvent.metaStateToString(getMetaState())); msg.append(", flags=0x").append(Integer.toHexString(getFlags())); msg.append(", edgeFlags=0x").append(Integer.toHexString(getEdgeFlags())); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 8a72c4a14c66..12458987a63a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9135,9 +9135,15 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** - * Reset the resolved layout direction by clearing the corresponding flag + * Reset the resolved layout direction. + * + * Subclasses need to override this method to clear cached information that depends on the + * resolved layout direction, or to inform child views that inherit their layout direction. + * Overrides must also call the superclass implementation at the start of their implementation. + * + * @hide */ - void resetLayoutDirectionResolution() { + protected void resetLayoutDirectionResolution() { // Reset the current View resolution mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED; } diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java index 2b692f3e93f1..d70c79850a18 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewAncestor.java @@ -4637,13 +4637,18 @@ public final class ViewAncestor extends Handler implements ViewParent, public void run() { if (mView != null) { - // Send the event directly since we do not want to append the - // source text because this is the text for the entire window - // and we just want to notify that the content has changed. - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - mView.onInitializeAccessibilityEvent(event); - AccessibilityManager.getInstance(mView.mContext).sendAccessibilityEvent(event); + // Check again for accessibility state since this is executed delayed. + AccessibilityManager accessibilityManager = + AccessibilityManager.getInstance(mView.mContext); + if (accessibilityManager.isEnabled()) { + // Send the event directly since we do not want to append the + // source text because this is the text for the entire window + // and we just want to notify that the content has changed. + AccessibilityEvent event = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + mView.onInitializeAccessibilityEvent(event); + accessibilityManager.sendAccessibilityEvent(event); + } mIsPending = false; } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index f3a5050001ea..dbcbd6e5ae1f 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -19,6 +19,8 @@ package android.view; import android.app.AppGlobals; import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.RemoteException; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.SparseArray; @@ -219,6 +221,9 @@ public class ViewConfiguration { private final int mOverscrollDistance; private final int mOverflingDistance; + private boolean sHasPermanentMenuKey; + private boolean sHasPermanentMenuKeySet; + private static final SparseArray<ViewConfiguration> sConfigurations = new SparseArray<ViewConfiguration>(2); @@ -254,11 +259,12 @@ public class ViewConfiguration { * @see android.util.DisplayMetrics */ private ViewConfiguration(Context context) { - final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + final Resources res = context.getResources(); + final DisplayMetrics metrics = res.getDisplayMetrics(); + final Configuration config = res.getConfiguration(); final float density = metrics.density; final float sizeAndDensity; - if (context.getResources().getConfiguration().isLayoutSizeAtLeast( - Configuration.SCREENLAYOUT_SIZE_XLARGE)) { + if (config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_XLARGE)) { sizeAndDensity = density * 1.5f; } else { sizeAndDensity = density; @@ -280,6 +286,17 @@ public class ViewConfiguration { mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f); mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f); + + if (!sHasPermanentMenuKeySet) { + IWindowManager wm = Display.getWindowManager(); + try { + sHasPermanentMenuKey = wm.canStatusBarHide() && !res.getBoolean( + com.android.internal.R.bool.config_showNavigationBar); + sHasPermanentMenuKeySet = true; + } catch (RemoteException ex) { + sHasPermanentMenuKey = false; + } + } } /** @@ -640,4 +657,20 @@ public class ViewConfiguration { public static float getScrollFriction() { return SCROLL_FRICTION; } + + /** + * Report if the device has a permanent menu key available to the user. + * + * <p>As of Android 3.0, devices may not have a permanent menu key available. + * Apps should use the action bar to present menu options to users. + * However, there are some apps where the action bar is inappropriate + * or undesirable. This method may be used to detect if a menu key is present. + * If not, applications should provide another on-screen affordance to access + * functionality. + * + * @return true if a permanent menu key is present, false otherwise. + */ + public boolean hasPermanentMenuKey() { + return sHasPermanentMenuKey; + } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 5ebe4e5c64ed..41412deea742 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1746,6 +1746,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final long now = SystemClock.uptimeMillis(); event = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); syntheticEvent = true; } @@ -4998,12 +4999,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager viewAncestor.requestTransitionStart(transition); } - /** - * This method will be called when we need to reset the layout direction resolution flag - * - */ @Override - void resetLayoutDirectionResolution() { + protected void resetLayoutDirectionResolution() { super.resetLayoutDirectionResolution(); // Take care of resetting the children resolution too diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 25f01a7dc9fa..ac867690724b 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -25,40 +25,51 @@ import java.util.ArrayList; import java.util.List; /** + * <p> * This class represents accessibility events that are sent by the system when * something notable happens in the user interface. For example, when a * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc. + * </p> * <p> * An accessibility event is fired by an individual view which populates the event with - * a record for its state and requests from its parent to send the event to interested - * parties. The parent can optionally add a record for itself before dispatching a similar - * request to its parent. A parent can also choose not to respect the request for sending - * an event. The accessibility event is sent by the topmost view in the view tree. - * Therefore, an {@link android.accessibilityservice.AccessibilityService} can explore - * all records in an accessibility event to obtain more information about the context - * in which the event was fired. + * data for its state and requests from its parent to send the event to interested + * parties. The parent can optionally add an {@link AccessibilityRecord} for itself before + * dispatching a similar request to its parent. A parent can also choose not to respect the + * request for sending an event. The accessibility event is sent by the topmost view in the + * view tree. Therefore, an {@link android.accessibilityservice.AccessibilityService} can + * explore all records in an accessibility event to obtain more information about the + * context in which the event was fired. + * </p> * <p> - * A client can add, remove, and modify records. The getters and setters for individual - * properties operate on the current record which can be explicitly set by the client. By - * default current is the first record. Thus, querying a record would require setting - * it as the current one and interacting with the property getters and setters. + * The main purpose of an accessibility event is to expose enough information for an + * {@link android.accessibilityservice.AccessibilityService} to provide meaningful feedback + * to the user. Sometimes however, an accessibility service may need more contextual + * information then the one in the event pay-load. In such cases the service can obtain + * the event source which is an {@link AccessibilityNodeInfo} (snapshot of a View state) + * which can be used for exploring the window content. Note that the privilege for accessing + * an event's source, thus the window content, has to be explicitly requested. For more + * details refer to {@link android.accessibilityservice.AccessibilityService}. If an + * accessibility service has not requested to retrieve the window content the event will + * not contain reference to its source. Also for events of type + * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available. + * </p> * <p> * This class represents various semantically different accessibility event - * types. Each event type has associated a set of related properties. In other + * types. Each event type has an associated set of related properties. In other * words, each event type is characterized via a subset of the properties exposed * by this class. For each event type there is a corresponding constant defined - * in this class. Since some event types are semantically close there are mask - * constants that group them together. Follows a specification of the event - * types and their associated properties: + * in this class. Follows a specification of the event types and their associated properties: + * </p> * <p> - * <b>VIEW TYPES</b> <br> + * <b>VIEW TYPES</b></br> + * </p> * <p> * <b>View clicked</b> - represents the event of clicking on a {@link android.view.View} - * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br> - * Type:{@link #TYPE_VIEW_CLICKED} <br> - * Properties:</br> + * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.</br> + * <em>Type:</em>{@link #TYPE_VIEW_CLICKED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -67,13 +78,14 @@ import java.util.List; * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> * </ul> + * </p> * <p> * <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View} - * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br> - * Type:{@link #TYPE_VIEW_LONG_CLICKED} <br> - * Properties:</br> + * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc </br> + * <em>Type:</em>{@link #TYPE_VIEW_LONG_CLICKED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -82,13 +94,14 @@ import java.util.List; * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> * </ul> + * </p> * <p> * <b>View selected</b> - represents the event of selecting an item usually in - * the context of an {@link android.widget.AdapterView}. <br> - * Type: {@link #TYPE_VIEW_SELECTED} <br> - * Properties:</br> + * the context of an {@link android.widget.AdapterView}.</br> + * <em>Type:</em> {@link #TYPE_VIEW_SELECTED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -96,17 +109,17 @@ import java.util.List; * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> - * <li>{@link #getItemCount()} -The number of selectable items of the source.</li> + * <li>{@link #getItemCount()} - The number of selectable items of the source.</li> * <li>{@link #getCurrentItemIndex()} - The currently selected item index.</li> * </ul> - * <p> + * </p> * <p> * <b>View focused</b> - represents the event of focusing a - * {@link android.view.View}. <br> - * Type: {@link #TYPE_VIEW_FOCUSED} <br> - * Properties:</br> + * {@link android.view.View}.</br> + * <em>Type:</em> {@link #TYPE_VIEW_FOCUSED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -114,16 +127,17 @@ import java.util.List; * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> - * <li>{@link #getItemCount()} -The number of focusable items on the screen.</li> + * <li>{@link #getItemCount()} - The number of focusable items on the screen.</li> * <li>{@link #getCurrentItemIndex()} - The currently focused item index.</li> * </ul> + * </p> * <p> * <b>View text changed</b> - represents the event of changing the text of an - * {@link android.widget.EditText}. <br> - * Type: {@link #TYPE_VIEW_TEXT_CHANGED} <br> - * Properties:</br> + * {@link android.widget.EditText}.</br> + * <em>Type:</em> {@link #TYPE_VIEW_TEXT_CHANGED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -136,13 +150,14 @@ import java.util.List; * <li>{@link #getRemovedCount()} - The number of removed characters.</li> * <li>{@link #getBeforeText()} - The text of the source before the change.</li> * </ul> + * </p> * <p> * <b>View text selection changed</b> - represents the event of changing the text - * selection of an {@link android.widget.EditText}.<br> - * Type: {@link #TYPE_VIEW_TEXT_SELECTION_CHANGED} <br> - * Properties:</br> + * selection of an {@link android.widget.EditText}.</br> + * <em>Type:</em> {@link #TYPE_VIEW_TEXT_SELECTION_CHANGED} </br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -152,7 +167,8 @@ import java.util.List; * <li>{@link #getFromIndex()} - The selection start index.</li> * <li>{@link #getToIndex()} - The selection end index.</li> * <li>{@link #getItemCount()} - The length of the source text.</li> - * <ul> + * </ul> + * </p> * <p> * <b>View scrolled</b> - represents the event of scrolling a view. If * the source is a descendant of {@link android.widget.AdapterView} the @@ -161,11 +177,11 @@ import java.util.List; * is unaware if its pixel size since its adapter is responsible for * creating views. In all other cases the scroll is reported as the current * scroll on the X and Y axis respectively plus the height of the source in - * pixels.<br> - * Type: {@link #TYPE_VIEW_SCROLLED} <br> - * Properties:</br> + * pixels.</br> + * <em>Type:</em> {@link #TYPE_VIEW_SCROLLED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -181,41 +197,49 @@ import java.util.List; * (for descendants of AdapterView).</li> * <li>{@link #getItemCount()} - The total items of the source (for descendants of AdapterView) * or the height of the source in pixels (all other cases).</li> - * <ul> - * <p> - * <b>TRANSITION TYPES</b> <br> + * </ul> + * </p> * <p> + * <b>TRANSITION TYPES</b></br> + * </p> * <b>Window state changed</b> - represents the event of opening a * {@link android.widget.PopupWindow}, {@link android.view.Menu}, - * {@link android.app.Dialog}, etc. <br> - * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br> - * Properties:</br> + * {@link android.app.Dialog}, etc.</br> + * <em>Type:</em> {@link #TYPE_WINDOW_STATE_CHANGED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> * <li>{@link #getText()} - The text of the source.</li> * </ul> + * </p> * <p> * <b>Window content changed</b> - represents the event of change in the * content of a window. This change can be adding/removing view, changing - * a view size, etc.<br> - * Type: {@link #TYPE_WINDOW_CONTENT_CHANGED} <br> - * Properties:</br> + * a view size, etc.</br> + * <p> + * <strong>Note:</strong> This event is fired only for the window source of the + * last accessibility event different from {@link #TYPE_NOTIFICATION_STATE_CHANGED}) + * and its purpose is to notify clients that the content of the user interaction + * window has changed. + * </p> + * <em>Type:</em> {@link #TYPE_WINDOW_CONTENT_CHANGED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <ul> + * </ul> * <p> - * <b>NOTIFICATION TYPES</b> <br> + * <b>NOTIFICATION TYPES</b></br> * <p> - * <b>Notification state changed</b> - represents the event showing/hiding + * <b>Notification state changed</b> - represents the event showing * {@link android.app.Notification}. - * Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED} <br> - * Properties:</br> + * <em>Type:</em> {@link #TYPE_NOTIFICATION_STATE_CHANGED}</br> + * <em>Properties:</em></br> * <ul> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> @@ -223,15 +247,17 @@ import java.util.List; * <li>{@link #getText()} - The text of the source.</li> * <li>{@link #getParcelableData()} - The posted {@link android.app.Notification}.</li> * </ul> + * </p> * <p> * <b>Security note</b> * <p> - * Since an event contains the text of its source privacy can be compromised by leaking of + * Since an event contains the text of its source privacy can be compromised by leaking * sensitive information such as passwords. To address this issue any event fired in response * to manipulation of a PASSWORD field does NOT CONTAIN the text of the password. * * @see android.view.accessibility.AccessibilityManager * @see android.accessibilityservice.AccessibilityService + * @see AccessibilityNodeInfo */ public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable { private static final boolean DEBUG = false; @@ -285,13 +311,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010; /** - * Represents the event of opening/closing a {@link android.widget.PopupWindow}, + * Represents the event of opening a {@link android.widget.PopupWindow}, * {@link android.view.Menu}, {@link android.app.Dialog}, etc. */ public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020; /** - * Represents the event showing/hiding a {@link android.app.Notification}. + * Represents the event showing a {@link android.app.Notification}. */ public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040; @@ -340,6 +366,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @see #TYPE_VIEW_TEXT_CHANGED * @see #TYPE_WINDOW_STATE_CHANGED * @see #TYPE_NOTIFICATION_STATE_CHANGED + * @see #TYPE_VIEW_HOVER_ENTER + * @see #TYPE_VIEW_HOVER_EXIT + * @see #TYPE_TOUCH_EXPLORATION_GESTURE_START + * @see #TYPE_TOUCH_EXPLORATION_GESTURE_END + * @see #TYPE_WINDOW_CONTENT_CHANGED + * @see #TYPE_VIEW_SCROLLED + * @see #TYPE_VIEW_TEXT_SELECTION_CHANGED */ public static final int TYPES_ALL_MASK = 0xFFFFFFFF; @@ -432,10 +465,10 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** - * Gets the records at a given index. + * Gets the record at a given index. * * @param index The index. - * @return The records at the specified index. + * @return The record at the specified index. */ public AccessibilityRecord getRecord(int index) { return mRecords.get(index); @@ -506,7 +539,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * Returns a cached instance if such is available or a new one is - * instantiated with type property set. + * instantiated with its type property set. * * @param eventType The event type. * @return An instance. @@ -519,7 +552,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * Returns a cached instance if such is available or a new one is - * instantiated with type property set. + * initialized with from the given <code>event</code>. * * @param event The other event. * @return An instance. @@ -559,9 +592,10 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** - * Return an instance back to be reused. + * Recycles an instance back to be reused. * <p> - * <b>Note: You must not touch the object after calling this function.</b> + * <b>Note: You must not touch the object after calling this function.</b> + * </p> * * @throws IllegalStateException If the event is already recycled. */ @@ -714,7 +748,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("; EventType: ").append(eventTypeToString(mEventType)); + builder.append("EventType: ").append(eventTypeToString(mEventType)); builder.append("; EventTime: ").append(mEventTime); builder.append("; PackageName: ").append(mPackageName); builder.append(super.toString()); @@ -758,11 +792,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * Returns the string representation of an event type. For example, * {@link #TYPE_VIEW_CLICKED} is represented by the string TYPE_VIEW_CLICKED. * - * @param feedbackType The event type + * @param eventType The event type * @return The string representation. */ - public static String eventTypeToString(int feedbackType) { - switch (feedbackType) { + public static String eventTypeToString(int eventType) { + switch (eventType) { case TYPE_VIEW_CLICKED: return "TYPE_VIEW_CLICKED"; case TYPE_VIEW_LONG_CLICKED: diff --git a/core/java/android/view/accessibility/AccessibilityEventSource.java b/core/java/android/view/accessibility/AccessibilityEventSource.java index 3d70959b1ef9..f11880bc8c07 100644 --- a/core/java/android/view/accessibility/AccessibilityEventSource.java +++ b/core/java/android/view/accessibility/AccessibilityEventSource.java @@ -24,11 +24,12 @@ public interface AccessibilityEventSource { /** * Handles the request for sending an {@link AccessibilityEvent} given * the event type. The method must first check if accessibility is on - * via calling {@link AccessibilityManager#isEnabled()}, obtain - * an {@link AccessibilityEvent} from the event pool through calling - * {@link AccessibilityEvent#obtain(int)}, populate the event, and - * send it for dispatch via calling - * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent)}. + * via calling {@link AccessibilityManager#isEnabled() AccessibilityManager.isEnabled()}, + * obtain an {@link AccessibilityEvent} from the event pool through calling + * {@link AccessibilityEvent#obtain(int) AccessibilityEvent.obtain(int)}, populate the + * event, and send it for dispatch via calling + * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent) + * AccessibilityManager.sendAccessibilityEvent(AccessibilityEvent)}. * * @see AccessibilityEvent * @see AccessibilityManager @@ -41,7 +42,8 @@ public interface AccessibilityEventSource { * Handles the request for sending an {@link AccessibilityEvent}. The * method does not guarantee to check if accessibility is on before * sending the event for dispatch. It is responsibility of the caller - * to do the check via calling {@link AccessibilityManager#isEnabled()}. + * to do the check via calling {@link AccessibilityManager#isEnabled() + * AccessibilityManager.isEnabled()}. * * @see AccessibilityEvent * @see AccessibilityManager diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index eece64af3e16..314b7ca35821 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -37,16 +37,30 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** - * System level service that serves as an event dispatch for {@link AccessibilityEvent}s. - * Such events are generated when something notable happens in the user interface, + * System level service that serves as an event dispatch for {@link AccessibilityEvent}s, + * and provides facilities for querying the accessibility state of the system. + * Accessibility events are generated when something notable happens in the user interface, * for example an {@link android.app.Activity} starts, the focus or selection of a * {@link android.view.View} changes etc. Parties interested in handling accessibility * events implement and register an accessibility service which extends * {@link android.accessibilityservice.AccessibilityService}. + * <p> + * To obtain a handle to the accessibility manager do the following: + * </p> + * <p> + * <code> + * <pre> + * AccessibilityManager accessibilityManager = + * (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); + * </pre> + * </code> + * </p> * * @see AccessibilityEvent + * @see AccessibilityNodeInfo * @see android.accessibilityservice.AccessibilityService - * @see android.content.Context#getSystemService + * @see Context#getSystemService + * @see Context#ACCESSIBILITY_SERVICE */ public final class AccessibilityManager { private static final boolean DEBUG = false; @@ -72,10 +86,11 @@ public final class AccessibilityManager { * Listener for the accessibility state. */ public interface AccessibilityStateChangeListener { + /** * Called back on change in the accessibility state. * - * @param enabled + * @param enabled Whether accessibility is enabled. */ public void onAccessibilityStateChanged(boolean enabled); } @@ -142,9 +157,9 @@ public final class AccessibilityManager { } /** - * Returns if the {@link AccessibilityManager} is enabled. + * Returns if the accessibility in the system is enabled. * - * @return True if this {@link AccessibilityManager} is enabled, false otherwise. + * @return True if accessibility is enabled, false otherwise. */ public boolean isEnabled() { synchronized (mHandler) { @@ -161,17 +176,15 @@ public final class AccessibilityManager { * @hide */ public IAccessibilityManagerClient getClient() { - return (IAccessibilityManagerClient) mClient.asBinder(); + return (IAccessibilityManagerClient) mClient.asBinder(); } /** - * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not - * enabled the call is a NOOP. + * Sends an {@link AccessibilityEvent}. * - * @param event The {@link AccessibilityEvent}. + * @param event The event to send. * - * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent} - * while accessibility is not enabled. + * @throws IllegalStateException if accessibility is not enabled. */ public void sendAccessibilityEvent(AccessibilityEvent event) { if (!mIsEnabled) { @@ -199,7 +212,7 @@ public final class AccessibilityManager { } /** - * Requests interruption of the accessibility feedback from all accessibility services. + * Requests feedback interruption from all accessibility services. */ public void interrupt() { if (!mIsEnabled) { @@ -256,13 +269,20 @@ public final class AccessibilityManager { * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services * for a given feedback type. * - * @param feedbackType The feedback type (can be bitwise or of multiple types). + * @param feedbackTypeFlags The feedback type flags. * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. + * + * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE + * @see AccessibilityServiceInfo#FEEDBACK_GENERIC + * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC + * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN + * @see AccessibilityServiceInfo#FEEDBACK_VISUAL */ - public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) { + public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( + int feedbackTypeFlags) { List<AccessibilityServiceInfo> services = null; try { - services = mService.getEnabledAccessibilityServiceList(feedbackType); + services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags); if (DEBUG) { Log.i(LOG_TAG, "Installed AccessibilityServices " + services); } @@ -273,7 +293,8 @@ public final class AccessibilityManager { } /** - * Registers an {@link AccessibilityStateChangeListener}. + * Registers an {@link AccessibilityStateChangeListener} for changes in + * the global accessibility state of the system. * * @param listener The listener. * @return True if successfully registered. diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index dbbe7be4d533..031c6aeffd21 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -22,7 +22,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.text.TextUtils; -import android.util.SparseArray; import android.util.SparseIntArray; import android.view.View; @@ -30,12 +29,26 @@ import java.util.Collections; import java.util.List; /** - * This class represents a node of the screen content. From the point of - * view of an accessibility service the screen content is presented as tree - * of accessibility nodes. + * This class represents a node of the window content as well as actions that + * can be requested from its source. From the point of view of an + * {@link android.accessibilityservice.AccessibilityService} a window content is + * presented as tree of accessibility node info which may or may not map one-to-one + * to the view hierarchy. In other words, a custom view is free to report itself as + * a tree of accessibility node info. + * </p> + * <p> + * Once an accessibility node info is delivered to an accessibility service it is + * made immutable and calling a state mutation method generates an error. + * </p> + * <p> + * Please refer to {@link android.accessibilityservice.AccessibilityService} for + * details about how to obtain a handle to window content as a tree of accessibility + * node info as well as familiarizing with the security model. + * </p> * - * TODO(svertoslavganov): Update the documentation, add sample, and describe - * the security policy. + * @see android.accessibilityservice.AccessibilityService + * @see AccessibilityEvent + * @see AccessibilityManager */ public class AccessibilityNodeInfo implements Parcelable { @@ -85,9 +98,6 @@ public class AccessibilityNodeInfo implements Parcelable { private static final int PROPERTY_SCROLLABLE = 0x00000200; - // Readable representations - lazily initialized. - private static SparseArray<String> sActionSymbolicNames; - // Housekeeping. private static final int MAX_POOL_SIZE = 50; private static final Object sPoolLock = new Object(); @@ -154,12 +164,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Get the child at given index. * <p> - * <strong> - * It is a client responsibility to recycle the received info by - * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating - * of multiple instances. - * </strong> + * <strong>Note:</strong> It is a client responsibility to recycle the + * received info by calling {@link AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. * </p> + * * @param index The child index. * @return The child node. * @@ -184,9 +193,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Adds a child. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param child The child. * * @throws IllegalStateException If called from an AccessibilityService. @@ -215,9 +226,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Adds an action that can be performed on the node. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param action The action. * * @throws IllegalStateException If called from an AccessibilityService. @@ -230,9 +243,10 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Performs an action on the node. * <p> - * Note: An action can be performed only if the request is made + * <strong>Note:</strong> An action can be performed only if the request is made * from an {@link android.accessibilityservice.AccessibilityService}. * </p> + * * @param action The action to perform. * @return True if the action was performed. * @@ -256,6 +270,11 @@ public class AccessibilityNodeInfo implements Parcelable { * Finds {@link AccessibilityNodeInfo}s by text. The match is case * insensitive containment. The search is relative to this info i.e. * this info is the root of the traversed tree. + * <p> + * <strong>Note:</strong> It is a client responsibility to recycle the + * received info by calling {@link AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. + * </p> * * @param text The searched text. * @return A list of node info. @@ -277,12 +296,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Gets the unique id identifying this node's parent. * <p> - * <strong> - * It is a client responsibility to recycle the received info by - * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating - * of multiple instances. - * </strong> + * <strong>Note:</strong> It is a client responsibility to recycle the + * received info by calling {@link AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. * </p> + * * @return The node's patent id. */ public AccessibilityNodeInfo getParent() { @@ -302,9 +320,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the parent. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param parent The parent. * * @throws IllegalStateException If called from an AccessibilityService. @@ -327,9 +347,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the node bounds in parent coordinates. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param bounds The node bounds. * * @throws IllegalStateException If called from an AccessibilityService. @@ -352,9 +374,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the node bounds in screen coordinates. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param bounds The node bounds. * * @throws IllegalStateException If called from an AccessibilityService. @@ -376,9 +400,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is checkable. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param checkable True if the node is checkable. * * @throws IllegalStateException If called from an AccessibilityService. @@ -399,9 +425,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is checked. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param checked True if the node is checked. * * @throws IllegalStateException If called from an AccessibilityService. @@ -422,9 +450,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is focusable. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param focusable True if the node is focusable. * * @throws IllegalStateException If called from an AccessibilityService. @@ -445,9 +475,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is focused. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param focused True if the node is focused. * * @throws IllegalStateException If called from an AccessibilityService. @@ -468,9 +500,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is selected. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param selected True if the node is selected. * * @throws IllegalStateException If called from an AccessibilityService. @@ -491,9 +525,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is clickable. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param clickable True if the node is clickable. * * @throws IllegalStateException If called from an AccessibilityService. @@ -514,9 +550,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is long clickable. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param longClickable True if the node is long clickable. * * @throws IllegalStateException If called from an AccessibilityService. @@ -537,9 +575,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is enabled. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param enabled True if the node is enabled. * * @throws IllegalStateException If called from an AccessibilityService. @@ -560,9 +600,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is a password. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param password True if the node is a password. * * @throws IllegalStateException If called from an AccessibilityService. @@ -582,6 +624,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets if the node is scrollable. + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> * * @param scrollable True if the node is scrollable, false otherwise. * @@ -604,9 +651,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the package this node comes from. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param packageName The package name. * * @throws IllegalStateException If called from an AccessibilityService. @@ -628,9 +677,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the class this node comes from. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param className The class name. * * @throws IllegalStateException If called from an AccessibilityService. @@ -652,9 +703,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the text of this node. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param text The text. * * @throws IllegalStateException If called from an AccessibilityService. @@ -676,9 +729,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the content description of this node. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param contentDescription The content description. * * @throws IllegalStateException If called from an AccessibilityService. @@ -820,7 +875,7 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Return an instance back to be reused. * <p> - * <b>Note: You must not touch the object after calling this function.</b> + * <strong>Note:</strong> You must not touch the object after calling this function. * * @throws IllegalStateException If the info is already recycled. */ @@ -842,8 +897,8 @@ public class AccessibilityNodeInfo implements Parcelable { /** * {@inheritDoc} * <p> - * <b>Note: After the instance is written to a parcel it is recycled. - * You must not touch the object after calling this function.</b> + * <strong>Note:</strong> After the instance is written to a parcel it + * is recycled. You must not touch the object after calling this function. * </p> */ public void writeToParcel(Parcel parcel, int flags) { @@ -885,7 +940,7 @@ public class AccessibilityNodeInfo implements Parcelable { TextUtils.writeToParcel(mContentDescription, parcel, flags); // Since instances of this class are fetched via synchronous i.e. blocking - // calls in IPCs and we always recycle as soon as the instance is marshaled. + // calls in IPCs we always recycle as soon as the instance is marshaled. recycle(); } @@ -957,15 +1012,18 @@ public class AccessibilityNodeInfo implements Parcelable { * @return The symbolic name. */ private static String getActionSymbolicName(int action) { - SparseArray<String> actionSymbolicNames = sActionSymbolicNames; - if (actionSymbolicNames == null) { - actionSymbolicNames = sActionSymbolicNames = new SparseArray<String>(); - actionSymbolicNames.put(ACTION_FOCUS, "ACTION_FOCUS"); - actionSymbolicNames.put(ACTION_CLEAR_FOCUS, "ACTION_UNFOCUS"); - actionSymbolicNames.put(ACTION_SELECT, "ACTION_SELECT"); - actionSymbolicNames.put(ACTION_CLEAR_SELECTION, "ACTION_UNSELECT"); + switch (action) { + case ACTION_FOCUS: + return "ACTION_FOCUS"; + case ACTION_CLEAR_FOCUS: + return "ACTION_CLEAR_FOCUS"; + case ACTION_SELECT: + return "ACTION_SELECT"; + case ACTION_CLEAR_SELECTION: + return "ACTION_CLEAR_SELECTION"; + default: + throw new IllegalArgumentException("Unknown action: " + action); } - return actionSymbolicNames.get(action); } private boolean canPerformRequestOverConnection(int accessibilityViewId) { diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index b9815c5bbaef..f4d5e897e422 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -25,12 +25,28 @@ import java.util.ArrayList; import java.util.List; /** - * Represents a record in an accessibility event. This class encapsulates - * the information for a {@link android.view.View}. Note that not all properties - * are applicable to all view types. For detailed information please refer to - * {@link AccessibilityEvent}. + * Represents a record in an {@link AccessibilityEvent} and contains information + * about state change of its source {@link android.view.View}. When a view fires + * an accessibility event it requests from its parent to dispatch the + * constructed event. The parent may optionally append a record for itself + * for providing more context to + * {@link android.accessibilityservice.AccessibilityService}s. Hence, + * accessibility services can facilitate additional accessibility records + * to enhance feedback. + * </p> + * <p> + * Once the accessibility event containing a record is dispatched the record is + * made immutable and calling a state mutation method generates an error. + * </p> + * <p> + * <strong>Note:</strong> Not all properties are applicable to all accessibility + * event types. For detailed information please refer to {@link AccessibilityEvent}. + * </p> * * @see AccessibilityEvent + * @see AccessibilityManager + * @see android.accessibilityservice.AccessibilityService + * @see AccessibilityNodeInfo */ public class AccessibilityRecord { @@ -79,32 +95,6 @@ public class AccessibilityRecord { } /** - * Initialize this record from another one. - * - * @param record The to initialize from. - */ - void init(AccessibilityRecord record) { - mSealed = record.mSealed; - mBooleanProperties = record.mBooleanProperties; - mCurrentItemIndex = record.mCurrentItemIndex; - mItemCount = record.mItemCount; - mFromIndex = record.mFromIndex; - mToIndex = record.mToIndex; - mScrollX = record.mScrollX; - mScrollY = record.mScrollY; - mAddedCount = record.mAddedCount; - mRemovedCount = record.mRemovedCount; - mClassName = record.mClassName; - mContentDescription = record.mContentDescription; - mBeforeText = record.mBeforeText; - mParcelableData = record.mParcelableData; - mText.addAll(record.mText); - mSourceWindowId = record.mSourceWindowId; - mSourceViewId = record.mSourceViewId; - mConnection = record.mConnection; - } - - /** * Sets the event source. * * @param source The source. @@ -125,13 +115,12 @@ public class AccessibilityRecord { /** * Gets the {@link AccessibilityNodeInfo} of the event source. * <p> - * <strong> - * It is a client responsibility to recycle the received info by - * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating - * of multiple instances. - * </strong> + * <strong>Note:</strong> It is a client responsibility to recycle the received info + * by calling {@link AccessibilityNodeInfo#recycle() AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. + * * </p> - * @return The info. + * @return The info of the source. */ public AccessibilityNodeInfo getSource() { enforceSealed(); @@ -641,7 +630,7 @@ public class AccessibilityRecord { /** * Return an instance back to be reused. * <p> - * <b>Note: You must not touch the object after calling this function.</b> + * <strong>Note:</strong> You must not touch the object after calling this function. * * @throws IllegalStateException If the record is already recycled. */ @@ -661,6 +650,32 @@ public class AccessibilityRecord { } /** + * Initialize this record from another one. + * + * @param record The to initialize from. + */ + void init(AccessibilityRecord record) { + mSealed = record.mSealed; + mBooleanProperties = record.mBooleanProperties; + mCurrentItemIndex = record.mCurrentItemIndex; + mItemCount = record.mItemCount; + mFromIndex = record.mFromIndex; + mToIndex = record.mToIndex; + mScrollX = record.mScrollX; + mScrollY = record.mScrollY; + mAddedCount = record.mAddedCount; + mRemovedCount = record.mRemovedCount; + mClassName = record.mClassName; + mContentDescription = record.mContentDescription; + mBeforeText = record.mBeforeText; + mParcelableData = record.mParcelableData; + mText.addAll(record.mText); + mSourceWindowId = record.mSourceWindowId; + mSourceViewId = record.mSourceViewId; + mConnection = record.mConnection; + } + + /** * Clears the state of this instance. */ void clear() { diff --git a/core/java/android/view/accessibility/package.html b/core/java/android/view/accessibility/package.html new file mode 100644 index 000000000000..4afafd3ca128 --- /dev/null +++ b/core/java/android/view/accessibility/package.html @@ -0,0 +1,39 @@ +<html> +<body> +<p> + The classes in this package are used to represent screen content and changes to it + as well as APIs for querying the global accessibility state of the system. +</p> +<p> + {@link android.view.accessibility.AccessibilityEvent}s are sent by the system when + something notable happens in the user interface. For example, when a + {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc. +</p> +<p> + {@link android.view.accessibility.AccessibilityRecord} contains information + about state change of its source {@link android.view.View}. When a view fires + an accessibility event it requests from its parent to dispatch the + constructed event. The parent may optionally append a record for itself for + providing more context to {@link android.accessibilityservice.AccessibilityService}s. + Hence, accessibility services can facilitate additional accessibility records + to enhance feedback. +</p> +<p> + {@link android.view.accessibility.AccessibilityNodeInfo} represents a node of the + window content as well as actions that can be requested from its source. From the point + of view of an {@link android.accessibilityservice.AccessibilityService} a window content is + presented as tree of accessibility node info which may or may not map one-to-one + to the view hierarchy. In other words, a custom view is free to report itself as + a tree of accessibility node info. +</p> +<p> + {@link android.view.accessibility.AccessibilityManager} is a system level service that + serves as an event dispatch for {@link android.view.accessibility.AccessibilityEvent}s, + and provides facilities for querying the accessibility state of the system. Accessibility + events are generated when something notable happens in the user interface, for example an + {@link android.app.Activity} starts, the focus or selection of a {@link android.view.View} + changes etc. Parties interested in handling accessibility events implement and register an + accessibility service which extends {@link android.accessibilityservice.AccessibilityService}. +</p> +</body> +</html> diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 47f5e4c4f9cb..a1a7281c7710 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1126,7 +1126,7 @@ public final class InputMethodManager { if (mServedView == mNextServedView && !mNextServedNeedsStart) { return; } - + InputConnection ic = null; synchronized (mH) { if (mServedView == mNextServedView && !mNextServedNeedsStart) { @@ -1242,6 +1242,27 @@ public final class InputMethodManager { } /** + * Notify the event when the user tapped or clicked the text view. + */ + public void viewClicked(View view) { + final boolean focusChanged = mServedView != mNextServedView; + checkFocus(); + synchronized (mH) { + if ((mServedView != view && (mServedView == null + || !mServedView.checkInputConnectionProxy(view))) + || mCurrentTextBoxAttribute == null || mCurMethod == null) { + return; + } + try { + if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged); + mCurMethod.viewClicked(focusChanged); + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId, e); + } + } + } + + /** * Returns true if the current input method wants to watch the location * of the input editor's cursor in its window. */ diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java index bb03afad56ae..ea6f5ee4a976 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -63,6 +63,15 @@ public interface InputMethodSession { int candidatesStart, int candidatesEnd); /** + * This method is called when the user tapped a text view. + * IMEs can't rely on this method being called because this was not part of the original IME + * protocol, so applications with custom text editing written before this method appeared will + * not call to inform the IME of this interaction. + * @param focusChanged true if the user changed the focused view by this click. + */ + public void viewClicked(boolean focusChanged); + + /** * This method is called when cursor location of the target input field * has changed within its window. This is not normally called, but will * only be reported if requested by the input method. diff --git a/core/java/android/webkit/CertTool.java b/core/java/android/webkit/CertTool.java index 4c534f93cd26..a2325c328af5 100644 --- a/core/java/android/webkit/CertTool.java +++ b/core/java/android/webkit/CertTool.java @@ -21,31 +21,27 @@ import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier; import com.android.org.bouncycastle.jce.netscape.NetscapeCertRequest; import com.android.org.bouncycastle.util.encoders.Base64; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.Intent; import android.security.Credentials; +import android.security.KeyChain; import android.util.Log; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.util.HashMap; -class CertTool { +final class CertTool { private static final String LOGTAG = "CertTool"; private static final AlgorithmIdentifier MD5_WITH_RSA = new AlgorithmIdentifier(PKCSObjectIdentifiers.md5WithRSAEncryption); - static final String CERT = Credentials.CERTIFICATE; - static final String PKCS12 = Credentials.PKCS12; - private static HashMap<String, String> sCertificateTypeMap; static { sCertificateTypeMap = new HashMap<String, String>(); - sCertificateTypeMap.put("application/x-x509-ca-cert", CertTool.CERT); - sCertificateTypeMap.put("application/x-x509-user-cert", CertTool.CERT); - sCertificateTypeMap.put("application/x-pkcs12", CertTool.PKCS12); + sCertificateTypeMap.put("application/x-x509-ca-cert", KeyChain.EXTRA_CERTIFICATE); + sCertificateTypeMap.put("application/x-x509-user-cert", KeyChain.EXTRA_CERTIFICATE); + sCertificateTypeMap.put("application/x-pkcs12", KeyChain.EXTRA_PKCS12); } static String[] getKeyStrengthList() { @@ -77,7 +73,7 @@ class CertTool { static String getCertType(String mimeType) { return sCertificateTypeMap.get(mimeType); - } + } private CertTool() {} } diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java index 4cae9d80d614..0ea27a0b9f5e 100644 --- a/core/java/android/webkit/HTML5VideoFullScreen.java +++ b/core/java/android/webkit/HTML5VideoFullScreen.java @@ -104,9 +104,14 @@ public class HTML5VideoFullScreen extends HTML5VideoView public void surfaceDestroyed(SurfaceHolder holder) { - // after we return from this we can't use the surface any more - mSurfaceHolder = null; + // After we return from this we can't use the surface any more. // The current Video View will be destroy when we play a new video. + pauseAndDispatch(mProxy); + mPlayer.release(); + mSurfaceHolder = null; + if (mMediaController != null) { + mMediaController.hide(); + } } }; @@ -210,7 +215,6 @@ public class HTML5VideoFullScreen extends HTML5VideoView // which happens when the video view is detached from its parent // view. This happens in the WebChromeClient before this method // is invoked. - pauseAndDispatch(mProxy); mProxy.dispatchOnStopFullScreen(); mLayout.removeView(getSurfaceView()); @@ -223,6 +227,10 @@ public class HTML5VideoFullScreen extends HTML5VideoView mProxy.getWebView().getViewManager().showAll(); mProxy = null; + + // Don't show the controller after exiting the full screen. + mMediaController = null; + mCurrentState = STATE_RELEASED; } }; diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java index 5983a4444e96..67660b86af02 100644 --- a/core/java/android/webkit/HTML5VideoView.java +++ b/core/java/android/webkit/HTML5VideoView.java @@ -34,6 +34,7 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener { static final int STATE_NOTPREPARED = 1; static final int STATE_PREPARED = 2; static final int STATE_PLAYING = 3; + static final int STATE_RELEASED = 4; protected int mCurrentState; protected HTML5VideoViewProxy mProxy; @@ -84,7 +85,7 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener { } public void pause() { - if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) { + if (isPlaying()) { mPlayer.pause(); } else if (mCurrentState == STATE_NOTPREPARED) { mPauseDuringPreparing = true; @@ -120,11 +121,18 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener { } public boolean isPlaying() { - return mPlayer.isPlaying(); + if (mCurrentState == STATE_PREPARED) { + return mPlayer.isPlaying(); + } else { + return false; + } } public void release() { - mPlayer.release(); + if (mCurrentState != STATE_RELEASED) { + mPlayer.release(); + } + mCurrentState = STATE_RELEASED; } public void stopPlayback() { @@ -228,7 +236,7 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener { public int getCurrentState() { - if (mPlayer.isPlaying()) { + if (isPlaying()) { return STATE_PLAYING; } else { return mCurrentState; diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java index 5b4fb1df72de..4c42cde5b693 100644 --- a/core/java/android/webkit/L10nUtils.java +++ b/core/java/android/webkit/L10nUtils.java @@ -70,7 +70,11 @@ public class L10nUtils { com.android.internal.R.string.autofill_expiration_month_re, // IDS_AUTOFILL_EXPIRATION_MONTH_RE com.android.internal.R.string.autofill_expiration_date_re, // IDS_AUTOFILL_EXPIRATION_DATE_RE com.android.internal.R.string.autofill_card_ignored_re, // IDS_AUTOFILL_CARD_IGNORED_RE - com.android.internal.R.string.autofill_fax_re // IDS_AUTOFILL_FAX_RE + com.android.internal.R.string.autofill_fax_re, // IDS_AUTOFILL_FAX_RE + com.android.internal.R.string.autofill_country_code_re, // IDS_AUTOFILL_COUNTRY_CODE_RE + com.android.internal.R.string.autofill_area_code_notext_re, // IDS_AUTOFILL_AREA_CODE_NOTEXT_RE + com.android.internal.R.string.autofill_phone_prefix_separator_re, // IDS_AUTOFILL_PHONE_PREFIX_SEPARATOR_RE + com.android.internal.R.string.autofill_phone_suffix_separator_re // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE }; private static Context mApplicationContext; diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 7e41d36eee65..4f97066aa381 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1950,6 +1950,7 @@ public final class WebViewCore { // mInitialViewState is set by didFirstLayout() and then reset in the // next webkitDraw after passing the state to the UI thread. private ViewState mInitialViewState = null; + private boolean mFirstLayoutForNonStandardLoad; static class ViewState { float mMinScale; @@ -1977,6 +1978,7 @@ public final class WebViewCore { int mMinPrefWidth; // only non-null if it is for the first picture set after the first layout ViewState mViewState; + boolean mFirstLayoutForNonStandardLoad; boolean mFocusSizeChanged; } @@ -2026,6 +2028,10 @@ public final class WebViewCore { draw.mViewState = mInitialViewState; mInitialViewState = null; } + if (mFirstLayoutForNonStandardLoad) { + draw.mFirstLayoutForNonStandardLoad = true; + mFirstLayoutForNonStandardLoad = false; + } if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); Message.obtain(mWebView.mPrivateHandler, WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); @@ -2312,6 +2318,8 @@ public final class WebViewCore { // if mViewportWidth is 0, it means device-width, always update. if (mViewportWidth != 0 && !updateViewState) { + // For non standard load, since updateViewState will be false. + mFirstLayoutForNonStandardLoad = true; ViewState viewState = new ViewState(); viewState.mMinScale = mViewportMinimumScale / 100.0f; viewState.mMaxScale = mViewportMaximumScale / 100.0f; @@ -2471,9 +2479,10 @@ public final class WebViewCore { // called by JNI private void restoreScale(float scale, float textWrapScale) { if (mBrowserFrame.firstLayoutDone() == false) { - mRestoredScale = scale; + final float defaultScale = mWebView.getDefaultZoomScale(); + mRestoredScale = (scale <= 0.0) ? defaultScale : scale; if (mSettings.getUseWideViewPort()) { - mRestoredTextWrapScale = textWrapScale; + mRestoredTextWrapScale = (textWrapScale <= 0.0) ? defaultScale : textWrapScale; } } } diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 883656bd6fae..7d43e94ff695 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -1024,6 +1024,11 @@ class ZoomManager { } else { mInZoomOverview = !scaleHasDiff; } + if (drawData.mFirstLayoutForNonStandardLoad && settings.getLoadWithOverviewMode()) { + // Set mInitialZoomOverview in case this is the first picture for non standard load, + // so next new picture could be forced into overview mode if it's true. + mInitialZoomOverview = mInZoomOverview; + } } /** @@ -1107,7 +1112,8 @@ class ZoomManager { } reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale); } - mInitialZoomOverview = !exceedsMinScaleIncrement(scale, overviewScale); + mInitialZoomOverview = settings.getLoadWithOverviewMode() && + !exceedsMinScaleIncrement(scale, overviewScale); setZoomScale(scale, reflowText); // update the zoom buttons as the scale can be changed diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 15702244596f..7c0470e17434 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -24,6 +24,7 @@ import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; +import android.util.Pair; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -33,7 +34,6 @@ import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,7 +67,7 @@ import static java.lang.Math.min; * * <h4>Default Cell Assignment</h4> * - * If no child specifies the row and column indices of the cell it + * If a child does not specify the row and column indices of the cell it * wishes to occupy, GridLayout assigns cell locations automatically using its: * {@link GridLayout#setOrientation(int) orientation}, * {@link GridLayout#setRowCount(int) rowCount} and @@ -94,8 +94,8 @@ import static java.lang.Math.min; * * Like {@link LinearLayout}, a child's ability to stretch is controlled * using <em>weights</em>, which are specified using the - * {@link GridLayout.LayoutParams#rowWeight rowWeight} and - * {@link GridLayout.LayoutParams#columnWeight columnWeight} layout parameters. + * {@link GridLayout.LayoutParams#widthSpec widthSpec} and + * {@link GridLayout.LayoutParams#heightSpec heightSpec} layout parameters. * <p> * <p> * See {@link GridLayout.LayoutParams} for a full description of the @@ -171,9 +171,7 @@ public class GridLayout extends ViewGroup { private static final String TAG = GridLayout.class.getName(); private static final boolean DEBUG = false; private static final double GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2; - private static final int MIN = 0; private static final int PRF = 1; - private static final int MAX = 2; // Defaults @@ -184,6 +182,7 @@ public class GridLayout extends ViewGroup { private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS; // todo remove this private static final int DEFAULT_CONTAINER_MARGIN = 20; + private static final int MAX_SIZE = 100000; // TypedArray indices @@ -205,36 +204,16 @@ public class GridLayout extends ViewGroup { private int mAlignmentMode = DEFAULT_ALIGNMENT_MODE; private int mDefaultGravity = Gravity.NO_GRAVITY; - /* package */ boolean accommodateBothMinAndMax = false; - // Constructors /** * {@inheritDoc} */ - public GridLayout(Context context) { - this(context, null, 0); - } - - /** - * {@inheritDoc} - */ public GridLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (DEBUG) { setWillNotDraw(false); } - processAttributes(context, attrs); - } - - /** - * {@inheritDoc} - */ - public GridLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - private void processAttributes(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout); try { setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT)); @@ -249,6 +228,20 @@ public class GridLayout extends ViewGroup { } } + /** + * {@inheritDoc} + */ + public GridLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * {@inheritDoc} + */ + public GridLayout(Context context) { + this(context, null); + } + // Implementation /** @@ -527,11 +520,10 @@ public class GridLayout extends ViewGroup { return result; } - private static int sum(float[] a) { - int result = 0; - for (int i = 0, length = a.length; i < length; i++) { - result += a[i]; - } + private static <T> T[] append(T[] a, T[] b) { + T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length); + System.arraycopy(a, 0, result, 0, a.length); + System.arraycopy(b, 0, result, a.length, b.length); return result; } @@ -603,13 +595,13 @@ public class GridLayout extends ViewGroup { if (isGone(c)) continue; LayoutParams lp = getLayoutParams1(c); - Group colGroup = lp.columnGroup; - Interval cols = colGroup.span; - int colSpan = cols.size(); + final Group colGroup = lp.columnGroup; + final Interval cols = colGroup.span; + final int colSpan = cols.size(); - Group rowGroup = lp.rowGroup; - Interval rows = rowGroup.span; - int rowSpan = rows.size(); + final Group rowGroup = lp.rowGroup; + final Interval rows = rowGroup.span; + final int rowSpan = rows.size(); if (horizontal) { row = valueIfDefined2(rows.min, row); @@ -700,8 +692,8 @@ public class GridLayout extends ViewGroup { } private void drawRectangle(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) { - // x2 = x2 - 1; - // y2 = y2 - 1; + x2 = x2 - 1; + y2 = y2 - 1; graphics.drawLine(x1, y1, x1, y2, paint); graphics.drawLine(x1, y1, x2, y1, paint); graphics.drawLine(x1, y2, x2, y2, paint); @@ -734,9 +726,9 @@ public class GridLayout extends ViewGroup { drawLine(canvas, 0, y, width - 1, y, paint); } } + // Draw bounds paint.setColor(Color.BLUE); - for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); drawRectangle(canvas, @@ -748,7 +740,6 @@ public class GridLayout extends ViewGroup { // Draw margins paint.setColor(Color.YELLOW); - for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); drawRectangle(canvas, @@ -819,11 +810,11 @@ public class GridLayout extends ViewGroup { protected void onMeasure(int widthSpec, int heightSpec) { measureChildrenWithMargins(widthSpec, heightSpec); - int computedWidth = getPaddingLeft() + mHorizontalAxis.getMin() + getPaddingRight(); - int computedHeight = getPaddingTop() + mVerticalAxis.getMin() + getPaddingBottom(); + int width = getPaddingLeft() + mHorizontalAxis.getMeasure(widthSpec) + getPaddingRight(); + int height = getPaddingTop() + mVerticalAxis.getMeasure(heightSpec) + getPaddingBottom(); - int measuredWidth = Math.max(computedWidth, getSuggestedMinimumWidth()); - int measuredHeight = Math.max(computedHeight, getSuggestedMinimumHeight()); + int measuredWidth = Math.max(width, getSuggestedMinimumWidth()); + int measuredHeight = Math.max(height, getSuggestedMinimumHeight()); setMeasuredDimension( resolveSizeAndState(measuredWidth, widthSpec, 0), @@ -834,12 +825,12 @@ public class GridLayout extends ViewGroup { return (alignment == UNDEFINED) ? 0 : alignment; } - private int getMeasurement(View c, boolean horizontal, int measurementType) { + private int getMeasurement(View c, boolean horizontal) { return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight(); } - private int getMeasurementIncludingMargin(View c, boolean horizontal, int measurementType) { - int result = getMeasurement(c, horizontal, measurementType); + private int getMeasurementIncludingMargin(View c, boolean horizontal) { + int result = getMeasurement(c, horizontal); if (mAlignmentMode == ALIGN_MARGINS) { return result + getTotalMargin(c, horizontal); } @@ -889,17 +880,17 @@ public class GridLayout extends ViewGroup { Interval colSpan = columnGroup.span; Interval rowSpan = rowGroup.span; - int x1 = mHorizontalAxis.getLocationIncludingMargin(c, true, colSpan.min); - int y1 = mVerticalAxis.getLocationIncludingMargin(c, true, rowSpan.min); + int x1 = mHorizontalAxis.getLocationIncludingMargin(true, colSpan.min); + int y1 = mVerticalAxis.getLocationIncludingMargin(true, rowSpan.min); - int x2 = mHorizontalAxis.getLocationIncludingMargin(c, false, colSpan.max); - int y2 = mVerticalAxis.getLocationIncludingMargin(c, false, rowSpan.max); + int x2 = mHorizontalAxis.getLocationIncludingMargin(false, colSpan.max); + int y2 = mVerticalAxis.getLocationIncludingMargin(false, rowSpan.max); int cellWidth = x2 - x1; int cellHeight = y2 - y1; - int pWidth = getMeasurement(c, true, PRF); - int pHeight = getMeasurement(c, false, PRF); + int pWidth = getMeasurement(c, true); + int pHeight = getMeasurement(c, false); Alignment hAlign = columnGroup.alignment; Alignment vAlign = rowGroup.alignment; @@ -910,9 +901,8 @@ public class GridLayout extends ViewGroup { Bounds rowBounds = mVerticalAxis.getGroupBounds().getValue(i); // Gravity offsets: the location of the alignment group relative to its cell group. - int type = PRF; - int c2ax = protect(hAlign.getAlignmentValue(null, cellWidth - colBounds.size(), type)); - int c2ay = protect(vAlign.getAlignmentValue(null, cellHeight - rowBounds.size(), type)); + int c2ax = protect(hAlign.getAlignmentValue(null, cellWidth - colBounds.size(true))); + int c2ay = protect(vAlign.getAlignmentValue(null, cellHeight - rowBounds.size(true))); if (mAlignmentMode == ALIGN_MARGINS) { int leftMargin = getMargin(c, true, true); @@ -925,8 +915,8 @@ public class GridLayout extends ViewGroup { int mHeight = topMargin + pHeight + bottomMargin; // Alignment offsets: the location of the view relative to its alignment group. - int a2vx = colBounds.getOffset(c, hAlign, type, mWidth); - int a2vy = rowBounds.getOffset(c, vAlign, type, mHeight); + int a2vx = colBounds.getOffset(c, hAlign, mWidth); + int a2vy = rowBounds.getOffset(c, vAlign, mHeight); dx = c2ax + a2vx + leftMargin; dy = c2ay + a2vy + topMargin; @@ -935,13 +925,14 @@ public class GridLayout extends ViewGroup { cellHeight -= topMargin + bottomMargin; } else { // Alignment offsets: the location of the view relative to its alignment group. - int a2vx = colBounds.getOffset(c, hAlign, type, pWidth); - int a2vy = rowBounds.getOffset(c, vAlign, type, pHeight); + int a2vx = colBounds.getOffset(c, hAlign, pWidth); + int a2vy = rowBounds.getOffset(c, vAlign, pHeight); dx = c2ax + a2vx; dy = c2ay + a2vy; } + int type = PRF; int width = hAlign.getSizeInCell(c, pWidth, cellWidth, type); int height = vAlign.getSizeInCell(c, pHeight, cellHeight, type); @@ -962,7 +953,7 @@ public class GridLayout extends ViewGroup { private class Axis { private static final int MIN_VALUE = -1000000; - private static final int UNVISITED = 0; + private static final int NEW = 0; private static final int PENDING = 1; private static final int COMPLETE = 2; @@ -975,8 +966,11 @@ public class GridLayout extends ViewGroup { PackedMap<Group, Bounds> groupBounds; public boolean groupBoundsValid = false; - PackedMap<Interval, MutableInt> spanSizes; - public boolean spanSizesValid = false; + PackedMap<Interval, MutableInt> forwardLinks; + public boolean forwardLinksValid = false; + + PackedMap<Interval, MutableInt> backwardLinks; + public boolean backwardLinksValid = false; public int[] leadingMargins; public boolean leadingMarginsValid = false; @@ -987,14 +981,14 @@ public class GridLayout extends ViewGroup { public Arc[] arcs; public boolean arcsValid = false; - public int[] minima; - public boolean minimaValid = false; - - public float[] weights; public int[] locations; + public boolean locationsValid = false; private boolean mOrderPreserved = DEFAULT_ORDER_PRESERVED; + private MutableInt parentMin = new MutableInt(0); + private MutableInt parentMax = new MutableInt(-MAX_SIZE); + private Axis(boolean horizontal) { this.horizontal = horizontal; } @@ -1036,22 +1030,19 @@ public class GridLayout extends ViewGroup { } private PackedMap<Group, Bounds> createGroupBounds() { - int N = getChildCount(); - Group[] groups = new Group[N]; - Arrays.fill(groups, Group.GONE); - Bounds[] bounds = new Bounds[N]; - Arrays.fill(bounds, Bounds.GONE); - for (int i = 0; i < N; i++) { + Assoc<Group, Bounds> assoc = Assoc.of(Group.class, Bounds.class); + for (int i = 0, N = getChildCount(); i < N; i++) { View c = getChildAt(i); - if (isGone(c)) continue; - LayoutParams lp = getLayoutParams(c); - Group group = horizontal ? lp.columnGroup : lp.rowGroup; - - groups[i] = group; - bounds[i] = group.alignment.getBounds(); + if (isGone(c)) { + assoc.put(Group.GONE, Bounds.GONE); + } else { + LayoutParams lp = getLayoutParams(c); + Group group = horizontal ? lp.columnGroup : lp.rowGroup; + Bounds bounds = group.alignment.getBounds(); + assoc.put(group, bounds); + } } - - return new PackedMap<Group, Bounds>(groups, bounds); + return assoc.pack(); } private void computeGroupBounds() { @@ -1064,13 +1055,7 @@ public class GridLayout extends ViewGroup { if (isGone(c)) continue; LayoutParams lp = getLayoutParams(c); Group g = horizontal ? lp.columnGroup : lp.rowGroup; - - Bounds bounds = groupBounds.getValue(i); - - int size = getMeasurementIncludingMargin(c, horizontal, PRF); - // todo test this works correctly when the returned value is UNDEFINED - int before = g.alignment.getAlignmentValue(c, size, PRF); - bounds.include(before, size - before); + groupBounds.getValue(i).include(c, g, GridLayout.this, this, lp); } } @@ -1086,80 +1071,91 @@ public class GridLayout extends ViewGroup { } // Add values computed by alignment - taking the max of all alignments in each span - private PackedMap<Interval, MutableInt> createSpanSizes() { - PackedMap<Group, Bounds> groupBounds = getGroupBounds(); - int N = groupBounds.keys.length; - Interval[] spans = new Interval[N]; - MutableInt[] values = new MutableInt[N]; - for (int i = 0; i < N; i++) { - Interval key = groupBounds.keys[i].span; - - spans[i] = key; - values[i] = new MutableInt(); + private PackedMap<Interval, MutableInt> createLinks(boolean min) { + Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class); + Group[] keys = getGroupBounds().keys; + for (int i = 0, N = keys.length; i < N; i++) { + Interval span = min ? keys[i].span : keys[i].span.inverse(); + result.put(span, new MutableInt()); } - return new PackedMap<Interval, MutableInt>(spans, values); + return result.pack(); } - private void computeSpanSizes() { - MutableInt[] spans = spanSizes.values; + private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) { + MutableInt[] spans = links.values; for (int i = 0; i < spans.length; i++) { spans[i].reset(); } - Bounds[] bounds = getGroupBounds().values; // use getter to trigger a re-evaluation + // use getter to trigger a re-evaluation + Bounds[] bounds = getGroupBounds().values; for (int i = 0; i < bounds.length; i++) { - int value = bounds[i].size(); - - MutableInt valueHolder = spanSizes.getValue(i); + int size = bounds[i].size(min); + int value = min ? size : -size; + MutableInt valueHolder = links.getValue(i); valueHolder.value = max(valueHolder.value, value); } } - private PackedMap<Interval, MutableInt> getSpanSizes() { - if (spanSizes == null) { - spanSizes = createSpanSizes(); + private PackedMap<Interval, MutableInt> getForwardLinks() { + if (forwardLinks == null) { + forwardLinks = createLinks(true); } - if (!spanSizesValid) { - computeSpanSizes(); - spanSizesValid = true; + if (!forwardLinksValid) { + computeLinks(forwardLinks, true); + forwardLinksValid = true; } - return spanSizes; + return forwardLinks; } - private void include(List<Arc> arcs, Interval key, MutableInt size) { - // this bit below should really be computed outside here - - // its just to stop default (col>0) constraints obliterating valid entries - for (Arc arc : arcs) { - Interval span = arc.span; - if (span.equals(key)) { - return; - } + private PackedMap<Interval, MutableInt> getBackwardLinks() { + if (backwardLinks == null) { + backwardLinks = createLinks(false); } - arcs.add(new Arc(key, size)); + if (!backwardLinksValid) { + computeLinks(backwardLinks, false); + backwardLinksValid = true; + } + return backwardLinks; } - private void include2(List<Arc> arcs, Interval span, MutableInt min, MutableInt max, - boolean both) { - include(arcs, span, min); - if (both) { - // todo -// include(arcs, span.inverse(), max.neg()); + private void include(List<Arc> arcs, Interval key, MutableInt size, + boolean ignoreIfAlreadyPresent) { + /* + Remove self referential links. + These appear: + . as parental constraints when GridLayout has no children + . when components have been marked as GONE + */ + if (key.size() == 0) { + return; } + // this bit below should really be computed outside here - + // its just to stop default (row/col > 0) constraints obliterating valid entries + if (ignoreIfAlreadyPresent) { + for (Arc arc : arcs) { + Interval span = arc.span; + if (span.equals(key)) { + return; + } + } + } + arcs.add(new Arc(key, size)); } - private void include2(List<Arc> arcs, Interval span, int min, int max, boolean both) { - include2(arcs, span, new MutableInt(min), new MutableInt(max), both); + private void include(List<Arc> arcs, Interval key, MutableInt size) { + include(arcs, key, size, true); } // Group arcs by their first vertex, returning an array of arrays. // This is linear in the number of arcs. private Arc[][] groupArcsByFirstVertex(Arc[] arcs) { - int N = getCount() + 1;// the number of vertices + int N = getCount() + 1; // the number of vertices Arc[][] result = new Arc[N][]; int[] sizes = new int[N]; for (Arc arc : arcs) { sizes[arc.span.min]++; - } + } for (int i = 0; i < sizes.length; i++) { result[i] = new Arc[sizes[i]]; } @@ -1173,38 +1169,46 @@ public class GridLayout extends ViewGroup { return result; } - private Arc[] topologicalSort(final Arc[] arcs, int start) { - // todo ensure the <start> vertex is added in edge cases - final List<Arc> result = new ArrayList<Arc>(); - new Object() { - Arc[][] arcsByFirstVertex = groupArcsByFirstVertex(arcs); + private Arc[] topologicalSort(final Arc[] arcs) { + return new Object() { + Arc[] result = new Arc[arcs.length]; + int cursor = result.length - 1; + Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs); int[] visited = new int[getCount() + 1]; - boolean completesCycle(int loc) { - int state = visited[loc]; - if (state == UNVISITED) { - visited[loc] = PENDING; - for (Arc arc : arcsByFirstVertex[loc]) { - Interval span = arc.span; - // the recursive call - if (completesCycle(span.max)) { - // which arcs get set here is dependent on the order - // in which we explore nodes - arc.completesCycle = true; + void walk(int loc) { + switch (visited[loc]) { + case NEW: { + visited[loc] = PENDING; + for (Arc arc : arcsByVertex[loc]) { + walk(arc.span.max); + result[cursor--] = arc; } - result.add(arc); + visited[loc] = COMPLETE; + break; + } + case PENDING: { + assert false; + break; + } + case COMPLETE: { + break; } - visited[loc] = COMPLETE; - } else if (state == PENDING) { - return true; - } else if (state == COMPLETE) { } - return false; } - }.completesCycle(start); - Collections.reverse(result); - assert arcs.length == result.size(); - return result.toArray(new Arc[result.size()]); + + Arc[] sort() { + for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) { + walk(loc); + } + assert cursor == -1; + return result; + } + }.sort(); + } + + private Arc[] topologicalSort(List<Arc> arcs) { + return topologicalSort(arcs.toArray(new Arc[arcs.size()])); } private boolean[] findUsed(Collection<Arc> arcs) { @@ -1254,43 +1258,64 @@ public class GridLayout extends ViewGroup { return result; } + private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) { + for (int i = 0; i < links.keys.length; i++) { + Interval key = links.keys[i]; + include(result, key, links.values[i], false); + } + } + private Arc[] createArcs() { - List<Arc> result = new ArrayList<Arc>(); + List<Arc> mins = new ArrayList<Arc>(); + List<Arc> maxs = new ArrayList<Arc>(); - // Add all the preferred elements that were not defined by the user. - PackedMap<Interval, MutableInt> spanSizes = getSpanSizes(); - for (int i = 0; i < spanSizes.keys.length; i++) { - Interval key = spanSizes.keys[i]; - if (key == Interval.GONE) continue; - MutableInt value = spanSizes.values[i]; - // todo remove value duplicate - include2(result, key, value, value, accommodateBothMinAndMax); - } + // Add the minimum values from the components. + addComponentSizes(mins, getForwardLinks()); + // Add the maximum values from the components. + addComponentSizes(maxs, getBackwardLinks()); // Find redundant rows/cols and glue them together with 0-length arcs to link the tree - boolean[] used = findUsed(result); + boolean[] used = findUsed(mins); for (int i = 0; i < getCount(); i++) { if (!used[i]) { Interval span = new Interval(i, i + 1); - include(result, span, new MutableInt(0)); - include(result, span.inverse(), new MutableInt(0)); + include(mins, span, new MutableInt(0)); + include(maxs, span.inverse(), new MutableInt(0)); } } + // Add ordering constraints to prevent row/col sizes from going negative if (mOrderPreserved) { - // Add preferred gaps + // Add a constraint for every row/col for (int i = 0; i < getCount(); i++) { if (used[i]) { - include2(result, new Interval(i, i + 1), 0, 0, false); + include(mins, new Interval(i, i + 1), new MutableInt(0)); } } } else { + // Add a constraint for each row/col that separates opposing component edges for (Interval gap : getSpacers()) { - include2(result, gap, 0, 0, false); + include(mins, gap, new MutableInt(0)); } } - Arc[] arcs = result.toArray(new Arc[result.size()]); - return topologicalSort(arcs, 0); + + // Add the container constraints. Use the version of include that allows + // duplicate entries in case a child spans the entire grid. + int N = getCount(); + include(mins, new Interval(0, N), parentMin, false); + include(maxs, new Interval(N, 0), parentMax, false); + + // Sort + Arc[] sMins = topologicalSort(mins); + Arc[] sMaxs = topologicalSort(maxs); + + return append(sMins, sMaxs); + } + + private void computeArcs() { + // getting the links validates the values that are shared by the arc list + getForwardLinks(); + getBackwardLinks(); } public Arc[] getArcs() { @@ -1298,13 +1323,16 @@ public class GridLayout extends ViewGroup { arcs = createArcs(); } if (!arcsValid) { - getSpanSizes(); + computeArcs(); arcsValid = true; } return arcs; } private boolean relax(int[] locations, Arc entry) { + if (!entry.valid) { + return false; + } Interval span = entry.span; int u = span.min; int v = span.max; @@ -1351,7 +1379,8 @@ public class GridLayout extends ViewGroup { typical layout problems complete after the first iteration and the algorithm completes in O(N) steps with very low constants. */ - private int[] solve(Arc[] arcs, int[] locations) { + private void solve(Arc[] arcs, int[] locations) { + String axis = horizontal ? "horizontal" : "vertical"; int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1. boolean changed = false; @@ -1359,20 +1388,44 @@ public class GridLayout extends ViewGroup { for (int i = 0; i < N; i++) { changed = false; for (int j = 0, length = arcs.length; j < length; j++) { - changed = changed | relax(locations, arcs[j]); + changed |= relax(locations, arcs[j]); } if (!changed) { if (DEBUG) { - Log.d(TAG, "Iteration " + - " completed after " + (1 + i) + " steps out of " + N); + Log.d(TAG, axis + " iteration completed in " + (1 + i) + " steps of " + N); } - break; + return; } } - if (changed) { - Log.d(TAG, "*** Algorithm failed to terminate ***"); + + Log.d(TAG, "The " + axis + " constraints contained a contradiction. Resolving... "); + Log.d(TAG, Arrays.toString(arcs)); + + boolean[] culprits = new boolean[arcs.length]; + for (int i = 0; i < N; i++) { + for (int j = 0, length = arcs.length; j < length; j++) { + culprits[j] |= relax(locations, arcs[j]); + } } - return locations; + for (int i = 0; i < culprits.length; i++) { + if (culprits[i]) { + Arc arc = arcs[i]; + // Only remove max values, min values alone cannot be inconsistent + if (arc.span.min < arc.span.max) { + continue; + } + Log.d(TAG, "Removing: " + arc); + arc.valid = false; + break; + } + } + solve1(arcs, locations); + } + + private void solve1(Arc[] arcs, int[] a) { + Arrays.fill(a, MIN_VALUE); + a[0] = 0; + solve(arcs, a); } private void computeMargins(boolean leading) { @@ -1418,11 +1471,11 @@ public class GridLayout extends ViewGroup { for (int i = 0, N = getCount(); i < N; i++) { int margins = leadingMargins[i] + trailingMargins[i + 1]; delta += margins; - minima[i + 1] += delta; + locations[i + 1] += delta; } } - private int getLocationIncludingMargin(View view, boolean leading, int index) { + private int getLocationIncludingMargin(boolean leading, int index) { int location = locations[index]; int margin; if (mAlignmentMode != ALIGN_MARGINS) { @@ -1433,53 +1486,22 @@ public class GridLayout extends ViewGroup { return leading ? (location + margin) : (location - margin); } - private void computeMinima(int[] a) { - Arrays.fill(a, MIN_VALUE); - a[0] = 0; - solve(getArcs(), a); + private void computeLocations(int[] a) { + solve1(getArcs(), a); if (mAlignmentMode != ALIGN_MARGINS) { addMargins(); } } - private int[] getMinima() { - if (minima == null) { - int N = getCount() + 1; - minima = new int[N]; - } - if (!minimaValid) { - computeMinima(minima); - minimaValid = true; - } - return minima; - } - - private void computeWeights() { - for (int i = 0, N = getChildCount(); i < N; i++) { - View c = getChildAt(i); - if (isGone(c)) continue; - LayoutParams lp = getLayoutParams(c); - Group g = horizontal ? lp.columnGroup : lp.rowGroup; - Interval span = g.span; - int penultimateIndex = span.max - 1; - weights[penultimateIndex] += horizontal ? lp.columnWeight : lp.rowWeight; - } - } - - private float[] getWeights() { - if (weights == null) { - int N = getCount(); - weights = new float[N]; - } - computeWeights(); - return weights; - } - private int[] getLocations() { if (locations == null) { int N = getCount() + 1; locations = new int[N]; } + if (!locationsValid) { + computeLocations(locations); + locationsValid = true; + } return locations; } @@ -1489,48 +1511,53 @@ public class GridLayout extends ViewGroup { return max2(locations, 0) - locations[0]; } - private int getMin() { - return size(getMinima()); + private void setParentConstraints(int min, int max) { + parentMin.value = min; + parentMax.value = -max; + locationsValid = false; } - private void layout(int targetSize) { - int[] mins = getMinima(); - - int totalDelta = max(0, targetSize - size(mins)); // confine to expansion - - float[] weights = getWeights(); - float totalWeight = sum(weights); + private int getMeasure(int min, int max) { + setParentConstraints(min, max); + return size(getLocations()); + } - if (totalWeight == 0f && weights.length > 0) { - weights[weights.length - 1] = 1; - totalWeight = 1; + private int getMeasure(int measureSpec) { + int mode = MeasureSpec.getMode(measureSpec); + int size = MeasureSpec.getSize(measureSpec); + switch (mode) { + case MeasureSpec.UNSPECIFIED: { + return getMeasure(0, MAX_SIZE); + } + case MeasureSpec.EXACTLY: { + return getMeasure(size, size); + } + case MeasureSpec.AT_MOST: { + return getMeasure(0, size); + } + default: { + assert false; + return 0; + } } + } - int[] locations = getLocations(); - int cumulativeDelta = 0; - - // note |weights| = |locations| - 1 - for (int i = 0; i < weights.length; i++) { - float weight = weights[i]; - int delta = (int) (totalDelta * weight / totalWeight); - cumulativeDelta += delta; - locations[i + 1] = mins[i + 1] + cumulativeDelta; - - totalDelta -= delta; - totalWeight -= weight; - } + private void layout(int size) { + setParentConstraints(size, size); + getLocations(); } private void invalidateStructure() { countValid = false; groupBounds = null; - spanSizes = null; + forwardLinks = null; + backwardLinks = null; + leadingMargins = null; trailingMargins = null; arcs = null; - minima = null; - weights = null; + locations = null; invalidateValues(); @@ -1538,11 +1565,14 @@ public class GridLayout extends ViewGroup { private void invalidateValues() { groupBoundsValid = false; - spanSizesValid = false; - arcsValid = false; + forwardLinksValid = false; + backwardLinksValid = false; + leadingMarginsValid = false; trailingMarginsValid = false; - minimaValid = false; + arcsValid = false; + + locationsValid = false; } } @@ -1592,16 +1622,16 @@ public class GridLayout extends ViewGroup { * <li>{@link #rowGroup}{@code .alignment} = {@link #BASELINE} </li> * <li>{@link #columnGroup}{@code .span} = {@code [0, 1]} </li> * <li>{@link #columnGroup}{@code .alignment} = {@link #LEFT} </li> - * <li>{@link #rowWeight} = {@code 0f} </li> - * <li>{@link #columnWeight} = {@code 0f} </li> + * <li>{@link #widthSpec} = {@link #FIXED} </li> + * <li>{@link #heightSpec} = {@link #FIXED} </li> * </ul> * * @attr ref android.R.styleable#GridLayout_Layout_layout_row * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan - * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight + * @attr ref android.R.styleable#GridLayout_Layout_layout_heightSpec * @attr ref android.R.styleable#GridLayout_Layout_layout_column * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan - * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight + * @attr ref android.R.styleable#GridLayout_Layout_layout_widthSpec * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity */ public static class LayoutParams extends MarginLayoutParams { @@ -1621,14 +1651,15 @@ public class GridLayout extends ViewGroup { new Group(DEFAULT_SPAN, DEFAULT_COLUMN_ALIGNMENT); private static final Group DEFAULT_ROW_GROUP = new Group(DEFAULT_SPAN, DEFAULT_ROW_ALIGNMENT); - private static final int DEFAULT_WEIGHT_0 = 0; - private static final int DEFAULT_WEIGHT_1 = 1; + private static final Spec DEFAULT_SPEC = FIXED; + private static final int DEFAULT_SPEC_INDEX = 0; // Misc private static final Rect CONTAINER_BOUNDS = new Rect(0, 0, 2, 2); private static final Alignment[] COLUMN_ALIGNMENTS = { LEFT, CENTER, RIGHT }; private static final Alignment[] ROW_ALIGNMENTS = { TOP, CENTER, BOTTOM }; + private static final Spec[] SPECS = { FIXED, CAN_SHRINK, CAN_STRETCH }; // TypedArray indices @@ -1641,10 +1672,10 @@ public class GridLayout extends ViewGroup { private static final int COLUMN = styleable.GridLayout_Layout_layout_column; private static final int COLUMN_SPAN = styleable.GridLayout_Layout_layout_columnSpan; - private static final int COLUMN_WEIGHT = styleable.GridLayout_Layout_layout_columnWeight; + private static final int WIDTH_SPEC = styleable.GridLayout_Layout_layout_widthSpec; private static final int ROW = styleable.GridLayout_Layout_layout_row; private static final int ROW_SPAN = styleable.GridLayout_Layout_layout_rowSpan; - private static final int ROW_WEIGHT = styleable.GridLayout_Layout_layout_rowWeight; + private static final int HEIGHT_SPEC = styleable.GridLayout_Layout_layout_heightSpec; private static final int GRAVITY = styleable.GridLayout_Layout_layout_gravity; // Instance variables @@ -1660,28 +1691,29 @@ public class GridLayout extends ViewGroup { */ public Group columnGroup; /** - * The proportional space that should be taken by the associated row group + * The proportional space that should be taken by the associated column group * during excess space distribution. */ - public float rowWeight; + public Spec widthSpec; /** - * The proportional space that should be taken by the associated column group + * The proportional space that should be taken by the associated row group * during excess space distribution. */ - public float columnWeight; + public Spec heightSpec; // Constructors private LayoutParams( int width, int height, int left, int top, int right, int bottom, - Group rowGroup, Group columnGroup, float rowWeight, float columnWeight) { + Group rowGroup, Group columnGroup, + Spec widthSpec, Spec heightSpec) { super(width, height); setMargins(left, top, right, bottom); this.rowGroup = rowGroup; this.columnGroup = columnGroup; - this.rowWeight = rowWeight; - this.columnWeight = columnWeight; + this.heightSpec = heightSpec; + this.widthSpec = widthSpec; } /** @@ -1695,7 +1727,7 @@ public class GridLayout extends ViewGroup { public LayoutParams(Group rowGroup, Group columnGroup) { this(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, - rowGroup, columnGroup, DEFAULT_WEIGHT_0, DEFAULT_WEIGHT_0); + rowGroup, columnGroup, DEFAULT_SPEC, DEFAULT_SPEC); } /** @@ -1728,8 +1760,8 @@ public class GridLayout extends ViewGroup { super(that); this.columnGroup = that.columnGroup; this.rowGroup = that.rowGroup; - this.columnWeight = that.columnWeight; - this.rowWeight = that.rowWeight; + this.widthSpec = that.widthSpec; + this.heightSpec = that.heightSpec; } // AttributeSet constructors @@ -1813,11 +1845,6 @@ public class GridLayout extends ViewGroup { !definesVertical(gravity), defaultAlignment); } - private int getDefaultWeight(int size) { - //return (size == MATCH_PARENT) ? DEFAULT_WEIGHT_1 : DEFAULT_WEIGHT_0; - return DEFAULT_WEIGHT_0; - } - private void init(Context context, AttributeSet attrs, int defaultGravity) { TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout_Layout); try { @@ -1827,13 +1854,13 @@ public class GridLayout extends ViewGroup { int columnSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE); Interval hSpan = new Interval(column, column + columnSpan); this.columnGroup = new Group(hSpan, getColumnAlignment(gravity, width)); - this.columnWeight = a.getFloat(COLUMN_WEIGHT, getDefaultWeight(width)); + this.widthSpec = SPECS[a.getInt(WIDTH_SPEC, DEFAULT_SPEC_INDEX)]; int row = a.getInt(ROW, DEFAULT_ROW); int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE); Interval vSpan = new Interval(row, row + rowSpan); this.rowGroup = new Group(vSpan, getRowAlignment(gravity, height)); - this.rowWeight = a.getFloat(ROW_WEIGHT, getDefaultWeight(height)); + this.heightSpec = SPECS[a.getInt(HEIGHT_SPEC, DEFAULT_SPEC_INDEX)]; } finally { a.recycle(); } @@ -1874,7 +1901,7 @@ public class GridLayout extends ViewGroup { private static class Arc { public final Interval span; public final MutableInt value; - public boolean completesCycle; + public boolean valid = true; public Arc(Interval span, MutableInt value) { this.span = span; @@ -1883,7 +1910,7 @@ public class GridLayout extends ViewGroup { @Override public String toString() { - return span + " " + (completesCycle ? "+>" : "->") + " " + value; + return span + " " + (!valid ? "+>" : "->") + " " + value; } } @@ -1903,6 +1930,41 @@ public class GridLayout extends ViewGroup { private void reset() { value = Integer.MIN_VALUE; } + + @Override + public String toString() { + return Integer.toString(value); + } + } + + private static class Assoc<K, V> extends ArrayList<Pair<K, V>> { + private final Class<K> keyType; + private final Class<V> valueType; + + private Assoc(Class<K> keyType, Class<V> valueType) { + this.keyType = keyType; + this.valueType = valueType; + } + + private static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) { + return new Assoc<K, V>(keyType, valueType); + } + + public void put(K key, V value) { + add(Pair.create(key, value)); + } + + @SuppressWarnings(value = "unchecked") + public PackedMap<K, V> pack() { + int N = size(); + K[] keys = (K[]) Array.newInstance(keyType, N); + V[] values = (V[]) Array.newInstance(valueType, N); + for (int i = 0; i < N; i++) { + keys[i] = get(i).first; + values[i] = get(i).second; + } + return new PackedMap<K, V>(keys, values); + } } /* @@ -1989,6 +2051,7 @@ public class GridLayout extends ViewGroup { public int before; public int after; + public boolean canStretch; private Bounds() { reset(); @@ -1997,6 +2060,7 @@ public class GridLayout extends ViewGroup { protected void reset() { before = Integer.MIN_VALUE; after = Integer.MIN_VALUE; + canStretch = false; } protected void include(int before, int after) { @@ -2004,12 +2068,26 @@ public class GridLayout extends ViewGroup { this.after = max(this.after, after); } - protected int size() { + protected int size(boolean min) { + if (!min && canStretch) { + return MAX_SIZE; + } return before + after; } - protected int getOffset(View c, Alignment alignment, int type, int size) { - return before - alignment.getAlignmentValue(c, size, type); + protected int getOffset(View c, Alignment alignment, int size) { + return before - alignment.getAlignmentValue(c, size); + } + + protected void include(View c, Group g, GridLayout gridLayout, Axis axis, LayoutParams lp) { + Spec spec = axis.horizontal ? lp.widthSpec : lp.heightSpec; + if (spec == CAN_STRETCH) { + canStretch = true; + } + int size = gridLayout.getMeasurementIncludingMargin(c, axis.horizontal); + // todo test this works correctly when the returned value is UNDEFINED + int before = g.alignment.getAlignmentValue(c, size); + include(before, size - before); } @Override @@ -2032,7 +2110,7 @@ public class GridLayout extends ViewGroup { * Intervals are often written as {@code [min, max]} and represent the set of values * {@code x} such that {@code min <= x < max}. */ - /* package */ static class Interval { + static class Interval { private static final Interval GONE = new Interval(UNDEFINED, UNDEFINED); /** @@ -2129,7 +2207,7 @@ public class GridLayout extends ViewGroup { * See {@link GridLayout} for a description of the conventions used by GridLayout * for grid indices. */ - /* package */ final Interval span; + final Interval span; /** * Specifies how cells should be aligned in this group. * For row groups, this specifies the vertical alignment. @@ -2147,7 +2225,7 @@ public class GridLayout extends ViewGroup { * @param span the span * @param alignment the alignment */ - /* package */ Group(Interval span, Alignment alignment) { + Group(Interval span, Alignment alignment) { this.span = span; this.alignment = alignment; } @@ -2248,17 +2326,16 @@ public class GridLayout extends ViewGroup { * so that the locations defined by the alignment values * are the same for all of the views in a group. * <p> - */ public static abstract class Alignment { private static final Alignment GONE = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { assert false; return 0; } }; - /*pp*/ Alignment() { + Alignment() { } /** @@ -2269,12 +2346,9 @@ public class GridLayout extends ViewGroup { * * @param view the view to which this alignment should be applied * @param viewSize the measured size of the view - * @param measurementType This parameter is currently unused as GridLayout only supports - * one type of measurement: {@link View#measure(int, int)}. - * * @return the alignment value */ - /*pp*/ abstract int getAlignmentValue(View view, int viewSize, int measurementType); + abstract int getAlignmentValue(View view, int viewSize); /** * Returns the size of the view specified by this alignment. @@ -2291,24 +2365,24 @@ public class GridLayout extends ViewGroup { * * @return the aligned size */ - /*pp*/ int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) { + int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) { return viewSize; } - /*pp*/ Bounds getBounds() { + Bounds getBounds() { return new Bounds(); } } private static final Alignment LEADING = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { return 0; } }; private static final Alignment TRAILING = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { return viewSize; } }; @@ -2343,7 +2417,7 @@ public class GridLayout extends ViewGroup { * LayoutParams#columnGroup columnGroups}. */ public static final Alignment CENTER = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { return viewSize >> 1; } }; @@ -2356,7 +2430,7 @@ public class GridLayout extends ViewGroup { * @see View#getBaseline() */ public static final Alignment BASELINE = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { if (view == null) { return UNDEFINED; } @@ -2378,7 +2452,7 @@ public class GridLayout extends ViewGroup { @Override protected void reset() { super.reset(); - size = 0; + size = Integer.MIN_VALUE; } @Override @@ -2388,13 +2462,13 @@ public class GridLayout extends ViewGroup { } @Override - protected int size() { - return max(super.size(), size); + protected int size(boolean min) { + return max(super.size(min), size); } @Override - protected int getOffset(View c, Alignment alignment, int type, int size) { - return max(0, super.getOffset(c, alignment, type, size)); + protected int getOffset(View c, Alignment alignment, int size) { + return max(0, super.getOffset(c, alignment, size)); } }; } @@ -2406,7 +2480,7 @@ public class GridLayout extends ViewGroup { * {@link LayoutParams#columnGroup columnGroups}. */ public static final Alignment FILL = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { return UNDEFINED; } @@ -2415,4 +2489,41 @@ public class GridLayout extends ViewGroup { return cellSize; } }; + + /** + * Spec's tell GridLayout how to derive minimum and maximum size values for a + * component. Specifications are made with respect to a child's 'measured size'. + * A child's measured size is, in turn, controlled by its height and width + * layout parameters which either specify a size or, in the case of + * WRAP_CONTENT, defer to the computed size of the component. + */ + public static abstract class Spec { + } + + /** + * Indicates that a view requests precisely the size specified by its layout parameters. + * + * @see Spec + */ + public static final Spec FIXED = new Spec() { + }; + + /** + * Indicates that a view's size should lie between its minimum and the size specified by + * its layout parameters. + * + * @see Spec + */ + public static final Spec CAN_SHRINK = new Spec() { + }; + + /** + * Indicates that a view's size should be greater than or equal to the size specified by + * its layout parameters. + * + * @see Spec + */ + public static final Spec CAN_STRETCH = new Spec() { + }; + } diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 2947ebeb1d4b..aa23ad0f6fa4 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -1201,18 +1201,15 @@ public class NumberPicker extends LinearLayout { private void initializeScrollWheel() { if (mInitialScrollOffset != Integer.MIN_VALUE) { return; - } int[] selectorIndices = getSelectorIndices(); int totalTextHeight = selectorIndices.length * mTextSize; - int totalTextGapHeight = (mBottom - mTop) - totalTextHeight; - int textGapCount = selectorIndices.length - 1; - int selectorTextGapHeight = totalTextGapHeight / textGapCount; - // compensate for integer division loss of the components used to - // calculate the text gap - int integerDivisionLoss = (mTextSize + mBottom - mTop) % textGapCount; - mInitialScrollOffset = mCurrentScrollOffset = mTextSize - integerDivisionLoss / 2; + float totalTextGapHeight = (mBottom - mTop) - totalTextHeight; + float textGapCount = selectorIndices.length - 1; + int selectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f); mSelectorElementHeight = mTextSize + selectorTextGapHeight; + mInitialScrollOffset = mTextSize - 3 * (mSelectorElementHeight % 2); + mCurrentScrollOffset = mInitialScrollOffset; updateInputTextView(); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f6b71f8b9415..c91f1a61306d 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -340,6 +340,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private WordIterator mWordIterator; + // The alignment to pass to Layout, or null if not resolved. + private Layout.Alignment mLayoutAlignment; + + // The default value for mTextAlign. + private TextAlign mTextAlign = TextAlign.INHERIT; + + private static enum TextAlign { + INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END; + } + /* * Kick-start the font cache for the zygote process (to pay the cost of * initializing freetype for our default font only once). @@ -4948,7 +4958,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mMovement != null && mText instanceof Editable && mLayout != null && onCheckIsTextEditor()) { InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) imm.showSoftInput(this, 0); + if (imm != null) { + imm.viewClicked(this); + imm.showSoftInput(this, 0); + } } } } @@ -5529,6 +5542,73 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener physicalWidth, false); } + @Override + protected void resetLayoutDirectionResolution() { + super.resetLayoutDirectionResolution(); + + if (mLayoutAlignment != null && + (mTextAlign == TextAlign.VIEW_START || + mTextAlign == TextAlign.VIEW_END)) { + mLayoutAlignment = null; + } + } + + private Layout.Alignment getLayoutAlignment() { + if (mLayoutAlignment == null) { + Layout.Alignment alignment; + TextAlign textAlign = mTextAlign; + switch (textAlign) { + case INHERIT: + // fall through to gravity temporarily + // intention is to inherit value through view hierarchy. + case GRAVITY: + switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) { + case Gravity.START: + alignment = Layout.Alignment.ALIGN_NORMAL; + break; + case Gravity.END: + alignment = Layout.Alignment.ALIGN_OPPOSITE; + break; + case Gravity.LEFT: + alignment = Layout.Alignment.ALIGN_LEFT; + break; + case Gravity.RIGHT: + alignment = Layout.Alignment.ALIGN_RIGHT; + break; + case Gravity.CENTER_HORIZONTAL: + alignment = Layout.Alignment.ALIGN_CENTER; + break; + default: + alignment = Layout.Alignment.ALIGN_NORMAL; + break; + } + break; + case TEXT_START: + alignment = Layout.Alignment.ALIGN_NORMAL; + break; + case TEXT_END: + alignment = Layout.Alignment.ALIGN_OPPOSITE; + break; + case CENTER: + alignment = Layout.Alignment.ALIGN_CENTER; + break; + case VIEW_START: + alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? + Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT; + break; + case VIEW_END: + alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? + Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT; + break; + default: + alignment = Layout.Alignment.ALIGN_NORMAL; + break; + } + mLayoutAlignment = alignment; + } + return mLayoutAlignment; + } + /** * The width passed in is now the desired layout width, * not the full view width with padding. @@ -5549,25 +5629,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hintWidth = 0; } - final int layoutDirection = getResolvedLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); - - Layout.Alignment alignment; - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - alignment = Layout.Alignment.ALIGN_CENTER; - break; - - case Gravity.RIGHT: - // Note, Layout resolves ALIGN_OPPOSITE to left or - // right based on the paragraph direction. - alignment = Layout.Alignment.ALIGN_OPPOSITE; - break; - - default: - alignment = Layout.Alignment.ALIGN_NORMAL; - } - + Layout.Alignment alignment = getLayoutAlignment(); boolean shouldEllipsize = mEllipsize != null && mInput == null; if (mText instanceof Spannable) { @@ -7398,8 +7460,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if ((isTextEditable() || mTextIsSelectable) && touchIsFinished) { // Show the IME, except when selecting in read-only text. + final InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.viewClicked(this); + } if (!mTextIsSelectable) { - final InputMethodManager imm = InputMethodManager.peekInstance(); handled |= imm != null && imm.showSoftInput(this, 0); } diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 8d5df6f96714..519acf5d3eca 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -16,7 +16,6 @@ package com.android.internal.app; -import com.android.internal.R; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPopupHelper; import com.android.internal.view.menu.SubMenuBuilder; @@ -36,7 +35,6 @@ import android.app.Dialog; import android.app.FragmentTransaction; import android.content.Context; import android.content.res.Configuration; -import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.ActionMode; @@ -580,6 +578,9 @@ public class ActionBarImpl extends ActionBar { mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE); + if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) { + mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); + } } /** @@ -620,6 +621,7 @@ public class ActionBarImpl extends ActionBar { // Clear out the context mode views after the animation finishes mContextView.closeMode(); + mActionMode = null; if (mWasHiddenBeforeMode) { diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index b57046c39b2c..ec64552051d4 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -135,6 +135,16 @@ public class PackageHelper { return null; } + public static String getSdFilesystem(String cid) { + try { + return getMountService().getSecureContainerFilesystemPath(cid); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get container path for " + cid + + " with exception " + e); + } + return null; + } + public static boolean finalizeSdDir(String cid) { try { int rc = getMountService().finalizeSecureContainer(cid); diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index 773be5bc2b21..572a1d761d65 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -23,6 +23,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; +import java.util.List; + /** * A simple container used to carry information in VpnBuilder, VpnDialogs, * and com.android.server.connectivity.Vpn. Internal use only. @@ -33,12 +35,6 @@ public class VpnConfig implements Parcelable { public static final String ACTION_VPN_REVOKED = "android.net.vpn.action.REVOKED"; - public static void enforceCallingPackage(String packageName) { - if (!"com.android.vpndialogs".equals(packageName)) { - throw new SecurityException("Unauthorized Caller"); - } - } - public static Intent getIntentForConfirmation() { Intent intent = new Intent(); intent.setClassName("com.android.vpndialogs", "com.android.vpndialogs.ConfirmDialog"); @@ -58,11 +54,12 @@ public class VpnConfig implements Parcelable { public String packageName; public String sessionName; public String interfaceName; - public String configureActivity; + public PendingIntent configureIntent; public int mtu = -1; public String addresses; public String routes; - public String dnsServers; + public List<String> dnsServers; + public List<String> searchDomains; public long startTime = -1; @Override @@ -75,11 +72,12 @@ public class VpnConfig implements Parcelable { out.writeString(packageName); out.writeString(sessionName); out.writeString(interfaceName); - out.writeString(configureActivity); + out.writeParcelable(configureIntent, flags); out.writeInt(mtu); out.writeString(addresses); out.writeString(routes); - out.writeString(dnsServers); + out.writeStringList(dnsServers); + out.writeStringList(searchDomains); out.writeLong(startTime); } @@ -91,11 +89,12 @@ public class VpnConfig implements Parcelable { config.packageName = in.readString(); config.sessionName = in.readString(); config.interfaceName = in.readString(); - config.configureActivity = in.readString(); + config.configureIntent = in.readParcelable(null); config.mtu = in.readInt(); config.addresses = in.readString(); config.routes = in.readString(); - config.dnsServers = in.readString(); + config.dnsServers = in.createStringArrayList(); + config.searchDomains = in.createStringArrayList(); config.startTime = in.readLong(); return config; } diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 5e9cd23a82cd..f13e77067d5b 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -25,16 +25,13 @@ import android.os.Process; import android.os.SystemProperties; import android.util.Log; import android.util.Slog; - import com.android.internal.logging.AndroidConfig; - +import com.android.server.NetworkManagementSocketTagger; import dalvik.system.VMRuntime; - import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.logging.LogManager; import java.util.TimeZone; - +import java.util.logging.LogManager; import org.apache.harmony.luni.internal.util.TimezoneGetter; /** @@ -129,6 +126,11 @@ public class RuntimeInit { System.setProperty("http.agent", userAgent); /* + * Wire socket tagging to traffic stats. + */ + NetworkManagementSocketTagger.install(); + + /* * If we're running in an emulator launched with "-trace", put the * VM into emulator trace profiling mode so that the user can hit * F9/F10 at any time to capture traces. This has performance diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index 338dcaa7db96..f875cbd95a07 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -36,7 +36,9 @@ oneway interface IInputMethodSession { void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd); - + + void viewClicked(boolean focusChanged); + void updateCursor(in Rect newCursor); void displayCompletions(in CompletionInfo[] completions); diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 322a8545d559..2fec9cd09f5b 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -26,6 +26,7 @@ import android.view.MenuItem; import android.view.SoundEffectConstants; import android.view.View; import android.view.View.MeasureSpec; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.ImageButton; @@ -69,9 +70,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter { final Resources res = context.getResources(); if (!mReserveOverflowSet) { - // TODO Use the no-buttons specifier instead here - mReserveOverflow = res.getConfiguration() - .isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE); + mReserveOverflow = !ViewConfiguration.get(context).hasPermanentMenuKey(); } if (!mWidthLimitSet) { diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index d710cfae56f3..953328c03ddd 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -105,24 +105,19 @@ public class ActionBarContainer extends FrameLayout { public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int nonTabHeight = 0; - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); + if (mActionBarView == null) return; - if (child == mTabContainer) continue; - - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - nonTabHeight = Math.max(nonTabHeight, - child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); - } + final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams(); + final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 : + mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { final int mode = MeasureSpec.getMode(heightMeasureSpec); if (mode == MeasureSpec.AT_MOST) { final int maxHeight = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(getMeasuredWidth(), - Math.min(nonTabHeight + mTabContainer.getMeasuredHeight(), maxHeight)); + Math.min(actionBarViewHeight + mTabContainer.getMeasuredHeight(), + maxHeight)); } } } @@ -137,12 +132,14 @@ public class ActionBarContainer extends FrameLayout { if ((mActionBarView.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == 0) { // Not showing home, put tabs on top. final int count = getChildCount(); - for (int i = 0; i < count; i++){ + for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child == mTabContainer) continue; - child.offsetTopAndBottom(tabHeight); + if (!mActionBarView.isCollapsed()) { + child.offsetTopAndBottom(tabHeight); + } } mTabContainer.layout(l, 0, r, tabHeight); } else { diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index fc439944f518..3e3eeab4cbed 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -287,7 +287,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0); } - if (mMenuView != null) { + if (mMenuView != null && mMenuView.getParent() == this) { availableWidth = measureChildView(mMenuView, availableWidth, childSpecHeight, 0); } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 8eb046ebd94d..09bc1fc3cf7f 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -117,6 +117,7 @@ public class ActionBarView extends AbsActionBarView { private boolean mUserTitle; private boolean mIncludeTabs; private boolean mIsCollapsable; + private boolean mIsCollapsed; private MenuBuilder mOptionsMenu; @@ -692,6 +693,10 @@ public class ActionBarView extends AbsActionBarView { mIsCollapsable = collapsable; } + public boolean isCollapsed() { + return mIsCollapsed; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int childCount = getChildCount(); @@ -708,9 +713,11 @@ public class ActionBarView extends AbsActionBarView { if (visibleChildren == 0) { // No size for an empty action bar when collapsable. setMeasuredDimension(0, 0); + mIsCollapsed = true; return; } } + mIsCollapsed = false; int widthMode = MeasureSpec.getMode(widthMeasureSpec); if (widthMode != MeasureSpec.EXACTLY) { diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java index 5b4d7ab143fa..2f7adf0c0b46 100644 --- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -15,6 +15,9 @@ */ package com.android.internal.widget; +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; import android.app.ActionBar; import android.content.Context; import android.graphics.drawable.Drawable; @@ -22,6 +25,7 @@ import android.text.TextUtils.TruncateAt; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.view.animation.DecelerateInterpolator; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; @@ -35,6 +39,13 @@ public class ScrollingTabContainerView extends HorizontalScrollView { int mMaxTabWidth; + protected Animator mVisibilityAnim; + protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener(); + + private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator(); + + private static final int FADE_DURATION = 200; + public ScrollingTabContainerView(Context context) { super(context); setHorizontalScrollBarEnabled(false); @@ -76,6 +87,30 @@ public class ScrollingTabContainerView extends HorizontalScrollView { } } + public void animateToVisibility(int visibility) { + if (mVisibilityAnim != null) { + mVisibilityAnim.cancel(); + } + if (visibility == VISIBLE) { + if (getVisibility() != VISIBLE) { + setAlpha(0); + } + ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1); + anim.setDuration(FADE_DURATION); + anim.setInterpolator(sAlphaInterpolator); + + anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); + anim.start(); + } else { + ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0); + anim.setDuration(FADE_DURATION); + anim.setInterpolator(sAlphaInterpolator); + + anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); + anim.start(); + } + } + public void animateToTab(int position) { final View tabView = mTabLayout.getChildAt(position); if (mTabSelector != null) { @@ -259,4 +294,38 @@ public class ScrollingTabContainerView extends HorizontalScrollView { } } } + + protected class VisibilityAnimListener implements Animator.AnimatorListener { + private boolean mCanceled = false; + private int mFinalVisibility; + + public VisibilityAnimListener withFinalVisibility(int visibility) { + mFinalVisibility = visibility; + return this; + } + + @Override + public void onAnimationStart(Animator animation) { + setVisibility(VISIBLE); + mVisibilityAnim = animation; + mCanceled = false; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mCanceled) return; + + mVisibilityAnim = null; + setVisibility(mFinalVisibility); + } + + @Override + public void onAnimationCancel(Animator animation) { + mCanceled = true; + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + } } diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java index 5b3510428f7b..04bb6892c1ae 100644 --- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java +++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java @@ -20,7 +20,7 @@ import java.util.ArrayList; import android.animation.Animator; import android.animation.Animator.AnimatorListener; -import android.animation.ObjectAnimator; +import android.animation.AnimatorListenerAdapter; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; @@ -43,9 +43,9 @@ import com.android.internal.R; * A special widget containing a center and outer ring. Moving the center ring to the outer ring * causes an event that can be caught by implementing OnTriggerListener. */ -public class MultiWaveView extends View implements AnimatorUpdateListener { +public class MultiWaveView extends View { private static final String TAG = "MultiWaveView"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; // Wave state machine private static final int STATE_IDLE = 0; @@ -67,14 +67,15 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { } // Tune-able parameters - private static final int CHEVRON_INCREMENTAL_DELAY = 50; - private static final int CHEVRON_ANIMATION_DURATION = 1000; - private static final int RETURN_TO_HOME_DURATION = 150; - private static final int HIDE_ANIMATION_DELAY = 500; - private static final int HIDE_ANIMATION_DURACTION = 2000; + private static final int CHEVRON_INCREMENTAL_DELAY = 160; + private static final int CHEVRON_ANIMATION_DURATION = 650; + private static final int RETURN_TO_HOME_DELAY = 1200; + private static final int RETURN_TO_HOME_DURATION = 300; + private static final int HIDE_ANIMATION_DELAY = 200; + private static final int HIDE_ANIMATION_DURATION = RETURN_TO_HOME_DELAY; private static final int SHOW_ANIMATION_DURATION = 0; private static final int SHOW_ANIMATION_DELAY = 0; - private TimeInterpolator mChevronAnimationInterpolator = Ease.Quint.easeOut; + private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut; private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>(); private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>(); @@ -99,14 +100,31 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { private float mHitRadius = 0.0f; private float mSnapMargin = 0.0f; private boolean mDragging; + private int mNewTargetResources; - private AnimatorListener mResetListener = new Animator.AnimatorListener() { - public void onAnimationStart(Animator animation) { } - public void onAnimationRepeat(Animator animation) { } - public void onAnimationEnd(Animator animation) { + private AnimatorListener mResetListener = new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animator) { switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY); } - public void onAnimationCancel(Animator animation) { } + }; + + private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() { + public void onAnimationUpdate(ValueAnimator animation) { + invalidateGlobalRegion(mHandleDrawable); + invalidate(); + } + }; + + private boolean mAnimatingTargets; + private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animator) { + if (mNewTargetResources != 0) { + internalSetTargetResources(mNewTargetResources); + mNewTargetResources = 0; + hideTargets(false); + } + mAnimatingTargets = false; + } }; public MultiWaveView(Context context) { @@ -135,31 +153,23 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { mOuterRing = new TargetDrawable(res, a.getDrawable(R.styleable.MultiWaveView_waveDrawable)); // Read chevron animation drawables - Drawable leftChevron = a.getDrawable(R.styleable.MultiWaveView_leftChevronDrawable); - for (int i = 0; i < mFeedbackCount; i++) { - mChevronDrawables.add( - leftChevron != null ? new TargetDrawable(res, leftChevron) : null); - } - Drawable rightChevron = a.getDrawable(R.styleable.MultiWaveView_rightChevronDrawable); - for (int i = 0; i < mFeedbackCount; i++) { - mChevronDrawables.add( - rightChevron != null ? new TargetDrawable(res, rightChevron) : null); - } - Drawable topChevron = a.getDrawable(R.styleable.MultiWaveView_topChevronDrawable); - for (int i = 0; i < mFeedbackCount; i++) { - mChevronDrawables.add( - topChevron != null ? new TargetDrawable(res, topChevron) : null); - } - Drawable bottomChevron = a.getDrawable(R.styleable.MultiWaveView_bottomChevronDrawable); - for (int i = 0; i < mFeedbackCount; i++) { - mChevronDrawables.add( - bottomChevron != null ? new TargetDrawable(res, bottomChevron) : null); + final int chevrons[] = { R.styleable.MultiWaveView_leftChevronDrawable, + R.styleable.MultiWaveView_rightChevronDrawable, + R.styleable.MultiWaveView_topChevronDrawable, + R.styleable.MultiWaveView_bottomChevronDrawable + }; + for (int chevron : chevrons) { + Drawable chevronDrawable = a.getDrawable(chevron); + for (int i = 0; i < mFeedbackCount; i++) { + mChevronDrawables.add( + chevronDrawable != null ? new TargetDrawable(res, chevronDrawable) : null); + } } // Read array of target drawables TypedValue outValue = new TypedValue(); if (a.getValue(R.styleable.MultiWaveView_targetDrawables, outValue)) { - setTargetResources(outValue.resourceId); + internalSetTargetResources(outValue.resourceId); } if (mTargetDrawables == null || mTargetDrawables.size() == 0) { throw new IllegalStateException("Must specify at least one target drawable"); @@ -205,7 +215,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { case STATE_FIRST_TOUCH: stopHandleAnimation(); deactivateTargets(); - showTargets(); + showTargets(true); mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE); setGrabbedState(OnTriggerListener.CENTER_HANDLE); break; @@ -228,17 +238,18 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { * mFeedbackCount items in the order: left, right, top, bottom. */ private void startChevronAnimation() { - final float r = mHandleDrawable.getWidth() / 2; + final float r = mHandleDrawable.getWidth() * 0.4f; + final float chevronAnimationDistance = mOuterRadius * 0.8f; final float from[][] = { {mWaveCenterX - r, mWaveCenterY}, // left {mWaveCenterX + r, mWaveCenterY}, // right {mWaveCenterX, mWaveCenterY - r}, // top {mWaveCenterX, mWaveCenterY + r} }; // bottom final float to[][] = { - {mWaveCenterX - mOuterRadius, mWaveCenterY}, // left - {mWaveCenterX + mOuterRadius, mWaveCenterY}, // right - {mWaveCenterX, mWaveCenterY - mOuterRadius}, // top - {mWaveCenterX, mWaveCenterY + mOuterRadius} }; // bottom + {mWaveCenterX - chevronAnimationDistance, mWaveCenterY}, // left + {mWaveCenterX + chevronAnimationDistance, mWaveCenterY}, // right + {mWaveCenterX, mWaveCenterY - chevronAnimationDistance}, // top + {mWaveCenterX, mWaveCenterY + chevronAnimationDistance} }; // bottom mChevronAnimations.clear(); for (int direction = 0; direction < 4; direction++) { @@ -254,7 +265,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { "x", new float[] { from[direction][0], to[direction][0] }, "y", new float[] { from[direction][1], to[direction][1] }, "alpha", new float[] {1.0f, 0.0f}, - "onUpdate", this)); + "onUpdate", mUpdateListener)); } } } @@ -308,70 +319,109 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { } private void doFinish() { - // Inform listener of any active targets. Typically only one will be active. final int activeTarget = mActiveTarget; boolean targetHit = activeTarget != -1; - if (targetHit) { - Log.v(TAG, "Finish with target hit = " + targetHit); - dispatchTriggerEvent(mActiveTarget); - } - - setGrabbedState(OnTriggerListener.NO_HANDLE); - - // Animate finger outline back to home position - mHandleDrawable.setAlpha(targetHit ? 0.0f : 1.0f); - mHandleAnimation = Tweener.to(mHandleDrawable, RETURN_TO_HOME_DURATION, - "ease", Ease.Quart.easeOut, - "delay", targetHit ? HIDE_ANIMATION_DELAY : 0, - "alpha", 1.0f, - "x", mWaveCenterX, - "y", mWaveCenterY, - "onUpdate", this, - "onComplete", mResetListener); // Hide unselected targets hideTargets(true); // Highlight the selected one + mHandleDrawable.setAlpha(targetHit ? 0.0f : 1.0f); if (targetHit) { mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE); + + hideUnselected(activeTarget); + + // Inform listener of any active targets. Typically only one will be active. + if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit); + dispatchTriggerEvent(mActiveTarget); + mHandleAnimation = Tweener.to(mHandleDrawable, 0, + "ease", Ease.Quart.easeOut, + "delay", RETURN_TO_HOME_DELAY, + "alpha", 1.0f, + "x", mWaveCenterX, + "y", mWaveCenterY, + "onUpdate", mUpdateListener, + "onComplete", mResetListener); + } else { + // Animate finger outline back to home position + mHandleAnimation = Tweener.to(mHandleDrawable, RETURN_TO_HOME_DURATION, + "ease", Ease.Quart.easeOut, + "delay", 0, + "alpha", 1.0f, + "x", mWaveCenterX, + "y", mWaveCenterY, + "onUpdate", mUpdateListener, + "onComplete", mResetListener); } + + setGrabbedState(OnTriggerListener.NO_HANDLE); + } + + private void hideUnselected(int active) { + for (int i = 0; i < mTargetDrawables.size(); i++) { + if (i != active) { + mTargetDrawables.get(i).setAlpha(0.0f); + } + } + mOuterRing.setAlpha(0.0f); } private void hideTargets(boolean animate) { if (mTargetAnimations.size() > 0) { stopTargetAnimation(); } - for (TargetDrawable target : mTargetDrawables) { - target.setState(TargetDrawable.STATE_INACTIVE); - mTargetAnimations.add(Tweener.to(target, - animate ? HIDE_ANIMATION_DURACTION : 0, + // Note: these animations should complete at the same time so that we can swap out + // the target assets asynchronously from the setTargetResources() call. + mAnimatingTargets = animate; + if (animate) { + final int duration = animate ? HIDE_ANIMATION_DURATION : 0; + for (TargetDrawable target : mTargetDrawables) { + target.setState(TargetDrawable.STATE_INACTIVE); + mTargetAnimations.add(Tweener.to(target, duration, + "alpha", 0.0f, + "delay", HIDE_ANIMATION_DELAY, + "onUpdate", mUpdateListener)); + } + mTargetAnimations.add(Tweener.to(mOuterRing, duration, "alpha", 0.0f, "delay", HIDE_ANIMATION_DELAY, - "onUpdate", this)); + "onUpdate", mUpdateListener, + "onComplete", mTargetUpdateListener)); + } else { + for (TargetDrawable target : mTargetDrawables) { + target.setState(TargetDrawable.STATE_INACTIVE); + target.setAlpha(0.0f); + } + mOuterRing.setAlpha(0.0f); } - mTargetAnimations.add(Tweener.to(mOuterRing, - animate ? HIDE_ANIMATION_DURACTION : 0, - "alpha", 0.0f, - "delay", HIDE_ANIMATION_DELAY, - "onUpdate", this)); } - private void showTargets() { + private void showTargets(boolean animate) { if (mTargetAnimations.size() > 0) { stopTargetAnimation(); } - for (TargetDrawable target : mTargetDrawables) { - target.setState(TargetDrawable.STATE_INACTIVE); - mTargetAnimations.add(Tweener.to(target, SHOW_ANIMATION_DURATION, + mAnimatingTargets = animate; + if (animate) { + for (TargetDrawable target : mTargetDrawables) { + target.setState(TargetDrawable.STATE_INACTIVE); + mTargetAnimations.add(Tweener.to(target, SHOW_ANIMATION_DURATION, + "alpha", 1.0f, + "delay", SHOW_ANIMATION_DELAY, + "onUpdate", mUpdateListener)); + } + mTargetAnimations.add(Tweener.to(mOuterRing, SHOW_ANIMATION_DURATION, "alpha", 1.0f, "delay", SHOW_ANIMATION_DELAY, - "onUpdate", this)); + "onUpdate", mUpdateListener, + "onComplete", mTargetUpdateListener)); + } else { + for (TargetDrawable target : mTargetDrawables) { + target.setState(TargetDrawable.STATE_INACTIVE); + target.setAlpha(1.0f); + } + mOuterRing.setAlpha(1.0f); } - mTargetAnimations.add(Tweener.to(mOuterRing, SHOW_ANIMATION_DURATION, - "alpha", 1.0f, - "delay", SHOW_ANIMATION_DELAY, - "onUpdate", this)); } private void stopTargetAnimation() { @@ -387,12 +437,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { } } - /** - * Loads an array of drawables from the given resourceId. - * - * @param resourceId - */ - public void setTargetResources(int resourceId) { + private void internalSetTargetResources(int resourceId) { Resources res = getContext().getResources(); TypedArray array = res.obtainTypedArray(resourceId); int count = array.length(); @@ -402,6 +447,21 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { targetDrawables.add(new TargetDrawable(res, drawable)); } mTargetDrawables = targetDrawables; + updateTargetPositions(); + } + + /** + * Loads an array of drawables from the given resourceId. + * + * @param resourceId + */ + public void setTargetResources(int resourceId) { + if (mAnimatingTargets) { + // postpone this change until we return to the initial state + mNewTargetResources = resourceId; + } else { + internalSetTargetResources(resourceId); + } } /** @@ -590,20 +650,9 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { } } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - final int width = right - left; - final int height = bottom - top; - - mWaveCenterX = mHorizontalOffset + Math.max(width, mOuterRing.getWidth() ) / 2; - mWaveCenterY = mVerticalOffset + Math.max(height, mOuterRing.getHeight()) / 2; - moveHandleTo(mWaveCenterX, mWaveCenterY, false); - mOuterRing.setX(mWaveCenterX); - mOuterRing.setY(Math.max(mWaveCenterY, mWaveCenterY)); - mOuterRing.setAlpha(0.0f); + private void performInitialLayout(float centerX, float centerY) { if (mOuterRadius == 0.0f) { - mOuterRadius = 0.5f*(float) Math.sqrt(dist2(mWaveCenterX, mWaveCenterY)); + mOuterRadius = 0.5f*(float) Math.sqrt(dist2(centerX, centerY)); } if (mHitRadius == 0.0f) { // Use the radius of inscribed circle of the first target. @@ -613,6 +662,35 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics()); } + hideChevrons(); + hideTargets(false); + moveHandleTo(centerX, centerY, false); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + final int width = right - left; + final int height = bottom - top; + float newWaveCenterX = mHorizontalOffset + Math.max(width, mOuterRing.getWidth() ) / 2; + float newWaveCenterY = mVerticalOffset + Math.max(height, mOuterRing.getHeight()) / 2; + if (newWaveCenterX != mWaveCenterX || newWaveCenterY != mWaveCenterY) { + if (mWaveCenterX == 0 && mWaveCenterY == 0) { + performInitialLayout(newWaveCenterX, newWaveCenterY); + } + mWaveCenterX = newWaveCenterX; + mWaveCenterY = newWaveCenterY; + + mOuterRing.setX(mWaveCenterX); + mOuterRing.setY(Math.max(mWaveCenterY, mWaveCenterY)); + + updateTargetPositions(); + } + if (DEBUG) dump(); + } + + private void updateTargetPositions() { + // Reposition the target drawables if the view changed. for (int i = 0; i < mTargetDrawables.size(); i++) { final TargetDrawable targetIcon = mTargetDrawables.get(i); double angle = -2.0f * Math.PI * i / mTargetDrawables.size(); @@ -620,11 +698,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { float yPosition = mWaveCenterY + mOuterRadius * (float) Math.sin(angle); targetIcon.setX(xPosition); targetIcon.setY(yPosition); - targetIcon.setAlpha(0.0f); } - hideChevrons(); - hideTargets(false); - if (DEBUG) dump(); } private void hideChevrons() { @@ -655,11 +729,6 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { mOnTriggerListener = listener; } - public void onAnimationUpdate(ValueAnimator animation) { - invalidateGlobalRegion(mHandleDrawable); - invalidate(); - } - private float square(float d) { return d * d; } diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java new file mode 100644 index 000000000000..306d2236693a --- /dev/null +++ b/core/java/com/android/server/NetworkManagementSocketTagger.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import dalvik.system.SocketTagger; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.SocketException; +import java.nio.charset.Charsets; + +/** + * Assigns tags to sockets for traffic stats. + */ +public final class NetworkManagementSocketTagger extends SocketTagger { + + private static final boolean LOGI = false; + + private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() { + @Override protected SocketTags initialValue() { + return new SocketTags(); + } + }; + + public static void install() { + SocketTagger.set(new NetworkManagementSocketTagger()); + } + + public static void setThreadSocketStatsTag(int tag) { + threadSocketTags.get().statsTag = tag; + } + + public static void setThreadSocketStatsUid(int uid) { + threadSocketTags.get().statsUid = uid; + } + + @Override public void tag(FileDescriptor fd) throws SocketException { + final SocketTags options = threadSocketTags.get(); + if (LOGI) { + System.logI("tagSocket(" + fd.getInt$() + ") with statsTag=" + + options.statsTag + ", statsUid=" + options.statsUid); + } + try { + // TODO: skip tagging when options would be no-op + tagSocketFd(fd, options.statsTag, options.statsUid); + } catch (IOException e) { + throw new SocketException("Problem tagging socket", e); + } + } + + private void tagSocketFd(FileDescriptor fd, int tag, int uid) throws IOException { + final int fdNum = fd.getInt$(); + if (fdNum == -1 || (tag == -1 && uid == -1)) return; + + String cmd = "t " + fdNum; + if (tag == -1) { + // Case where just the uid needs adjusting. But probably the caller + // will want to track his own name here, just in case. + cmd += " 0"; + } else { + cmd += " " + tagToKernel(tag); + } + if (uid != -1) { + cmd += " " + uid; + } + internalModuleCtrl(cmd); + } + + @Override public void untag(FileDescriptor fd) throws SocketException { + if (LOGI) { + System.logI("untagSocket(" + fd.getInt$() + ")"); + } + try { + unTagSocketFd(fd); + } catch (IOException e) { + throw new SocketException("Problem untagging socket", e); + } + } + + private void unTagSocketFd(FileDescriptor fd) throws IOException { + int fdNum = fd.getInt$(); + if (fdNum == -1) return; + String cmd = "u " + fdNum; + internalModuleCtrl(cmd); + } + + public static class SocketTags { + public int statsTag = -1; + public int statsUid = -1; + } + + /** + * Sends commands to the kernel netfilter module. + * + * @param cmd command string for the qtaguid netfilter module. May not be null. + * <p>Supports: + * <ul><li>tag a socket:<br> + * <code>t <i>sock_fd</i> <i>acct_tag</i> [<i>uid_in_case_caller_is_acting_on_behalf_of</i>]</code><br> + * <code>*_tag</code> defaults to default_policy_tag_from_uid(uid_of_caller)<br> + * <code>acct_tag</code> is either 0 or greater that 2^32.<br> + * <code>uid_*</code> is only settable by privileged UIDs (DownloadManager,...) + * </li> + * <li>untag a socket, preserving counters:<br> + * <code>u <i>sock_fd</i></code> + * </li></ul> + * <p>Notes:<br> + * <ul><li><i>sock_fd</i> is withing the callers process space.</li> + * <li><i>*_tag</i> are 64bit values</li></ul> + * + */ + private void internalModuleCtrl(String cmd) throws IOException { + final FileOutputStream procOut; + // TODO: Use something like + // android.os.SystemProperties.getInt("persist.bandwidth.enable", 0) + // to see if tagging should happen or not. + try { + procOut = new FileOutputStream("/proc/net/xt_qtaguid/ctrl"); + } catch (FileNotFoundException e) { + if (LOGI) { + System.logI("Can't talk to kernel module:" + e); + } + return; + } + try { + procOut.write(cmd.getBytes(Charsets.US_ASCII)); + } finally { + procOut.close(); + } + } + + /** + * Convert {@link Integer} tag to {@code /proc/} format. Assumes unsigned + * base-10 format like {@code 2147483647}. Currently strips signed bit to + * avoid using {@link java.math.BigInteger}. + */ + public static String tagToKernel(int tag) { + // TODO: eventually write in hex, since that's what proc exports + // TODO: migrate to direct integer instead of odd shifting + return Long.toString((((long) tag) << 32) & 0x7FFFFFFF00000000L); + } + + /** + * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming + * format like {@code 0x7fffffff00000000}. + */ + public static int kernelToTag(String string) { + // TODO: migrate to direct integer instead of odd shifting + return (int) (Long.decode(string) >> 32); + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 47902a8bc2ac..1a320608efae 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1384,7 +1384,7 @@ <permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" android:label="@string/permlab_readNetworkUsageHistory" android:description="@string/permdesc_readNetworkUsageHistory" - android:protectionLevel="signature" /> + android:protectionLevel="signatureOrSystem" /> <!-- Allows an application to manage network policies (such as warning and disable limits) and to define application-specific rules. @hide --> @@ -1393,6 +1393,14 @@ android:description="@string/permdesc_manageNetworkPolicy" android:protectionLevel="signature" /> + <!-- Allows an application to account its network traffic against other UIDs. Used + by system services like download manager and media server. Not for use by + third party apps. @hide --> + <permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" + android:label="@string/permlab_modifyNetworkAccounting" + android:description="@string/permdesc_modifyNetworkAccounting" + android:protectionLevel="signatureOrSystem" /> + <!-- C2DM permission. @hide Used internally. --> diff --git a/core/res/res/drawable-hdpi/spinner_black_16.png b/core/res/res/drawable-hdpi/spinner_black_16.png Binary files differindex eb348679c24e..ef5ca352a754 100644 --- a/core/res/res/drawable-hdpi/spinner_black_16.png +++ b/core/res/res/drawable-hdpi/spinner_black_16.png diff --git a/core/res/res/drawable-hdpi/spinner_black_20.png b/core/res/res/drawable-hdpi/spinner_black_20.png Binary files differindex dac06d73be26..d938931d6348 100644 --- a/core/res/res/drawable-hdpi/spinner_black_20.png +++ b/core/res/res/drawable-hdpi/spinner_black_20.png diff --git a/core/res/res/drawable-hdpi/spinner_black_48.png b/core/res/res/drawable-hdpi/spinner_black_48.png Binary files differindex 337f72a9cfda..9d1efb7eb623 100644 --- a/core/res/res/drawable-hdpi/spinner_black_48.png +++ b/core/res/res/drawable-hdpi/spinner_black_48.png diff --git a/core/res/res/drawable-hdpi/spinner_black_76.png b/core/res/res/drawable-hdpi/spinner_black_76.png Binary files differindex 2edc3e7b9afd..0d90881110d7 100644 --- a/core/res/res/drawable-hdpi/spinner_black_76.png +++ b/core/res/res/drawable-hdpi/spinner_black_76.png diff --git a/core/res/res/drawable-hdpi/spinner_white_16.png b/core/res/res/drawable-hdpi/spinner_white_16.png Binary files differindex 7914a689de3e..32fa447b3bf6 100644 --- a/core/res/res/drawable-hdpi/spinner_white_16.png +++ b/core/res/res/drawable-hdpi/spinner_white_16.png diff --git a/core/res/res/drawable-hdpi/spinner_white_48.png b/core/res/res/drawable-hdpi/spinner_white_48.png Binary files differindex faee8caedfbc..31fa267f4ab1 100644 --- a/core/res/res/drawable-hdpi/spinner_white_48.png +++ b/core/res/res/drawable-hdpi/spinner_white_48.png diff --git a/core/res/res/drawable-hdpi/spinner_white_76.png b/core/res/res/drawable-hdpi/spinner_white_76.png Binary files differindex cd26379587e8..9f63292046e2 100644 --- a/core/res/res/drawable-hdpi/spinner_white_76.png +++ b/core/res/res/drawable-hdpi/spinner_white_76.png diff --git a/core/res/res/drawable-hdpi/textfield_default.9.png b/core/res/res/drawable-hdpi/textfield_default.9.png Binary files differindex 4c20179281b1..f7b6e9974324 100644..100755 --- a/core/res/res/drawable-hdpi/textfield_default.9.png +++ b/core/res/res/drawable-hdpi/textfield_default.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled.9.png b/core/res/res/drawable-hdpi/textfield_disabled.9.png Binary files differindex 81569d1b54b8..30115027bf53 100644..100755 --- a/core/res/res/drawable-hdpi/textfield_disabled.9.png +++ b/core/res/res/drawable-hdpi/textfield_disabled.9.png diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png Binary files differindex 2591490045a0..e0f82eb06d4b 100644..100755 --- a/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png +++ b/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png diff --git a/core/res/res/drawable-hdpi/textfield_selected.9.png b/core/res/res/drawable-hdpi/textfield_selected.9.png Binary files differindex a36ed72f487a..cf2cae30bf42 100644..100755 --- a/core/res/res/drawable-hdpi/textfield_selected.9.png +++ b/core/res/res/drawable-hdpi/textfield_selected.9.png diff --git a/core/res/res/drawable-mdpi/spinner_black_16.png b/core/res/res/drawable-mdpi/spinner_black_16.png Binary files differindex 5ee33cea6fa7..4b7fdfe09358 100644 --- a/core/res/res/drawable-mdpi/spinner_black_16.png +++ b/core/res/res/drawable-mdpi/spinner_black_16.png diff --git a/core/res/res/drawable-mdpi/spinner_black_20.png b/core/res/res/drawable-mdpi/spinner_black_20.png Binary files differindex e55b60dc6cce..86d7a205e867 100755 --- a/core/res/res/drawable-mdpi/spinner_black_20.png +++ b/core/res/res/drawable-mdpi/spinner_black_20.png diff --git a/core/res/res/drawable-mdpi/spinner_black_48.png b/core/res/res/drawable-mdpi/spinner_black_48.png Binary files differindex 3a681926b537..f1571f980f04 100644 --- a/core/res/res/drawable-mdpi/spinner_black_48.png +++ b/core/res/res/drawable-mdpi/spinner_black_48.png diff --git a/core/res/res/drawable-mdpi/spinner_black_76.png b/core/res/res/drawable-mdpi/spinner_black_76.png Binary files differindex ec57460277a6..e9f6e8f11ce4 100644 --- a/core/res/res/drawable-mdpi/spinner_black_76.png +++ b/core/res/res/drawable-mdpi/spinner_black_76.png diff --git a/core/res/res/drawable-mdpi/spinner_white_16.png b/core/res/res/drawable-mdpi/spinner_white_16.png Binary files differindex dd2e1fd7da16..650e315c7c60 100644 --- a/core/res/res/drawable-mdpi/spinner_white_16.png +++ b/core/res/res/drawable-mdpi/spinner_white_16.png diff --git a/core/res/res/drawable-mdpi/spinner_white_48.png b/core/res/res/drawable-mdpi/spinner_white_48.png Binary files differindex d25a33e24534..11eacf8a9a26 100644 --- a/core/res/res/drawable-mdpi/spinner_white_48.png +++ b/core/res/res/drawable-mdpi/spinner_white_48.png diff --git a/core/res/res/drawable-mdpi/spinner_white_76.png b/core/res/res/drawable-mdpi/spinner_white_76.png Binary files differindex f53e8ffdb193..6c31bc39f942 100644 --- a/core/res/res/drawable-mdpi/spinner_white_76.png +++ b/core/res/res/drawable-mdpi/spinner_white_76.png diff --git a/core/res/res/drawable-mdpi/textfield_default.9.png b/core/res/res/drawable-mdpi/textfield_default.9.png Binary files differindex cc78e6c92a97..1a59bb2ffe54 100644 --- a/core/res/res/drawable-mdpi/textfield_default.9.png +++ b/core/res/res/drawable-mdpi/textfield_default.9.png diff --git a/core/res/res/drawable-mdpi/textfield_disabled.9.png b/core/res/res/drawable-mdpi/textfield_disabled.9.png Binary files differindex 9c77149beaca..800205af7ebd 100644 --- a/core/res/res/drawable-mdpi/textfield_disabled.9.png +++ b/core/res/res/drawable-mdpi/textfield_disabled.9.png diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png Binary files differindex 6d47708ec9bf..59e1536c9c4e 100644 --- a/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png +++ b/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png diff --git a/core/res/res/drawable-mdpi/textfield_selected.9.png b/core/res/res/drawable-mdpi/textfield_selected.9.png Binary files differindex 0c1b4461fe07..faadace85cf9 100644 --- a/core/res/res/drawable-mdpi/textfield_selected.9.png +++ b/core/res/res/drawable-mdpi/textfield_selected.9.png diff --git a/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png b/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png Binary files differnew file mode 100644 index 000000000000..cdc0afb4c30e --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png diff --git a/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png b/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png Binary files differnew file mode 100644 index 000000000000..36b044822278 --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png diff --git a/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png b/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png Binary files differnew file mode 100644 index 000000000000..3a7a8b347105 --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png diff --git a/core/res/res/drawable-xhdpi/popup_center_bright.9.png b/core/res/res/drawable-xhdpi/popup_center_bright.9.png Binary files differnew file mode 100644 index 000000000000..b1a8e3e182a1 --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_center_bright.9.png diff --git a/core/res/res/drawable-xhdpi/popup_center_dark.9.png b/core/res/res/drawable-xhdpi/popup_center_dark.9.png Binary files differnew file mode 100644 index 000000000000..87378e153330 --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_center_dark.9.png diff --git a/core/res/res/drawable-xhdpi/popup_center_medium.9.png b/core/res/res/drawable-xhdpi/popup_center_medium.9.png Binary files differnew file mode 100644 index 000000000000..ea29ed48f4ff --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_center_medium.9.png diff --git a/core/res/res/drawable-xhdpi/popup_full_bright.9.png b/core/res/res/drawable-xhdpi/popup_full_bright.9.png Binary files differnew file mode 100644 index 000000000000..114faa0e1b8c --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_full_bright.9.png diff --git a/core/res/res/drawable-xhdpi/popup_full_dark.9.png b/core/res/res/drawable-xhdpi/popup_full_dark.9.png Binary files differnew file mode 100644 index 000000000000..996beaa6e94b --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_full_dark.9.png diff --git a/core/res/res/drawable-xhdpi/popup_top_bright.9.png b/core/res/res/drawable-xhdpi/popup_top_bright.9.png Binary files differnew file mode 100644 index 000000000000..64e41394201c --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_top_bright.9.png diff --git a/core/res/res/drawable-xhdpi/popup_top_dark.9.png b/core/res/res/drawable-xhdpi/popup_top_dark.9.png Binary files differnew file mode 100644 index 000000000000..902bc291319e --- /dev/null +++ b/core/res/res/drawable-xhdpi/popup_top_dark.9.png diff --git a/core/res/res/drawable-xhdpi/spinner_black_16.png b/core/res/res/drawable-xhdpi/spinner_black_16.png Binary files differnew file mode 100644 index 000000000000..5b1422ccf45b --- /dev/null +++ b/core/res/res/drawable-xhdpi/spinner_black_16.png diff --git a/core/res/res/drawable-xhdpi/spinner_black_20.png b/core/res/res/drawable-xhdpi/spinner_black_20.png Binary files differnew file mode 100644 index 000000000000..5f53e38226af --- /dev/null +++ b/core/res/res/drawable-xhdpi/spinner_black_20.png diff --git a/core/res/res/drawable-xhdpi/spinner_black_48.png b/core/res/res/drawable-xhdpi/spinner_black_48.png Binary files differnew file mode 100644 index 000000000000..3aab620dc048 --- /dev/null +++ b/core/res/res/drawable-xhdpi/spinner_black_48.png diff --git a/core/res/res/drawable-xhdpi/spinner_black_76.png b/core/res/res/drawable-xhdpi/spinner_black_76.png Binary files differnew file mode 100644 index 000000000000..2968d8c376c7 --- /dev/null +++ b/core/res/res/drawable-xhdpi/spinner_black_76.png diff --git a/core/res/res/drawable-xhdpi/spinner_white_16.png b/core/res/res/drawable-xhdpi/spinner_white_16.png Binary files differnew file mode 100644 index 000000000000..69be75229c28 --- /dev/null +++ b/core/res/res/drawable-xhdpi/spinner_white_16.png diff --git a/core/res/res/drawable-xhdpi/spinner_white_48.png b/core/res/res/drawable-xhdpi/spinner_white_48.png Binary files differnew file mode 100644 index 000000000000..7b196bce06ae --- /dev/null +++ b/core/res/res/drawable-xhdpi/spinner_white_48.png diff --git a/core/res/res/drawable-xhdpi/spinner_white_76.png b/core/res/res/drawable-xhdpi/spinner_white_76.png Binary files differnew file mode 100644 index 000000000000..ba470057bb0c --- /dev/null +++ b/core/res/res/drawable-xhdpi/spinner_white_76.png diff --git a/core/res/res/layout-land/ssl_certificate.xml b/core/res/res/layout-land/ssl_certificate.xml index 56e4e70ca033..c3e6deb832e3 100644 --- a/core/res/res/layout-land/ssl_certificate.xml +++ b/core/res/res/layout-land/ssl_certificate.xml @@ -20,6 +20,7 @@ android:layout_height="wrap_content" > <LinearLayout + android:id="@+id/body" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > diff --git a/core/res/res/layout-sw600dp/date_picker_dialog.xml b/core/res/res/layout-sw600dp/date_picker_dialog.xml new file mode 100644 index 000000000000..004d52a6593b --- /dev/null +++ b/core/res/res/layout-sw600dp/date_picker_dialog.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<DatePicker xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/datePicker" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> diff --git a/core/res/res/layout-sw600dp/number_picker.xml b/core/res/res/layout-sw600dp/number_picker.xml new file mode 100644 index 000000000000..807daf2b2dae --- /dev/null +++ b/core/res/res/layout-sw600dp/number_picker.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <ImageButton android:id="@+id/increment" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + style="?android:attr/numberPickerUpButtonStyle" + android:contentDescription="@string/number_picker_increment_button" /> + + <EditText android:id="@+id/numberpicker_input" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + style="?android:attr/numberPickerInputTextStyle" /> + + <ImageButton android:id="@+id/decrement" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + style="?android:attr/numberPickerDownButtonStyle" + android:contentDescription="@string/number_picker_decrement_button" /> + +</merge> diff --git a/core/res/res/layout/date_picker_dialog.xml b/core/res/res/layout/date_picker_dialog.xml index 004d52a6593b..db8f311c99c7 100644 --- a/core/res/res/layout/date_picker_dialog.xml +++ b/core/res/res/layout/date_picker_dialog.xml @@ -22,4 +22,6 @@ android:layout_gravity="center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:spinnersShown="true" + android:calendarViewShown="false" /> diff --git a/core/res/res/layout/date_picker_holo.xml b/core/res/res/layout/date_picker_holo.xml index 026cbfb8bca1..8627637b3b96 100644 --- a/core/res/res/layout/date_picker_holo.xml +++ b/core/res/res/layout/date_picker_holo.xml @@ -77,7 +77,8 @@ android:id="@+id/calendar_view" android:layout_width="245dip" android:layout_height="280dip" - android:layout_marginLeft="22dip" + android:layout_marginLeft="16dip" + android:layout_marginRight="16dip" android:layout_weight="1" android:focusable="true" android:focusableInTouchMode="true" diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml index 8ba08f68f17f..30df91b24450 100644 --- a/core/res/res/layout/keyguard_screen_password_landscape.xml +++ b/core/res/res/layout/keyguard_screen_password_landscape.xml @@ -156,7 +156,7 @@ /> <!-- Column 1 --> - <Space android:layout_columnWeight="1" android:layout_rowSpan="11" /> + <Space android:layout_widthSpec="canStretch" android:layout_rowSpan="11" /> <!-- Column 2 - password entry field and PIN keyboard --> <LinearLayout diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml index 5588adc88a81..c8597209e763 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml @@ -169,7 +169,10 @@ </LinearLayout> <!-- Column 1 --> - <Space android:width="20dip" android:layout_columnWeight="1" android:layout_rowSpan="10" /> + <Space + android:width="20dip" + android:layout_heightSpec="canStretch" + android:layout_rowSpan="10" /> <!-- Column 2 --> <com.android.internal.widget.multiwaveview.MultiWaveView diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml index d0538dd09b63..0070ed0ec87b 100644 --- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml @@ -100,9 +100,11 @@ <!-- TODO: remove hard coded height since layout_rowWeight doesn't seem to be working --> <Space - android:layout_height="43dip" - android:layout_gravity="fill" - android:layout_rowWeight="1" android:layout_columnWeight="1" /> + android:layout_height="43dip" + android:layout_gravity="fill" + android:layout_heightSpec="canStretch" + android:layout_widthSpec="canStretch" + /> <TextView android:id="@+id/carrier" android:layout_gravity="right" diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml index 774f830925f5..28c530291bcd 100644 --- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml @@ -125,7 +125,7 @@ android:layout_marginBottom="4dip" android:layout_marginLeft="8dip" android:layout_gravity="center|bottom" - android:layout_rowWeight="1" + android:layout_heightSpec="canStretch" /> <TextView diff --git a/core/res/res/layout/number_picker.xml b/core/res/res/layout/number_picker.xml index 807daf2b2dae..f2f524caf813 100644 --- a/core/res/res/layout/number_picker.xml +++ b/core/res/res/layout/number_picker.xml @@ -23,6 +23,8 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" style="?android:attr/numberPickerUpButtonStyle" + android:paddingTop="22dip" + android:paddingBottom="22dip" android:contentDescription="@string/number_picker_increment_button" /> <EditText android:id="@+id/numberpicker_input" @@ -34,6 +36,8 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" style="?android:attr/numberPickerDownButtonStyle" + android:paddingTop="22dip" + android:paddingBottom="22dip" android:contentDescription="@string/number_picker_decrement_button" /> </merge> diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml index 475aa5922f89..fee27ebe67c3 100644 --- a/core/res/res/layout/search_view.xml +++ b/core/res/res/layout/search_view.xml @@ -54,8 +54,8 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" - android:layout_marginLeft="16dip" - android:layout_marginRight="16dip" + android:layout_marginLeft="8dip" + android:layout_marginRight="8dip" android:layout_marginTop="4dip" android:layout_marginBottom="4dip" android:orientation="horizontal"> @@ -87,7 +87,6 @@ android:layout_gravity="bottom" android:paddingLeft="8dip" android:paddingRight="6dip" - android:drawablePadding="2dip" android:singleLine="true" android:ellipsize="end" android:background="@null" diff --git a/core/res/res/layout/ssl_certificate.xml b/core/res/res/layout/ssl_certificate.xml index 7206077ce6f5..ae661ce8ef67 100644 --- a/core/res/res/layout/ssl_certificate.xml +++ b/core/res/res/layout/ssl_certificate.xml @@ -20,6 +20,7 @@ android:layout_height="wrap_content" > <LinearLayout + android:id="@+id/body" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 4ff8fafdfccd..c84a591097db 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2212,7 +2212,8 @@ <!-- The event types this serivce would like to receive as specified in {@link android.view.accessibility.AccessibilityEvent}. This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> <attr name="accessibilityEventTypes"> <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED} events.--> <flag name="typeViewClicked" value="0x00000001" /> @@ -2236,17 +2237,24 @@ <flag name="typeTouchExplorationGestureStart" value="0x00000200" /> <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END} events. --> <flag name="typeTouchExplorationGestureEnd" value="0x00000400" /> + <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events. --> + <flag name="typeWindowContentChanged" value="0x00000800" /> + <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED} events. --> + <flag name="typeViewScrolled" value="0x000001000" /> + <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} events. --> + <flag name="typeViewTextSelectionChanged" value="0x000002000" /> <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPES_ALL_MASK} i.e. all events. --> <flag name="typeAllMask" value="0xffffffff" /> </attr> <!-- Comma separated package names from which this serivce would like to receive events (leave out for all packages). - This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> <attr name="packageNames" format="string" /> <!-- The feedback types this serivce provides as specified in {@link android.accessibilityservice.AccessibilityServiceInfo}. This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> <attr name="accessibilityFeedbackType"> <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_SPOKEN} feedback. --> <flag name="feedbackSpoken" value="0x00000001" /> @@ -2259,14 +2267,16 @@ <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_GENERIC} feedback. --> <flag name="feedbackGeneric" value="0x00000010" /> </attr> - <!-- The minimal period in milliseconds between two accessibility events are sent - to this serivce. This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + <!-- The minimal period in milliseconds between two accessibility events of the same type + are sent to this serivce. This setting can be changed at runtime by calling + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->> <attr name="notificationTimeout" format="integer" /> <!-- Additional flags as specified in {@link android.accessibilityservice.AccessibilityServiceInfo}. This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> <attr name="accessibilityFlags"> <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#DEFAULT} --> <flag name="flagDefault" value="0x00000001" /> @@ -2275,7 +2285,7 @@ the settings for this service. This setting cannot be changed at runtime. --> <attr name="settingsActivity" /> <!-- Flag whether the accessibility service wants to be able to retrieve the - focused window content. This setting cannot be changed at runtime. --> + active window content. This setting cannot be changed at runtime. --> <attr name="canRetrieveWindowContent" format="boolean" /> </declare-styleable> @@ -3318,11 +3328,6 @@ The default is one. See {@link android.widget.GridLayout.Group}. --> <attr name="layout_rowSpan" format="integer" min="1" /> - <!-- A number indicating the relative proportion of available space that - should be taken by this group of cells. - The default is zero. - See {@link android.widget.GridLayout.LayoutParams#columnWeight}. --> - <attr name="layout_rowWeight" format="float" /> <!-- The column boundary delimiting the left of the group of cells occupied by this view. --> <attr name="layout_column" /> @@ -3331,15 +3336,38 @@ The default is one. See {@link android.widget.GridLayout.Group}. --> <attr name="layout_columnSpan" format="integer" min="1" /> - <!-- A number indicating the relative proportion of available space that - should be taken by this group of cells. - The default is zero. - See {@link android.widget.GridLayout.LayoutParams#columnWeight}.--> - <attr name="layout_columnWeight" format="float" /> <!-- Gravity specifies how a component should be placed in its group of cells. The default is LEFT | BASELINE. See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. --> <attr name="layout_gravity" /> + <!-- A value specifying how much deficit or excess width this component can accomodate. + The default is FIXED. + See {@link android.widget.GridLayout.LayoutParams#widthSpec}.--> + <attr name="layout_widthSpec" > + <!-- If possible, width should be exactly as specified. + See {@link android.widget.GridLayout#FIXED}. --> + <enum name="fixed" value="0" /> + <!-- If possible, width should be less than or equal to the specified width. + See {@link android.widget.GridLayout#CAN_SHRINK}. --> + <enum name="canShrink" value="1" /> + <!-- If possible, width should be greater than or equal to the specified width. + See {@link android.widget.GridLayout#CAN_STRETCH}. --> + <enum name="canStretch" value="2" /> + </attr> + <!-- A value specifying how much deficit or excess height this component can accomodate. + The default is FIXED. + See {@link android.widget.GridLayout.LayoutParams#heightSpec}.--> + <attr name="layout_heightSpec" > + <!-- If possible, height should be exactly as specified. + See {@link android.widget.GridLayout#FIXED}. --> + <enum name="fixed" value="0" /> + <!-- If possible, height should be less than or equal to the specified height. + See {@link android.widget.GridLayout#CAN_SHRINK}. --> + <enum name="canShrink" value="1" /> + <!-- If possible, height should be greater than or equal to the specified height. + See {@link android.widget.GridLayout#CAN_STRETCH}. --> + <enum name="canStretch" value="2" /> + </attr> </declare-styleable> <declare-styleable name="FrameLayout_Layout"> <attr name="layout_gravity" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 827153e7243e..2c10b3dabe8f 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -658,4 +658,8 @@ This is intended to allow packaging drivers or tools for installation on a PC. --> <string translatable="false" name="config_isoImagePath"></string> + <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be + autodetected from the Configuration. --> + <bool name="config_showNavigationBar">false</bool> + </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index db6f98fa3400..945e0c483fad 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1738,9 +1738,11 @@ <public type="attr" name="layout_row" /> <public type="attr" name="layout_rowSpan" /> - <public type="attr" name="layout_rowWeight" /> <public type="attr" name="layout_columnSpan" /> - <public type="attr" name="layout_columnWeight" /> + + <public type="attr" name="layout_widthSpec" /> + <public type="attr" name="layout_heightSpec" /> + <public type="attr" name="actionModeSelectAllDrawable" /> <public type="attr" name="isAuxiliary" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index cd52a5aea219..c8b3b4fbfa81 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 --> @@ -2068,6 +2073,18 @@ <!-- Do not translate. Regex used by AutoFill. --> <string name="autofill_fax_re">fax<!-- fr-FR -->|télécopie|telecopie<!-- ja-JP -->|ファックス<!-- ru -->|факс<!-- zh-CN -->|传真<!-- zh-TW -->|傳真</string> + <!-- Do not translate. Regex used by AutoFill. --> + <string name="autofill_country_code_re">country.*code|ccode|_cc</string> + + <!-- Do not translate. Regex used by AutoFill. --> + <string name="autofill_area_code_notext_re">^\($</string> + + <!-- Do not translate. Regex used by AutoFill. --> + <string name="autofill_phone_prefix_separator_re">^-$|^\)$</string> + + <!-- Do not translate. Regex used by AutoFill. --> + <string name="autofill_phone_suffix_separator_re">^-$</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_readHistoryBookmarks">read Browser\'s history and bookmarks</string> @@ -2608,10 +2625,14 @@ <!-- USB_STORAGE_ERROR dialog ok button--> <string name="dlg_ok">OK</string> - <!-- USB_PREFERENCES: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across. This is the title --> - <string name="usb_preferences_notification_title">USB connected</string> + <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in MTP mode. This is the title --> + <string name="usb_mtp_notification_title">Connected as a media device</string> + <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in PTP mode. This is the title --> + <string name="usb_ptp_notification_title">Connected as a camera</string> + <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in mass storage mode (for installer CD image). This is the title --> + <string name="usb_cd_installer_notification_title">Connected as an installer</string> <!-- See USB_PREFERENCES. This is the message. --> - <string name="usb_preferece_notification_message">Select to configure USB file transfer.</string> + <string name="usb_notification_message">Touch for other USB options</string> <!-- External media format dialog strings --> <!-- This is the label for the activity, and should never be visible to the user. --> @@ -2783,10 +2804,10 @@ <string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string> <string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string> - <!-- Ticker text to show when VPN is active. --> - <string name="vpn_ticker"><xliff:g id="app" example="FooVPN client">%s</xliff:g> is activating VPN...</string> <!-- The title of the notification when VPN is active. --> - <string name="vpn_title">VPN is activated by <xliff:g id="app" example="FooVPN client">%s</xliff:g></string> + <string name="vpn_title">VPN is activated.</string> + <!-- The title of the notification when VPN is active with an application name. --> + <string name="vpn_title_long">VPN is activated by <xliff:g id="app" example="FooVPN client">%s</xliff:g></string> <!-- The text of the notification when VPN is active. --> <string name="vpn_text">Tap to manage the network.</string> <!-- The text of the notification when VPN is active with a session name. --> diff --git a/data/etc/platform.xml b/data/etc/platform.xml index b9c0d80ce25d..0b8d40f5d97c 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -84,6 +84,16 @@ <group gid="diag" /> </permission> + <!-- Group that can read detailed network usage statistics --> + <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"> + <group gid="net_bw_stats" /> + </permission> + + <!-- Group that can modify how network statistics are accounted --> + <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING"> + <group gid="net_bw_acct" /> + </permission> + <!-- ================================================================== --> <!-- ================================================================== --> <!-- ================================================================== --> diff --git a/data/fonts/DroidSansHebrew-Bold.ttf b/data/fonts/DroidSansHebrew-Bold.ttf Binary files differnew file mode 100644 index 000000000000..c1acb38df569 --- /dev/null +++ b/data/fonts/DroidSansHebrew-Bold.ttf diff --git a/data/fonts/DroidSansHebrew-Regular.ttf b/data/fonts/DroidSansHebrew-Regular.ttf Binary files differnew file mode 100644 index 000000000000..af6a58d8a500 --- /dev/null +++ b/data/fonts/DroidSansHebrew-Regular.ttf diff --git a/data/fonts/DroidSansHebrew.ttf b/data/fonts/DroidSansHebrew.ttf Binary files differdeleted file mode 100644 index 8d77e3e4cfaf..000000000000 --- a/data/fonts/DroidSansHebrew.ttf +++ /dev/null diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index 692ce3425d1e..d222c0b4d20e 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -18,7 +18,8 @@ PRODUCT_COPY_FILES := \ frameworks/base/data/fonts/DroidSans.ttf:system/fonts/DroidSans.ttf \ frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.ttf \ frameworks/base/data/fonts/DroidSansArabic.ttf:system/fonts/DroidSansArabic.ttf \ - frameworks/base/data/fonts/DroidSansHebrew.ttf:system/fonts/DroidSansHebrew.ttf \ + frameworks/base/data/fonts/DroidSansHebrew-Regular.ttf:system/fonts/DroidSansHebrew-Regular.ttf \ + frameworks/base/data/fonts/DroidSansHebrew-Bold.ttf:system/fonts/DroidSansHebrew-Bold.ttf \ frameworks/base/data/fonts/DroidSansThai.ttf:system/fonts/DroidSansThai.ttf \ frameworks/base/data/fonts/DroidSerif-Regular.ttf:system/fonts/DroidSerif-Regular.ttf \ frameworks/base/data/fonts/DroidSerif-Bold.ttf:system/fonts/DroidSerif-Bold.ttf \ diff --git a/docs/html/sdk/android-3.1-highlights.jd b/docs/html/sdk/android-3.1-highlights.jd index 3d132a3e8802..88bc1eec0c55 100644 --- a/docs/html/sdk/android-3.1-highlights.jd +++ b/docs/html/sdk/android-3.1-highlights.jd @@ -143,8 +143,8 @@ point, select, drag, scroll, hover, and other standard actions.</p> <p>To make the platform even better for gaming, Android 3.1 adds support for most PC joysticks and gamepads that are connected over USB or Bluetooth HID.</p> -<p>For example, users can connect Sony Playstation™ 3 and XBox 360™ game -controllers over USB (but not Bluetooth), Logitech Dual Action™ gamepads and +<p>For example, users can connect PlayStation<sup>®</sup>3 and Xbox 360<sup>®</sup> +game controllers over USB (but not Bluetooth), Logitech Dual Action™ gamepads and flight sticks, or a car racing controller. Game controllers that use proprietary networking or pairing are not supported by default, but in general, the platform supports most PC-connectible joysticks and gamepads.</p> diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java index 90a7ac22335b..1647ff3481a1 100644 --- a/graphics/java/android/graphics/SurfaceTexture.java +++ b/graphics/java/android/graphics/SurfaceTexture.java @@ -174,12 +174,15 @@ public class SurfaceTexture { * Retrieve the timestamp associated with the texture image set by the most recent call to * updateTexImage. * - * This timestamp is in nanoseconds, and is guaranteed to be monotonically increasing. The + * This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp + * should be unaffected by time-of-day adjustments, and for a camera should be strictly + * monotonic but for a MediaPlayer may be reset when the position is set. The * specific meaning and zero point of the timestamp depends on the source providing images to * the SurfaceTexture. Unless otherwise specified by the image source, timestamps cannot * generally be compared across SurfaceTexture instances, or across multiple program * invocations. It is mostly useful for determining time offsets between subsequent frames. */ + public long getTimestamp() { return nativeGetTimestamp(); } diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index c82fb9b77ed4..e36360c167a8 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -139,7 +139,7 @@ public: // setFrameAvailableListener sets the listener object that will be notified // when a new frame becomes available. - void setFrameAvailableListener(const sp<FrameAvailableListener>& l); + void setFrameAvailableListener(const sp<FrameAvailableListener>& listener); // getAllocator retrieves the binder object that must be referenced as long // as the GraphicBuffers dequeued from this SurfaceTexture are referenced. @@ -343,7 +343,7 @@ private: uint32_t mNextTransform; // mTexName is the name of the OpenGL texture to which streamed images will - // be bound when updateTexImage is called. It is set at construction time + // be bound when updateTexImage is called. It is set at construction time // changed with a call to setTexName. const GLuint mTexName; diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 18e8a5f39254..4328d3cc6d74 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -103,6 +103,10 @@ public: virtual status_t initCheck() = 0; virtual bool hardwareOutput() = 0; + virtual status_t setUID(uid_t uid) { + return INVALID_OPERATION; + } + virtual status_t setDataSource( const char *url, const KeyedVector<String8, String8> *headers = NULL) = 0; diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h index a31395ed9509..37dbcd8eae84 100644 --- a/include/media/stagefright/MediaSource.h +++ b/include/media/stagefright/MediaSource.h @@ -22,6 +22,7 @@ #include <media/stagefright/MediaErrors.h> #include <utils/RefBase.h> +#include <utils/Vector.h> namespace android { @@ -99,6 +100,15 @@ struct MediaSource : public RefBase { return ERROR_UNSUPPORTED; } + // The consumer of this media source requests that the given buffers + // are to be returned exclusively in response to read calls. + // This will be called after a successful start() and before the + // first read() call. + // Callee assumes ownership of the buffers if no error is returned. + virtual status_t setBuffers(const Vector<MediaBuffer *> &buffers) { + return ERROR_UNSUPPORTED; + } + protected: virtual ~MediaSource(); diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 99b72ad86415..57f678c75512 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -121,6 +121,8 @@ enum { // To store the timed text format data kKeyTextFormatData = 'text', // raw data + + kKeyRequiresSecureBuffers = 'secu', // bool (int32_t) }; enum { diff --git a/include/media/stagefright/MetadataBufferType.h b/include/media/stagefright/MetadataBufferType.h new file mode 100644 index 000000000000..275c19f679d5 --- /dev/null +++ b/include/media/stagefright/MetadataBufferType.h @@ -0,0 +1,77 @@ +/* + * 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 METADATA_BUFFER_TYPE_H +#define METADATA_BUFFER_TYPE_H + +namespace android { +/* + * MetadataBufferType defines the type of the metadata buffers that + * can be passed to video encoder component for encoding, via Stagefright + * media recording framework. To see how to work with the metadata buffers + * in media recording framework, please consult HardwareAPI.h + * + * The creator of metadata buffers and video encoder share common knowledge + * on what is actually being stored in these metadata buffers, and + * how the information can be used by the video encoder component + * to locate the actual pixel data as the source input for video + * encoder, plus whatever other information that is necessary. Stagefright + * media recording framework does not need to know anything specific about the + * metadata buffers, except for receving each individual metadata buffer + * as the source input, making a copy of the metadata buffer, and passing the + * copy via OpenMAX API to the video encoder component. + * + * The creator of the metadata buffers must ensure that the first + * 4 bytes in every metadata buffer indicates its buffer type, + * and the rest of the metadata buffer contains the + * actual metadata information. When a video encoder component receives + * a metadata buffer, it uses the first 4 bytes in that buffer to find + * out the type of the metadata buffer, and takes action appropriate + * to that type of metadata buffers (for instance, locate the actual + * pixel data input and then encoding the input data to produce a + * compressed output buffer). + * + * The following shows the layout of a metadata buffer, + * where buffer type is a 4-byte field of MetadataBufferType, + * and the payload is the metadata information. + * + * -------------------------------------------------------------- + * | buffer type | payload | + * -------------------------------------------------------------- + * + */ +typedef enum { + + /* + * kMetadataBufferTypeCameraSource is used to indicate that + * the source of the metadata buffer is the camera component. + */ + kMetadataBufferTypeCameraSource = 0, + + /* + * kMetadataBufferTypeGrallocSource is used to indicate that + * the payload of the metadata buffers can be interpreted as + * a buffer_handle_t. + */ + kMetadataBufferTypeGrallocSource = 1, + + // Add more here... + +} MetadataBufferType; + +} // namespace android + +#endif // METADATA_BUFFER_TYPE_H diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 92331a16f96a..7f3c497ecb32 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -53,6 +53,9 @@ struct OMXCodec : public MediaSource, // Enable GRALLOC_USAGE_PROTECTED for output buffers from native window kEnableGrallocUsageProtected = 128, + + // Secure decoding mode + kUseSecureInputBuffers = 256, }; static sp<MediaSource> Create( const sp<IOMX> &omx, @@ -164,6 +167,10 @@ private: bool mOMXLivesLocally; IOMX::node_id mNode; uint32_t mQuirks; + + // Flags specified in the creation of the codec. + uint32_t mFlags; + bool mIsEncoder; char *mMIME; char *mComponentName; @@ -205,15 +212,12 @@ private: List<size_t> mFilledBuffers; Condition mBufferFilled; - bool mIsMetaDataStoredInVideoBuffers; - bool mOnlySubmitOneBufferAtOneTime; - bool mEnableGrallocUsageProtected; - // Used to record the decoding time for an output picture from // a video encoder. List<int64_t> mDecodingTimeList; - OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks, + OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, + uint32_t quirks, uint32_t flags, bool isEncoder, const char *mime, const char *componentName, const sp<MediaSource> &source, const sp<ANativeWindow> &nativeWindow); @@ -287,6 +291,10 @@ private: void drainInputBuffers(); void fillOutputBuffers(); + bool drainAnyInputBuffer(); + BufferInfo *findInputBufferByDataPointer(void *ptr); + BufferInfo *findEmptyInputBuffer(); + // Returns true iff a flush was initiated and a completion event is // upcoming, false otherwise (A flush was not necessary as we own all // the buffers on that port). @@ -313,7 +321,7 @@ private: void dumpPortStatus(OMX_U32 portIndex); - status_t configureCodec(const sp<MetaData> &meta, uint32_t flags); + status_t configureCodec(const sp<MetaData> &meta); static uint32_t getComponentQuirks( const char *componentName, bool isEncoder); diff --git a/include/private/surfaceflinger/LayerState.h b/include/private/surfaceflinger/LayerState.h index d7fe572e954e..d2fed418f0d3 100644 --- a/include/private/surfaceflinger/LayerState.h +++ b/include/private/surfaceflinger/LayerState.h @@ -29,6 +29,7 @@ namespace android { class Parcel; +class ISurfaceComposerClient; struct layer_state_t { @@ -68,6 +69,13 @@ struct layer_state_t { Region transparentRegion; }; +struct ComposerState { + sp<ISurfaceComposerClient> client; + layer_state_t state; + status_t write(Parcel& output) const; + status_t read(const Parcel& input); +}; + }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index 03fd01b31850..dba98a32c325 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -34,6 +34,7 @@ namespace android { // ---------------------------------------------------------------------------- class IMemoryHeap; +class ComposerState; class ISurfaceComposer : public IInterface { @@ -105,8 +106,7 @@ public: virtual sp<IMemoryHeap> getCblk() const = 0; /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ - virtual void openGlobalTransaction() = 0; - virtual void closeGlobalTransaction() = 0; + virtual void setTransactionState(const Vector<ComposerState>& state) = 0; /* [un]freeze display. requires ACCESS_SURFACE_FLINGER permission */ virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags) = 0; @@ -149,8 +149,7 @@ public: CREATE_CONNECTION, CREATE_GRAPHIC_BUFFER_ALLOC, GET_CBLK, - OPEN_GLOBAL_TRANSACTION, - CLOSE_GLOBAL_TRANSACTION, + SET_TRANSACTION_STATE, SET_ORIENTATION, FREEZE_DISPLAY, UNFREEZE_DISPLAY, diff --git a/include/surfaceflinger/ISurfaceComposerClient.h b/include/surfaceflinger/ISurfaceComposerClient.h index 2e75a0e4cfd9..6e9a654642c5 100644 --- a/include/surfaceflinger/ISurfaceComposerClient.h +++ b/include/surfaceflinger/ISurfaceComposerClient.h @@ -37,8 +37,6 @@ typedef int32_t DisplayID; // ---------------------------------------------------------------------------- -class layer_state_t; - class ISurfaceComposerClient : public IInterface { public: @@ -69,11 +67,6 @@ public: * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t destroySurface(SurfaceID sid) = 0; - - /* - * Requires ACCESS_SURFACE_FLINGER permission - */ - virtual status_t setState(int32_t count, const layer_state_t* states) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h index 140b9f860bfd..7fbbfb24f03a 100644 --- a/include/surfaceflinger/SurfaceComposerClient.h +++ b/include/surfaceflinger/SurfaceComposerClient.h @@ -37,10 +37,12 @@ namespace android { // --------------------------------------------------------------------------- class DisplayInfo; +class Composer; class IMemoryHeap; class ISurfaceComposer; class Region; class surface_flinger_cblk_t; +struct layer_state_t; // --------------------------------------------------------------------------- @@ -59,8 +61,11 @@ public: // --------------------------------------------------------------------------- +class Composer; + class SurfaceComposerClient : public RefBase { + friend class Composer; public: SurfaceComposerClient(); virtual ~SurfaceComposerClient(); @@ -101,13 +106,7 @@ public: // All composer parameters must be changed within a transaction // several surfaces can be updated in one transaction, all changes are // committed at once when the transaction is closed. - // CloseTransaction() usually requires an IPC with the server. - - //! Open a composer transaction - status_t openTransaction(); - - //! commit the transaction - status_t closeTransaction(); + // closeGlobalTransaction() usually requires an IPC with the server. //! Open a composer transaction on all active SurfaceComposerClients. static void openGlobalTransaction(); @@ -152,19 +151,12 @@ public: private: virtual void onFirstRef(); - inline layer_state_t* get_state_l(SurfaceID id); - layer_state_t* lockLayerState(SurfaceID id); - inline void unlockLayerState(); - - mutable Mutex mLock; - SortedVector<layer_state_t> mStates; - int32_t mTransactionOpen; - layer_state_t* mPrebuiltLayerState; + Composer& getComposer(); - // these don't need to be protected because they never change - // after assignment + mutable Mutex mLock; status_t mStatus; sp<ISurfaceComposerClient> mClient; + Composer& mComposer; }; // --------------------------------------------------------------------------- diff --git a/include/utils/LinearTransform.h b/include/utils/LinearTransform.h new file mode 100644 index 000000000000..04cb355c7ff7 --- /dev/null +++ b/include/utils/LinearTransform.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBS_UTILS_LINEAR_TRANSFORM_H +#define _LIBS_UTILS_LINEAR_TRANSFORM_H + +#include <stdint.h> + +namespace android { + +// LinearTransform defines a structure which hold the definition of a +// transformation from single dimensional coordinate system A into coordinate +// system B (and back again). Values in A and in B are 64 bit, the linear +// scale factor is expressed as a rational number using two 32 bit values. +// +// Specifically, let +// f(a) = b +// F(b) = f^-1(b) = a +// then +// +// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero; +// +// and +// +// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero; +// +struct LinearTransform { + int64_t a_zero; + int64_t b_zero; + int32_t a_to_b_numer; + uint32_t a_to_b_denom; + + // Transform from A->B + // Returns true on success, or false in the case of a singularity or an + // overflow. + bool doForwardTransform(int64_t a_in, int64_t* b_out) const; + + // Transform from B->A + // Returns true on success, or false in the case of a singularity or an + // overflow. + bool doReverseTransform(int64_t b_in, int64_t* a_out) const; + + // Helpers which will reduce the fraction N/D using Euclid's method. + template <class T> static void reduce(T* N, T* D); + static void reduce(int32_t* N, uint32_t* D); +}; + + +} + +#endif // _LIBS_UTILS_LINEAR_TRANSFORM_H diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h index 8beec573205e..0e98aeb05fd8 100644 --- a/include/utils/SortedVector.h +++ b/include/utils/SortedVector.h @@ -32,6 +32,8 @@ namespace android { template <class TYPE> class SortedVector : private SortedVectorImpl { + friend class Vector<TYPE>; + public: typedef TYPE value_type; diff --git a/include/utils/Vector.h b/include/utils/Vector.h index f1e87e60907e..b908e2ab2e2c 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -29,6 +29,9 @@ namespace android { +template <typename TYPE> +class SortedVector; + /*! * The main templated vector class ensuring type safety * while making use of VectorImpl. @@ -47,13 +50,17 @@ public: Vector(); Vector(const Vector<TYPE>& rhs); + explicit Vector(const SortedVector<TYPE>& rhs); virtual ~Vector(); /*! copy operator */ const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const; Vector<TYPE>& operator = (const Vector<TYPE>& rhs); - /* + const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const; + Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs); + + /* * empty the vector */ @@ -215,6 +222,11 @@ Vector<TYPE>::Vector(const Vector<TYPE>& rhs) } template<class TYPE> inline +Vector<TYPE>::Vector(const SortedVector<TYPE>& rhs) + : VectorImpl(static_cast<const VectorImpl&>(rhs)) { +} + +template<class TYPE> inline Vector<TYPE>::~Vector() { finish_vector(); } @@ -227,6 +239,18 @@ Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) { template<class TYPE> inline const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const { + VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)); + return *this; +} + +template<class TYPE> inline +Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) { + VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)); + return *this; +} + +template<class TYPE> inline +const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const { VectorImpl::operator = (rhs); return *this; } diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index ab4b9e04d671..f75208dfd0aa 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -60,16 +60,10 @@ public class Credentials { public static final String WIFI = "WIFI_"; /** Data type for public keys. */ - public static final String PUBLIC_KEY = "KEY"; + public static final String EXTRA_PUBLIC_KEY = "KEY"; /** Data type for private keys. */ - public static final String PRIVATE_KEY = "PKEY"; - - /** Data type for certificates. */ - public static final String CERTIFICATE = "CERT"; - - /** Data type for PKCS12. */ - public static final String PKCS12 = "PKCS12"; + public static final String EXTRA_PRIVATE_KEY = "PKEY"; // historically used by Android public static final String EXTENSION_CRT = ".crt"; @@ -130,16 +124,9 @@ public class Credentials { } } - private Intent createInstallIntent() { - Intent intent = new Intent(INSTALL_ACTION); - intent.setClassName("com.android.certinstaller", - "com.android.certinstaller.CertInstallerMain"); - return intent; - } - public void install(Context context) { try { - Intent intent = createInstallIntent(); + Intent intent = KeyChain.createInstallIntent(); context.startActivity(intent); } catch (ActivityNotFoundException e) { Log.w(LOGTAG, e.toString()); @@ -148,9 +135,9 @@ public class Credentials { public void install(Context context, KeyPair pair) { try { - Intent intent = createInstallIntent(); - intent.putExtra(PRIVATE_KEY, pair.getPrivate().getEncoded()); - intent.putExtra(PUBLIC_KEY, pair.getPublic().getEncoded()); + Intent intent = KeyChain.createInstallIntent(); + intent.putExtra(EXTRA_PRIVATE_KEY, pair.getPrivate().getEncoded()); + intent.putExtra(EXTRA_PUBLIC_KEY, pair.getPublic().getEncoded()); context.startActivity(intent); } catch (ActivityNotFoundException e) { Log.w(LOGTAG, e.toString()); @@ -159,7 +146,7 @@ public class Credentials { public void install(Context context, String type, byte[] value) { try { - Intent intent = createInstallIntent(); + Intent intent = KeyChain.createInstallIntent(); intent.putExtra(type, value); context.startActivity(intent); } catch (ActivityNotFoundException e) { diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 18011e618f49..e91bcab55401 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -45,8 +45,12 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import libcore.util.Objects; +import org.apache.harmony.xnet.provider.jsse.TrustedCertificateStore; /** * The {@code KeyChain} class provides access to private keys and @@ -89,31 +93,117 @@ public final class KeyChain { public static final String ACCOUNT_TYPE = "com.android.keychain"; /** + * Action to bring up the KeyChainActivity + */ + private static final String ACTION_CHOOSER = "com.android.keychain.CHOOSER"; + + /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_RESPONSE = "response"; /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_HOST = "host"; /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_PORT = "port"; /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_ALIAS = "alias"; /** + * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ public static final String EXTRA_SENDER = "sender"; /** + * Action to bring up the CertInstaller + */ + private static final String ACTION_INSTALL = "android.credentials.INSTALL"; + + /** + * Optional extra to specify a {@code String} credential name on + * the {@code Intent} returned by {@link #createInstallIntent}. + * + * @hide TODO make public + */ + // Compatible with old com.android.certinstaller.CredentialHelper.CERT_NAME_KEY + public static final String EXTRA_NAME = "name"; + + /** + * Optional extra to specify an X.509 certificate to install on + * the {@code Intent} returned by {@link #createInstallIntent}. + * The extra value should be a PEM or ASN.1 DER encoded {@code + * byte[]}. An {@link X509Certificate} can be converted to DER + * encoded bytes with {@link X509Certificate#getEncoded}. + * + * <p>{@link #EXTRA_NAME} may be used to provide a default alias + * name for the installed certificate. + * + * @hide TODO make public + */ + // Compatible with old android.security.Credentials.CERTIFICATE + public static final String EXTRA_CERTIFICATE = "CERT"; + + /** + * Optional extra for use with the {@code Intent} returned by + * {@link #createInstallIntent} to specify a PKCS#12 key store to + * install. The extra value should be a {@code byte[]}. The bytes + * may come from an external source or be generated with {@link + * KeyStore#store} on a "PKCS12" instance. + * + * <p>The user will be prompted for the password to load the key store. + * + * <p>The key store will be scanned for {@link + * java.security.KeyStore.PrivateKeyEntry} entries and both the + * private key and associated certificate chain will be installed. + * + * <p>{@link #EXTRA_NAME} may be used to provide a default alias + * name for the installed credentials. + * + * @hide TODO make public + */ + // Compatible with old android.security.Credentials.PKCS12 + public static final String EXTRA_PKCS12 = "PKCS12"; + + /** + * Returns an {@code Intent} that can be used for credential + * installation. The intent may be used without any extras, in + * which case the user will be able to install credentials from + * their own source. + * + * <p>Alternatively, {@link #EXTRA_CERTIFICATE} or {@link + * #EXTRA_PKCS12} maybe used to specify the bytes of an X.509 + * certificate or a PKCS#12 key store for installation. These + * extras may be combined with {@link EXTRA_NAME} to provide a + * default alias name for credentials being installed. + * + * <p>When used with {@link Activity#startActivityForResult}, + * {@link Activity#RESULT_OK} will be returned if a credential was + * successfully installed, otherwise {@link + * Activity#RESULT_CANCELED} will be returned. + * + * @hide TODO make public with createInstallIntent, EXTRA_NAME, EXTRA_CERTIFICATE, EXTRA_PKCS12 + */ + public static Intent createInstallIntent() { + Intent intent = new Intent(ACTION_INSTALL); + intent.setClassName("com.android.certinstaller", + "com.android.certinstaller.CertInstallerMain"); + return intent; + } + + /** * Launches an {@code Activity} for the user to select the alias * for a private key and certificate pair for authentication. The * selected alias or null will be returned via the @@ -176,7 +266,7 @@ public final class KeyChain { if (response == null) { throw new NullPointerException("response == null"); } - Intent intent = new Intent("com.android.keychain.CHOOSER"); + Intent intent = new Intent(ACTION_CHOOSER); intent.putExtra(EXTRA_RESPONSE, new AliasResponse(activity, response)); intent.putExtra(EXTRA_HOST, host); intent.putExtra(EXTRA_PORT, port); @@ -299,7 +389,21 @@ public final class KeyChain { } IKeyChainService keyChainService = keyChainConnection.getService(); byte[] certificateBytes = keyChainService.getCertificate(alias, authToken); - return new X509Certificate[] { toCertificate(certificateBytes) }; + List<X509Certificate> chain = new ArrayList<X509Certificate>(); + chain.add(toCertificate(certificateBytes)); + TrustedCertificateStore store = new TrustedCertificateStore(); + for (int i = 0; true; i++) { + X509Certificate cert = chain.get(i); + if (Objects.equal(cert.getSubjectX500Principal(), cert.getIssuerX500Principal())) { + break; + } + X509Certificate issuer = store.findIssuer(cert); + if (issuer == null) { + break; + } + chain.add(issuer); + } + return chain.toArray(new X509Certificate[chain.size()]); } catch (RemoteException e) { throw new KeyChainException(e); } catch (RuntimeException e) { diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 40450a3d0750..c1156d5c86c5 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -25,6 +25,8 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <private/surfaceflinger/LayerState.h> + #include <surfaceflinger/ISurfaceComposer.h> #include <ui/DisplayInfo.h> @@ -74,18 +76,17 @@ public: return interface_cast<IMemoryHeap>(reply.readStrongBinder()); } - virtual void openGlobalTransaction() + virtual void setTransactionState(const Vector<ComposerState>& state) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::OPEN_GLOBAL_TRANSACTION, data, &reply); - } - - virtual void closeGlobalTransaction() - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CLOSE_GLOBAL_TRANSACTION, data, &reply); + Vector<ComposerState>::const_iterator b(state.begin()); + Vector<ComposerState>::const_iterator e(state.end()); + data.writeInt32(state.size()); + for ( ; b != e ; ++b ) { + b->write(data); + } + remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); } virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags) @@ -218,13 +219,17 @@ status_t BnSurfaceComposer::onTransact( sp<IBinder> b = createGraphicBufferAlloc()->asBinder(); reply->writeStrongBinder(b); } break; - case OPEN_GLOBAL_TRANSACTION: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - openGlobalTransaction(); - } break; - case CLOSE_GLOBAL_TRANSACTION: { + case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - closeGlobalTransaction(); + size_t count = data.readInt32(); + ComposerState s; + Vector<ComposerState> state; + state.setCapacity(count); + for (size_t i=0 ; i<count ; i++) { + s.read(data); + state.add(s); + } + setTransactionState(state); } break; case SET_ORIENTATION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index 8d8339258860..bc97cacbe0a3 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -51,8 +51,7 @@ namespace android { enum { CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, - DESTROY_SURFACE, - SET_STATE + DESTROY_SURFACE }; class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient> @@ -92,17 +91,6 @@ public: remote()->transact(DESTROY_SURFACE, data, &reply); return reply.readInt32(); } - - virtual status_t setState(int32_t count, const layer_state_t* states) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - data.writeInt32(count); - for (int i=0 ; i<count ; i++) - states[i].write(data); - remote()->transact(SET_STATE, data, &reply); - return reply.readInt32(); - } }; IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient"); @@ -133,17 +121,6 @@ status_t BnSurfaceComposerClient::onTransact( reply->writeInt32( destroySurface( data.readInt32() ) ); return NO_ERROR; } break; - case SET_STATE: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - int32_t count = data.readInt32(); - layer_state_t* states = new layer_state_t[count]; - for (int i=0 ; i<count ; i++) - states[i].read(data); - status_t err = setState(count, states); - delete [] states; - reply->writeInt32(err); - return NO_ERROR; - } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 01c4c7ebfe84..87901e87f706 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -17,6 +17,7 @@ #include <utils/Errors.h> #include <binder/Parcel.h> #include <private/surfaceflinger/LayerState.h> +#include <surfaceflinger/ISurfaceComposerClient.h> namespace android { @@ -58,4 +59,14 @@ status_t layer_state_t::read(const Parcel& input) return NO_ERROR; } +status_t ComposerState::write(Parcel& output) const { + output.writeStrongBinder(client->asBinder()); + return state.write(output); +} + +status_t ComposerState::read(const Parcel& input) { + client = interface_cast<ISurfaceComposerClient>(input.readStrongBinder()); + return state.read(input); +} + }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 1678711aaf42..8cead808a1b2 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -74,75 +74,52 @@ static inline surface_flinger_cblk_t const volatile * get_cblk() { // --------------------------------------------------------------------------- +// NOTE: this is NOT a member function (it's a friend defined with its +// declaration). +static inline +int compare_type( const ComposerState& lhs, const ComposerState& rhs) { + if (lhs.client < rhs.client) return -1; + if (lhs.client > rhs.client) return 1; + if (lhs.state.surface < rhs.state.surface) return -1; + if (lhs.state.surface > rhs.state.surface) return 1; + return 0; +} + class Composer : public Singleton<Composer> { - Mutex mLock; - SortedVector< wp<SurfaceComposerClient> > mActiveConnections; - SortedVector<sp<SurfaceComposerClient> > mOpenTransactions; + friend class Singleton<Composer>; - Composer() : Singleton<Composer>() { - } + mutable Mutex mLock; + SortedVector<ComposerState> mStates; - void addClientImpl(const sp<SurfaceComposerClient>& client) { - Mutex::Autolock _l(mLock); - mActiveConnections.add(client); - } + Composer() : Singleton<Composer>() { } - void removeClientImpl(const sp<SurfaceComposerClient>& client) { - Mutex::Autolock _l(mLock); - mActiveConnections.remove(client); - } + void closeGlobalTransactionImpl(); - void openGlobalTransactionImpl() - { - Mutex::Autolock _l(mLock); - if (mOpenTransactions.size()) { - LOGE("openGlobalTransaction() called more than once. skipping."); - return; - } - const size_t N = mActiveConnections.size(); - for (size_t i=0; i<N; i++) { - sp<SurfaceComposerClient> client(mActiveConnections[i].promote()); - if (client != 0 && mOpenTransactions.indexOf(client) < 0) { - if (client->openTransaction() == NO_ERROR) { - mOpenTransactions.add(client); - } else { - LOGE("openTransaction on client %p failed", client.get()); - // let it go, it'll fail later when the user - // tries to do something with the transaction - } - } - } - } + layer_state_t* getLayerStateLocked( + const sp<SurfaceComposerClient>& client, SurfaceID id); - void closeGlobalTransactionImpl() - { - mLock.lock(); - SortedVector< sp<SurfaceComposerClient> > clients(mOpenTransactions); - mOpenTransactions.clear(); - mLock.unlock(); - - sp<ISurfaceComposer> sm(getComposerService()); - sm->openGlobalTransaction(); - const size_t N = clients.size(); - for (size_t i=0; i<N; i++) { - clients[i]->closeTransaction(); - } - sm->closeGlobalTransaction(); - } +public: - friend class Singleton<Composer>; + status_t setPosition(const sp<SurfaceComposerClient>& client, SurfaceID id, + int32_t x, int32_t y); + status_t setSize(const sp<SurfaceComposerClient>& client, SurfaceID id, + uint32_t w, uint32_t h); + status_t setLayer(const sp<SurfaceComposerClient>& client, SurfaceID id, + int32_t z); + status_t setFlags(const sp<SurfaceComposerClient>& client, SurfaceID id, + uint32_t flags, uint32_t mask); + status_t setTransparentRegionHint( + const sp<SurfaceComposerClient>& client, SurfaceID id, + const Region& transparentRegion); + status_t setAlpha(const sp<SurfaceComposerClient>& client, SurfaceID id, + float alpha); + status_t setMatrix(const sp<SurfaceComposerClient>& client, SurfaceID id, + float dsdx, float dtdx, float dsdy, float dtdy); + status_t setFreezeTint( + const sp<SurfaceComposerClient>& client, SurfaceID id, + uint32_t tint); -public: - static void addClient(const sp<SurfaceComposerClient>& client) { - Composer::getInstance().addClientImpl(client); - } - static void removeClient(const sp<SurfaceComposerClient>& client) { - Composer::getInstance().removeClientImpl(client); - } - static void openGlobalTransaction() { - Composer::getInstance().openGlobalTransactionImpl(); - } static void closeGlobalTransaction() { Composer::getInstance().closeGlobalTransactionImpl(); } @@ -152,127 +129,185 @@ ANDROID_SINGLETON_STATIC_INSTANCE(Composer); // --------------------------------------------------------------------------- -static inline int compare_type( const layer_state_t& lhs, - const layer_state_t& rhs) { - if (lhs.surface < rhs.surface) return -1; - if (lhs.surface > rhs.surface) return 1; - return 0; +void Composer::closeGlobalTransactionImpl() { + sp<ISurfaceComposer> sm(getComposerService()); + + Vector<ComposerState> transaction; + + { // scope for the lock + Mutex::Autolock _l(mLock); + transaction = mStates; + mStates.clear(); + } + + sm->setTransactionState(transaction); +} + +layer_state_t* Composer::getLayerStateLocked( + const sp<SurfaceComposerClient>& client, SurfaceID id) { + + ComposerState s; + s.client = client->mClient; + s.state.surface = id; + + ssize_t index = mStates.indexOf(s); + if (index < 0) { + // we don't have it, add an initialized layer_state to our list + index = mStates.add(s); + } + + ComposerState* const out = mStates.editArray(); + return &(out[index].state); +} + +status_t Composer::setPosition(const sp<SurfaceComposerClient>& client, + SurfaceID id, int32_t x, int32_t y) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::ePositionChanged; + s->x = x; + s->y = y; + return NO_ERROR; +} + +status_t Composer::setSize(const sp<SurfaceComposerClient>& client, + SurfaceID id, uint32_t w, uint32_t h) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eSizeChanged; + s->w = w; + s->h = h; + return NO_ERROR; +} + +status_t Composer::setLayer(const sp<SurfaceComposerClient>& client, + SurfaceID id, int32_t z) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eLayerChanged; + s->z = z; + return NO_ERROR; +} + +status_t Composer::setFlags(const sp<SurfaceComposerClient>& client, + SurfaceID id, uint32_t flags, + uint32_t mask) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eVisibilityChanged; + s->flags &= ~mask; + s->flags |= (flags & mask); + s->mask |= mask; + return NO_ERROR; +} + +status_t Composer::setTransparentRegionHint( + const sp<SurfaceComposerClient>& client, SurfaceID id, + const Region& transparentRegion) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eTransparentRegionChanged; + s->transparentRegion = transparentRegion; + return NO_ERROR; +} + +status_t Composer::setAlpha(const sp<SurfaceComposerClient>& client, + SurfaceID id, float alpha) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eAlphaChanged; + s->alpha = alpha; + return NO_ERROR; +} + +status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client, + SurfaceID id, float dsdx, float dtdx, + float dsdy, float dtdy) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eMatrixChanged; + layer_state_t::matrix22_t matrix; + matrix.dsdx = dsdx; + matrix.dtdx = dtdx; + matrix.dsdy = dsdy; + matrix.dtdy = dtdy; + s->matrix = matrix; + return NO_ERROR; } +status_t Composer::setFreezeTint(const sp<SurfaceComposerClient>& client, + SurfaceID id, uint32_t tint) { + Mutex::Autolock _l(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) + return BAD_INDEX; + s->what |= ISurfaceComposer::eFreezeTintChanged; + s->tint = tint; + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + SurfaceComposerClient::SurfaceComposerClient() - : mTransactionOpen(0), mPrebuiltLayerState(0), mStatus(NO_INIT) + : mStatus(NO_INIT), mComposer(Composer::getInstance()) { } -void SurfaceComposerClient::onFirstRef() -{ +void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sm(getComposerService()); if (sm != 0) { sp<ISurfaceComposerClient> conn = sm->createConnection(); if (conn != 0) { mClient = conn; - Composer::addClient(this); - mPrebuiltLayerState = new layer_state_t; mStatus = NO_ERROR; } } } -SurfaceComposerClient::~SurfaceComposerClient() -{ - delete mPrebuiltLayerState; +SurfaceComposerClient::~SurfaceComposerClient() { dispose(); } -status_t SurfaceComposerClient::initCheck() const -{ +status_t SurfaceComposerClient::initCheck() const { return mStatus; } -sp<IBinder> SurfaceComposerClient::connection() const -{ +sp<IBinder> SurfaceComposerClient::connection() const { return (mClient != 0) ? mClient->asBinder() : 0; } status_t SurfaceComposerClient::linkToComposerDeath( const sp<IBinder::DeathRecipient>& recipient, - void* cookie, uint32_t flags) -{ + void* cookie, uint32_t flags) { sp<ISurfaceComposer> sm(getComposerService()); return sm->asBinder()->linkToDeath(recipient, cookie, flags); } -void SurfaceComposerClient::dispose() -{ +void SurfaceComposerClient::dispose() { // this can be called more than once. sp<ISurfaceComposerClient> client; Mutex::Autolock _lm(mLock); if (mClient != 0) { - Composer::removeClient(this); client = mClient; // hold ref while lock is held mClient.clear(); } mStatus = NO_INIT; } -status_t SurfaceComposerClient::getDisplayInfo( - DisplayID dpy, DisplayInfo* info) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - - info->w = dcblk->w; - info->h = dcblk->h; - info->orientation = dcblk->orientation; - info->xdpi = dcblk->xdpi; - info->ydpi = dcblk->ydpi; - info->fps = dcblk->fps; - info->density = dcblk->density; - return getPixelFormatInfo(dcblk->format, &(info->pixelFormatInfo)); -} - -ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - return dcblk->w; -} - -ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - return dcblk->h; -} - -ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) -{ - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) - return BAD_VALUE; - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - volatile display_cblk_t const * dcblk = cblk->displays + dpy; - return dcblk->orientation; -} - -ssize_t SurfaceComposerClient::getNumberOfDisplays() -{ - volatile surface_flinger_cblk_t const * cblk = get_cblk(); - uint32_t connected = cblk->connected; - int n = 0; - while (connected) { - if (connected&1) n++; - connected >>= 1; - } - return n; -} - sp<SurfaceControl> SurfaceComposerClient::createSurface( DisplayID display, uint32_t w, @@ -310,237 +345,167 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface( return result; } -status_t SurfaceComposerClient::destroySurface(SurfaceID sid) -{ +status_t SurfaceComposerClient::destroySurface(SurfaceID sid) { if (mStatus != NO_ERROR) return mStatus; - - // it's okay to destroy a surface while a transaction is open, - // (transactions really are a client-side concept) - // however, this indicates probably a misuse of the API or a bug - // in the client code. - LOGW_IF(mTransactionOpen, - "Destroying surface while a transaction is open. " - "Client %p: destroying surface %d, mTransactionOpen=%d", - this, sid, mTransactionOpen); - status_t err = mClient->destroySurface(sid); return err; } -void SurfaceComposerClient::openGlobalTransaction() -{ - Composer::openGlobalTransaction(); +inline Composer& SurfaceComposerClient::getComposer() { + return mComposer; } -void SurfaceComposerClient::closeGlobalTransaction() -{ - Composer::closeGlobalTransaction(); -} +// ---------------------------------------------------------------------------- -status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) -{ - sp<ISurfaceComposer> sm(getComposerService()); - return sm->freezeDisplay(dpy, flags); +void SurfaceComposerClient::openGlobalTransaction() { + // Currently a no-op } -status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags) -{ - sp<ISurfaceComposer> sm(getComposerService()); - return sm->unfreezeDisplay(dpy, flags); +void SurfaceComposerClient::closeGlobalTransaction() { + Composer::closeGlobalTransaction(); } -int SurfaceComposerClient::setOrientation(DisplayID dpy, - int orientation, uint32_t flags) -{ - sp<ISurfaceComposer> sm(getComposerService()); - return sm->setOrientation(dpy, orientation, flags); -} +// ---------------------------------------------------------------------------- -status_t SurfaceComposerClient::openTransaction() -{ - if (mStatus != NO_ERROR) - return mStatus; - Mutex::Autolock _l(mLock); - mTransactionOpen++; - return NO_ERROR; +status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) { + return getComposer().setFreezeTint(this, id, tint); } -status_t SurfaceComposerClient::closeTransaction() -{ - if (mStatus != NO_ERROR) - return mStatus; - - Mutex::Autolock _l(mLock); - if (mTransactionOpen <= 0) { - LOGE( "closeTransaction (client %p, mTransactionOpen=%d) " - "called more times than openTransaction()", - this, mTransactionOpen); - return INVALID_OPERATION; - } +status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) { + return getComposer().setPosition(this, id, x, y); +} - if (mTransactionOpen >= 2) { - mTransactionOpen--; - return NO_ERROR; - } +status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) { + return getComposer().setSize(this, id, w, h); +} - mTransactionOpen = 0; - const ssize_t count = mStates.size(); - if (count) { - mClient->setState(count, mStates.array()); - mStates.clear(); - } - return NO_ERROR; +status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) { + return getComposer().setLayer(this, id, z); } -layer_state_t* SurfaceComposerClient::get_state_l(SurfaceID index) -{ - // API usage error, do nothing. - if (mTransactionOpen<=0) { - LOGE("Not in transaction (client=%p, SurfaceID=%d, mTransactionOpen=%d", - this, int(index), mTransactionOpen); - return 0; - } +status_t SurfaceComposerClient::hide(SurfaceID id) { + return getComposer().setFlags(this, id, + ISurfaceComposer::eLayerHidden, + ISurfaceComposer::eLayerHidden); +} - // use mPrebuiltLayerState just to find out if we already have it - layer_state_t& dummy(*mPrebuiltLayerState); - dummy.surface = index; - ssize_t i = mStates.indexOf(dummy); - if (i < 0) { - // we don't have it, add an initialized layer_state to our list - i = mStates.add(dummy); - } - return mStates.editArray() + i; +status_t SurfaceComposerClient::show(SurfaceID id, int32_t) { + return getComposer().setFlags(this, id, + 0, + ISurfaceComposer::eLayerHidden); } -layer_state_t* SurfaceComposerClient::lockLayerState(SurfaceID id) -{ - layer_state_t* s; - mLock.lock(); - s = get_state_l(id); - if (!s) mLock.unlock(); - return s; +status_t SurfaceComposerClient::freeze(SurfaceID id) { + return getComposer().setFlags(this, id, + ISurfaceComposer::eLayerFrozen, + ISurfaceComposer::eLayerFrozen); } -void SurfaceComposerClient::unlockLayerState() -{ - mLock.unlock(); +status_t SurfaceComposerClient::unfreeze(SurfaceID id) { + return getComposer().setFlags(this, id, + 0, + ISurfaceComposer::eLayerFrozen); } -status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) -{ - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::ePositionChanged; - s->x = x; - s->y = y; - unlockLayerState(); - return NO_ERROR; +status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags, + uint32_t mask) { + return getComposer().setFlags(this, id, flags, mask); } -status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) -{ - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eSizeChanged; - s->w = w; - s->h = h; - unlockLayerState(); - return NO_ERROR; +status_t SurfaceComposerClient::setTransparentRegionHint(SurfaceID id, + const Region& transparentRegion) { + return getComposer().setTransparentRegionHint(this, id, transparentRegion); } -status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) -{ - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eLayerChanged; - s->z = z; - unlockLayerState(); - return NO_ERROR; +status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) { + return getComposer().setAlpha(this, id, alpha); } -status_t SurfaceComposerClient::hide(SurfaceID id) -{ - return setFlags(id, ISurfaceComposer::eLayerHidden, - ISurfaceComposer::eLayerHidden); +status_t SurfaceComposerClient::setMatrix(SurfaceID id, float dsdx, float dtdx, + float dsdy, float dtdy) { + return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy); } -status_t SurfaceComposerClient::show(SurfaceID id, int32_t) +// ---------------------------------------------------------------------------- + +status_t SurfaceComposerClient::getDisplayInfo( + DisplayID dpy, DisplayInfo* info) { - return setFlags(id, 0, ISurfaceComposer::eLayerHidden); + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + + info->w = dcblk->w; + info->h = dcblk->h; + info->orientation = dcblk->orientation; + info->xdpi = dcblk->xdpi; + info->ydpi = dcblk->ydpi; + info->fps = dcblk->fps; + info->density = dcblk->density; + return getPixelFormatInfo(dcblk->format, &(info->pixelFormatInfo)); } -status_t SurfaceComposerClient::freeze(SurfaceID id) +ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) { - return setFlags(id, ISurfaceComposer::eLayerFrozen, - ISurfaceComposer::eLayerFrozen); + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->w; } -status_t SurfaceComposerClient::unfreeze(SurfaceID id) +ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) { - return setFlags(id, 0, ISurfaceComposer::eLayerFrozen); + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->h; } -status_t SurfaceComposerClient::setFlags(SurfaceID id, - uint32_t flags, uint32_t mask) +ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eVisibilityChanged; - s->flags &= ~mask; - s->flags |= (flags & mask); - s->mask |= mask; - unlockLayerState(); - return NO_ERROR; + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->orientation; } -status_t SurfaceComposerClient::setTransparentRegionHint( - SurfaceID id, const Region& transparentRegion) +ssize_t SurfaceComposerClient::getNumberOfDisplays() { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eTransparentRegionChanged; - s->transparentRegion = transparentRegion; - unlockLayerState(); - return NO_ERROR; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + uint32_t connected = cblk->connected; + int n = 0; + while (connected) { + if (connected&1) n++; + connected >>= 1; + } + return n; } -status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) +// ---------------------------------------------------------------------------- + +status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eAlphaChanged; - s->alpha = alpha; - unlockLayerState(); - return NO_ERROR; + sp<ISurfaceComposer> sm(getComposerService()); + return sm->freezeDisplay(dpy, flags); } -status_t SurfaceComposerClient::setMatrix( - SurfaceID id, - float dsdx, float dtdx, - float dsdy, float dtdy ) +status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags) { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eMatrixChanged; - layer_state_t::matrix22_t matrix; - matrix.dsdx = dsdx; - matrix.dtdx = dtdx; - matrix.dsdy = dsdy; - matrix.dtdy = dtdy; - s->matrix = matrix; - unlockLayerState(); - return NO_ERROR; + sp<ISurfaceComposer> sm(getComposerService()); + return sm->unfreezeDisplay(dpy, flags); } -status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) +int SurfaceComposerClient::setOrientation(DisplayID dpy, + int orientation, uint32_t flags) { - layer_state_t* s = lockLayerState(id); - if (!s) return BAD_INDEX; - s->what |= ISurfaceComposer::eFreezeTintChanged; - s->tint = tint; - unlockLayerState(); - return NO_ERROR; + sp<ISurfaceComposer> sm(getComposerService()); + return sm->setOrientation(dpy, orientation, flags); } // ---------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 0925001965dd..3bf6477cf208 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -148,6 +148,11 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) { LOGV("SurfaceTexture::setBufferCount"); Mutex::Autolock lock(mMutex); + if (bufferCount > NUM_BUFFER_SLOTS) { + LOGE("setBufferCount: bufferCount larger than slots available"); + return BAD_VALUE; + } + // Error out if the user has dequeued buffers for (int i=0 ; i<mBufferCount ; i++) { if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { @@ -208,7 +213,7 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { LOGV("SurfaceTexture::dequeueBuffer"); - if ((w && !h) || (!w & h)) { + if ((w && !h) || (!w && h)) { LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); return BAD_VALUE; } @@ -699,10 +704,10 @@ nsecs_t SurfaceTexture::getTimestamp() { } void SurfaceTexture::setFrameAvailableListener( - const sp<FrameAvailableListener>& l) { + const sp<FrameAvailableListener>& listener) { LOGV("SurfaceTexture::setFrameAvailableListener"); Mutex::Autolock lock(mMutex); - mFrameAvailableListener = l; + mFrameAvailableListener = listener; } sp<IBinder> SurfaceTexture::getAllocator() { diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index dfa9211afac5..c06400e5219a 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -84,10 +84,10 @@ protected: ASSERT_TRUE(mSurfaceControl != NULL); ASSERT_TRUE(mSurfaceControl->isValid()); - ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction()); + SurfaceComposerClient::openGlobalTransaction(); ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); - ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction()); + SurfaceComposerClient::closeGlobalTransaction(); sp<ANativeWindow> window = mSurfaceControl->getSurface(); mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 35c86405584b..450cdf1f8f47 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -36,10 +36,10 @@ protected: ASSERT_TRUE(mSurfaceControl != NULL); ASSERT_TRUE(mSurfaceControl->isValid()); - ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction()); + SurfaceComposerClient::openGlobalTransaction(); ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000)); ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); - ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction()); + SurfaceComposerClient::closeGlobalTransaction(); mSurface = mSurfaceControl->getSurface(); ASSERT_TRUE(mSurface != NULL); diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index ffdfe663afe9..c46d6f4724a2 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -443,7 +443,8 @@ status_t InputPublisher::appendMotionSample( if (! mPinned || ! mMotionEventSampleDataTail) { LOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current " - "AMOTION_EVENT_ACTION_MOVE event.", mChannel->getName().string()); + "AMOTION_EVENT_ACTION_MOVE or AMOTION_EVENT_ACTION_HOVER_MOVE event.", + mChannel->getName().string()); return INVALID_OPERATION; } diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 093189c5c05a..774e8c974558 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -27,6 +27,7 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ + LinearTransform.cpp \ ObbFile.cpp \ Pool.cpp \ PropertyMap.cpp \ diff --git a/libs/utils/LinearTransform.cpp b/libs/utils/LinearTransform.cpp new file mode 100644 index 000000000000..d752415cf5f0 --- /dev/null +++ b/libs/utils/LinearTransform.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define __STDC_LIMIT_MACROS + +#include <assert.h> +#include <stdint.h> + +#include <utils/LinearTransform.h> + +namespace android { + +template<class T> static inline T ABS(T x) { return (x < 0) ? -x : x; } + +// Static math methods involving linear transformations +static bool scale_u64_to_u64( + uint64_t val, + uint32_t N, + uint32_t D, + uint64_t* res, + bool round_up_not_down) { + uint64_t tmp1, tmp2; + uint32_t r; + + assert(res); + assert(D); + + // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit + // integer X. + // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit + // integer X. + // Let X[A, B] with A <= B denote bits A through B of the integer X. + // Let (A | B) denote the concatination of two 32 bit ints, A and B. + // IOW X = (A | B) => U32(X) == A && L32(X) == B + // + // compute M = val * N (a 96 bit int) + // --------------------------------- + // tmp2 = U32(val) * N (a 64 bit int) + // tmp1 = L32(val) * N (a 64 bit int) + // which means + // M = val * N = (tmp2 << 32) + tmp1 + tmp2 = (val >> 32) * N; + tmp1 = (val & UINT32_MAX) * N; + + // compute M[32, 95] + // tmp2 = tmp2 + U32(tmp1) + // = (U32(val) * N) + U32(L32(val) * N) + // = M[32, 95] + tmp2 += tmp1 >> 32; + + // if M[64, 95] >= D, then M/D has bits > 63 set and we have + // an overflow. + if ((tmp2 >> 32) >= D) { + *res = UINT64_MAX; + return false; + } + + // Divide. Going in we know + // tmp2 = M[32, 95] + // U32(tmp2) < D + r = tmp2 % D; + tmp2 /= D; + + // At this point + // tmp1 = L32(val) * N + // tmp2 = M[32, 95] / D + // = (M / D)[32, 95] + // r = M[32, 95] % D + // U32(tmp2) = 0 + // + // compute tmp1 = (r | M[0, 31]) + tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32); + + // Divide again. Keep the remainder around in order to round properly. + r = tmp1 % D; + tmp1 /= D; + + // At this point + // tmp2 = (M / D)[32, 95] + // tmp1 = (M / D)[ 0, 31] + // r = M % D + // U32(tmp1) = 0 + // U32(tmp2) = 0 + + // Pack the result and deal with the round-up case (As well as the + // remote possiblility over overflow in such a case). + *res = (tmp2 << 32) | tmp1; + if (r && round_up_not_down) { + ++(*res); + if (!(*res)) { + *res = UINT64_MAX; + return false; + } + } + + return true; +} + +static bool linear_transform_s64_to_s64( + int64_t val, + int64_t basis1, + int32_t N, + uint32_t D, + int64_t basis2, + int64_t* out) { + uint64_t scaled, res; + uint64_t abs_val; + bool is_neg; + + if (!out) + return false; + + // Compute abs(val - basis_64). Keep track of whether or not this delta + // will be negative after the scale opertaion. + if (val < basis1) { + is_neg = true; + abs_val = basis1 - val; + } else { + is_neg = false; + abs_val = val - basis1; + } + + if (N < 0) + is_neg = !is_neg; + + if (!scale_u64_to_u64(abs_val, + ABS(N), + D, + &scaled, + is_neg)) + return false; // overflow/undeflow + + // if scaled is >= 0x8000<etc>, then we are going to overflow or + // underflow unless ABS(basis2) is large enough to pull us back into the + // non-overflow/underflow region. + if (scaled & INT64_MIN) { + if (is_neg && (basis2 < 0)) + return false; // certain underflow + + if (!is_neg && (basis2 >= 0)) + return false; // certain overflow + + if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX)) + return false; // not enough + + // Looks like we are OK + *out = (is_neg ? (-scaled) : scaled) + basis2; + } else { + // Scaled fits within signed bounds, so we just need to check for + // over/underflow for two signed integers. Basically, if both scaled + // and basis2 have the same sign bit, and the result has a different + // sign bit, then we have under/overflow. An easy way to compute this + // is + // (scaled_signbit XNOR basis_signbit) && + // (scaled_signbit XOR res_signbit) + // == + // (scaled_signbit XOR basis_signbit XOR 1) && + // (scaled_signbit XOR res_signbit) + + if (is_neg) + scaled = -scaled; + res = scaled + basis2; + + if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN) + return false; + + *out = res; + } + + return true; +} + +bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const { + if (0 == a_to_b_denom) + return false; + + return linear_transform_s64_to_s64(a_in, + a_zero, + a_to_b_numer, + a_to_b_denom, + b_zero, + b_out); +} + +bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const { + if (0 == a_to_b_numer) + return false; + + return linear_transform_s64_to_s64(b_in, + b_zero, + a_to_b_denom, + a_to_b_numer, + a_zero, + a_out); +} + +template <class T> void LinearTransform::reduce(T* N, T* D) { + T a, b; + if (!N || !D || !(*D)) { + assert(false); + return; + } + + a = *N; + b = *D; + + if (a == 0) { + *D = 1; + return; + } + + // This implements Euclid's method to find GCD. + if (a < b) { + T tmp = a; + a = b; + b = tmp; + } + + while (1) { + // a is now the greater of the two. + const T remainder = a % b; + if (remainder == 0) { + *N /= b; + *D /= b; + return; + } + // by swapping remainder and b, we are guaranteeing that a is + // still the greater of the two upon entrance to the loop. + a = b; + b = remainder; + } +}; + +template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D); +template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D); + +void LinearTransform::reduce(int32_t* N, uint32_t* D) { + if (N && D && *D) { + if (*N < 0) { + *N = -(*N); + reduce(reinterpret_cast<uint32_t*>(N), D); + *N = -(*N); + } else { + reduce(reinterpret_cast<uint32_t*>(N), D); + } + } +} + +} // namespace android diff --git a/location/java/android/location/Country.java b/location/java/android/location/Country.java index 3c05403a6bc2..939bd4af109c 100755 --- a/location/java/android/location/Country.java +++ b/location/java/android/location/Country.java @@ -19,6 +19,8 @@ package android.location; import android.os.Parcel; import android.os.Parcelable; +import java.util.Locale; + /** * This class wraps the country information. * @@ -74,7 +76,7 @@ public class Country implements Parcelable { || source > COUNTRY_SOURCE_LOCALE) { throw new IllegalArgumentException(); } - mCountryIso = countryIso.toLowerCase(); + mCountryIso = countryIso.toUpperCase(Locale.US); mSource = source; } diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java index ffc3346da993..29dec6373b19 100755 --- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java +++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java @@ -205,7 +205,7 @@ public class GpsNetInitiatedHandler { mNiNotification.defaults &= ~Notification.DEFAULT_SOUND; } - mNiNotification.flags = Notification.FLAG_ONGOING_EVENT; + mNiNotification.flags = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_AUTO_CANCEL; mNiNotification.tickerText = getNotifTicker(notif, mContext); // if not to popup dialog immediately, pending intent will open the dialog diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 33312d195656..482b437dd3dc 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -611,15 +611,11 @@ public class MediaPlayer * needed. Not calling this method when playing back a video will * result in only the audio track being played. * - * @param sh the SurfaceHolder to use for video display - */ - /* - * This portion of comment has a non-Javadoc prefix so as not to refer to a - * hidden method. When unhidden, merge it with the previous javadoc comment. - * * Either a surface or surface texture must be set if a display or video sink * is needed. Not calling this method or {@link #setTexture(SurfaceTexture)} * when playing back a video will result in only the audio track being played. + * + * @param sh the SurfaceHolder to use for video display */ public void setDisplay(SurfaceHolder sh) { mSurfaceHolder = sh; @@ -648,7 +644,8 @@ public class MediaPlayer * SurfaceTexture set as the video sink have an unspecified zero point, * and cannot be directly compared between different media sources or different * instances of the same media source, or across multiple runs of the same - * program. + * program. The timestamp is normally monotonically increasing and unaffected + * by time-of-day adjustments, but is reset when the position is set. */ public void setTexture(SurfaceTexture st) { ParcelSurfaceTexture pst = null; diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 6f425966d592..e3cbd5783c8f 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -720,8 +720,9 @@ public class MediaRecorder * * <p>Since API level 13, if applications set a camera via * {@link #setCamera(Camera)}, the apps can use the camera after this method - * call. The apps do not need to lock the camera again. The apps should not - * start another recording session during recording. + * call. The apps do not need to lock the camera again. However, if this + * method fails, the apps should still lock the camera back. The apps should + * not start another recording session during recording. * * @throws IllegalStateException if it is called before * prepare(). diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java index 29c4b89e8517..0d2bcd5376e5 100644 --- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java +++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java @@ -957,12 +957,8 @@ class MediaArtistNativeHelper { public static final int FADE_FROM_BLACK = 8; - public static final int CURTAIN_OPENING = 9; - public static final int FADE_TO_BLACK = 16; - public static final int CURTAIN_CLOSING = 17; - public static final int EXTERNAL = 256; public static final int BLACK_AND_WHITE = 257; diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp index d43a5622a769..277e16c4dc3b 100755 --- a/media/jni/mediaeditor/VideoEditorClasses.cpp +++ b/media/jni/mediaeditor/VideoEditorClasses.cpp @@ -378,9 +378,7 @@ VIDEOEDIT_JAVA_DEFINE_CONSTANTS(VideoEffect) { VIDEOEDIT_JAVA_CONSTANT_INIT("NONE", M4VSS3GPP_kVideoEffectType_None), VIDEOEDIT_JAVA_CONSTANT_INIT("FADE_FROM_BLACK", M4VSS3GPP_kVideoEffectType_FadeFromBlack), - VIDEOEDIT_JAVA_CONSTANT_INIT("CURTAIN_OPENING", M4VSS3GPP_kVideoEffectType_CurtainOpening), VIDEOEDIT_JAVA_CONSTANT_INIT("FADE_TO_BLACK", M4VSS3GPP_kVideoEffectType_FadeToBlack), - VIDEOEDIT_JAVA_CONSTANT_INIT("CURTAIN_CLOSING", M4VSS3GPP_kVideoEffectType_CurtainClosing), VIDEOEDIT_JAVA_CONSTANT_INIT("EXTERNAL", M4VSS3GPP_kVideoEffectType_External), VIDEOEDIT_JAVA_CONSTANT_INIT("BLACK_AND_WHITE", M4xVSS_kVideoEffectType_BlackAndWhite), VIDEOEDIT_JAVA_CONSTANT_INIT("PINK", M4xVSS_kVideoEffectType_Pink), diff --git a/media/jni/mediaeditor/VideoEditorOsal.cpp b/media/jni/mediaeditor/VideoEditorOsal.cpp index 53e7de176ae4..a8c08ac936bd 100755 --- a/media/jni/mediaeditor/VideoEditorOsal.cpp +++ b/media/jni/mediaeditor/VideoEditorOsal.cpp @@ -166,7 +166,6 @@ static const VideoEdit_Osal_Result gkRESULTS[] = VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_NO_SUPPORTED_VIDEO_STREAM_IN_FILE ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_INTERNAL_STATE ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_LUMA_FILTER_ERROR ), - VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_CURTAIN_FILTER_ERROR ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_TRANSITION_FILTER_ERROR ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_AUDIO_DECODER_INIT_FAILED ), VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_AUDIO_DECODED_PCM_SIZE_ISSUE ), diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index a77dff1615d4..1e7c9693bc1a 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -250,7 +250,11 @@ sp<IMediaPlayer> MediaPlayerService::create( const KeyedVector<String8, String8> *headers, int audioSessionId) { int32_t connId = android_atomic_inc(&mNextConnId); - sp<Client> c = new Client(this, pid, connId, client, audioSessionId); + + sp<Client> c = new Client( + this, pid, connId, client, audioSessionId, + IPCThreadState::self()->getCallingUid()); + LOGV("Create new client(%d) from pid %d, url=%s, connId=%d, audioSessionId=%d", connId, pid, url, connId, audioSessionId); if (NO_ERROR != c->setDataSource(url, headers)) @@ -268,7 +272,11 @@ sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClie int fd, int64_t offset, int64_t length, int audioSessionId) { int32_t connId = android_atomic_inc(&mNextConnId); - sp<Client> c = new Client(this, pid, connId, client, audioSessionId); + + sp<Client> c = new Client( + this, pid, connId, client, audioSessionId, + IPCThreadState::self()->getCallingUid()); + LOGV("Create new client(%d) from pid %d, fd=%d, offset=%lld, length=%lld, audioSessionId=%d", connId, pid, fd, offset, length, audioSessionId); if (NO_ERROR != c->setDataSource(fd, offset, length)) { @@ -286,7 +294,10 @@ sp<IMediaPlayer> MediaPlayerService::create( pid_t pid, const sp<IMediaPlayerClient> &client, const sp<IStreamSource> &source, int audioSessionId) { int32_t connId = android_atomic_inc(&mNextConnId); - sp<Client> c = new Client(this, pid, connId, client, audioSessionId); + + sp<Client> c = new Client( + this, pid, connId, client, audioSessionId, + IPCThreadState::self()->getCallingUid()); LOGV("Create new client(%d) from pid %d, audioSessionId=%d", connId, pid, audioSessionId); @@ -496,8 +507,10 @@ void MediaPlayerService::removeClient(wp<Client> client) mClients.remove(client); } -MediaPlayerService::Client::Client(const sp<MediaPlayerService>& service, pid_t pid, - int32_t connId, const sp<IMediaPlayerClient>& client, int audioSessionId) +MediaPlayerService::Client::Client( + const sp<MediaPlayerService>& service, pid_t pid, + int32_t connId, const sp<IMediaPlayerClient>& client, + int audioSessionId, uid_t uid) { LOGV("Client(%d) constructor", connId); mPid = pid; @@ -507,6 +520,7 @@ MediaPlayerService::Client::Client(const sp<MediaPlayerService>& service, pid_t mLoop = false; mStatus = NO_INIT; mAudioSessionId = audioSessionId; + mUID = uid; #if CALLBACK_ANTAGONIZER LOGD("create Antagonizer"); @@ -671,6 +685,9 @@ sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerT if (p == NULL) { p = android::createPlayer(playerType, this, notify); } + + p->setUID(mUID); + return p; } diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 8bab471fac6b..e32b92a659ca 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -306,7 +306,8 @@ private: pid_t pid, int32_t connId, const sp<IMediaPlayerClient>& client, - int audioSessionId); + int audioSessionId, + uid_t uid); Client(); virtual ~Client(); @@ -336,6 +337,7 @@ private: bool mLoop; int32_t mConnId; int mAudioSessionId; + uid_t mUID; // Metadata filters. media::Metadata::Filter mMetadataAllow; // protected by mLock diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp index 870e290ee09e..40e055cc5aca 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.cpp +++ b/media/libmediaplayerservice/StagefrightPlayer.cpp @@ -47,6 +47,12 @@ status_t StagefrightPlayer::initCheck() { return OK; } +status_t StagefrightPlayer::setUID(uid_t uid) { + mPlayer->setUID(uid); + + return OK; +} + status_t StagefrightPlayer::setDataSource( const char *url, const KeyedVector<String8, String8> *headers) { return mPlayer->setDataSource(url, headers); diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h index 85a546dc89e8..cbc6d4996da1 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.h +++ b/media/libmediaplayerservice/StagefrightPlayer.h @@ -31,6 +31,8 @@ public: virtual status_t initCheck(); + virtual status_t setUID(uid_t uid); + virtual status_t setDataSource( const char *url, const KeyedVector<String8, String8> *headers); diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index b3b3af5b39a9..5a5330d69feb 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -35,8 +35,11 @@ namespace android { NuPlayer::HTTPLiveSource::HTTPLiveSource( const char *url, - const KeyedVector<String8, String8> *headers) + const KeyedVector<String8, String8> *headers, + bool uidValid, uid_t uid) : mURL(url), + mUIDValid(uidValid), + mUID(uid), mFlags(0), mEOS(false), mOffset(0) { @@ -65,7 +68,8 @@ void NuPlayer::HTTPLiveSource::start() { mLiveLooper->start(); mLiveSession = new LiveSession( - (mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0); + (mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0, + mUIDValid, mUID); mLiveLooper->registerHandler(mLiveSession); diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index 7a337e9aa025..36c67c501b38 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -29,7 +29,9 @@ struct LiveSession; struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { HTTPLiveSource( const char *url, - const KeyedVector<String8, String8> *headers); + const KeyedVector<String8, String8> *headers, + bool uidValid = false, + uid_t uid = 0); virtual void start(); @@ -54,6 +56,8 @@ private: AString mURL; KeyedVector<String8, String8> mExtraHeaders; + bool mUIDValid; + uid_t mUID; uint32_t mFlags; bool mEOS; off64_t mOffset; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index effa7038fe6d..b06f20d55c80 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -44,7 +44,8 @@ namespace android { //////////////////////////////////////////////////////////////////////////////// NuPlayer::NuPlayer() - : mAudioEOS(false), + : mUIDValid(false), + mAudioEOS(false), mVideoEOS(false), mScanSourcesPending(false), mScanSourcesGeneration(0), @@ -57,6 +58,11 @@ NuPlayer::NuPlayer() NuPlayer::~NuPlayer() { } +void NuPlayer::setUID(uid_t uid) { + mUIDValid = true; + mUID = uid; +} + void NuPlayer::setDriver(const wp<NuPlayerDriver> &driver) { mDriver = driver; } @@ -72,7 +78,7 @@ void NuPlayer::setDataSource( const char *url, const KeyedVector<String8, String8> *headers) { sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); - msg->setObject("source", new HTTPLiveSource(url, headers)); + msg->setObject("source", new HTTPLiveSource(url, headers, mUIDValid, mUID)); msg->post(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index fb5b001fb798..cf9185b814d9 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -33,6 +33,8 @@ struct NuPlayerDriver; struct NuPlayer : public AHandler { NuPlayer(); + void setUID(uid_t uid); + void setDriver(const wp<NuPlayerDriver> &driver); void setDataSource(const sp<IStreamSource> &source); @@ -84,6 +86,8 @@ private: }; wp<NuPlayerDriver> mDriver; + bool mUIDValid; + uid_t mUID; sp<Source> mSource; sp<NativeWindowWrapper> mNativeWindow; sp<MediaPlayerBase::AudioSink> mAudioSink; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index e1213f41c345..7cd8b6cc7f55 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -55,6 +55,12 @@ status_t NuPlayerDriver::initCheck() { return OK; } +status_t NuPlayerDriver::setUID(uid_t uid) { + mPlayer->setUID(uid); + + return OK; +} + status_t NuPlayerDriver::setDataSource( const char *url, const KeyedVector<String8, String8> *headers) { CHECK_EQ((int)mState, (int)UNINITIALIZED); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h index 145fd80b2dda..1bb7ca23f850 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h @@ -28,6 +28,8 @@ struct NuPlayerDriver : public MediaPlayerInterface { virtual status_t initCheck(); + virtual status_t setUID(uid_t uid); + virtual status_t setDataSource( const char *url, const KeyedVector<String8, String8> *headers); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 513eda82f868..d4d07b2815d9 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1190,6 +1190,17 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { CHECK(msg->findInt32("data1", &data1)); CHECK(msg->findInt32("data2", &data2)); + if (event == OMX_EventCmdComplete + && data1 == OMX_CommandFlush + && data2 == (int32_t)OMX_ALL) { + // Use of this notification is not consistent across + // implementations. We'll drop this notification and rely + // on flush-complete notifications on the individual port + // indices instead. + + return true; + } + return onOMXEvent( static_cast<OMX_EVENTTYPE>(event), static_cast<OMX_U32>(data1), @@ -2119,6 +2130,7 @@ bool ACodec::ExecutingToIdleState::onOMXEvent( return BaseState::onOMXEvent(event, data1, data2); } } + void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() { if (mCodec->allYourBuffersAreBelongToUs()) { CHECK_EQ(mCodec->mOMX->sendCommand( @@ -2282,6 +2294,11 @@ bool ACodec::FlushingState::onOMXEvent( if (data2 == kPortIndexInput || data2 == kPortIndexOutput) { CHECK(!mFlushComplete[data2]); mFlushComplete[data2] = true; + + if (mFlushComplete[kPortIndexInput] + && mFlushComplete[kPortIndexOutput]) { + changeStateIfWeOwnAllBuffers(); + } } else { CHECK_EQ(data2, OMX_ALL); CHECK(mFlushComplete[kPortIndexInput]); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index aa7edccb2ffb..77c25d19591b 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -128,6 +128,9 @@ struct AwesomeNativeWindowRenderer : public AwesomeRenderer { } virtual void render(MediaBuffer *buffer) { + int64_t timeUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); + native_window_set_buffers_timestamp(mNativeWindow.get(), timeUs * 1000); status_t err = mNativeWindow->queueBuffer( mNativeWindow.get(), buffer->graphicBuffer().get()); if (err != 0) { @@ -180,6 +183,7 @@ void addBatteryData(uint32_t params) { //////////////////////////////////////////////////////////////////////////////// AwesomePlayer::AwesomePlayer() : mQueueStarted(false), + mUIDValid(false), mTimeSource(NULL), mVideoRendererIsPreview(false), mAudioPlayer(NULL), @@ -243,6 +247,13 @@ void AwesomePlayer::setListener(const wp<MediaPlayerBase> &listener) { mListener = listener; } +void AwesomePlayer::setUID(uid_t uid) { + LOGI("AwesomePlayer running on behalf of uid %d", uid); + + mUID = uid; + mUIDValid = true; +} + status_t AwesomePlayer::setDataSource( const char *uri, const KeyedVector<String8, String8> *headers) { Mutex::Autolock autoLock(mLock); @@ -1928,6 +1939,10 @@ status_t AwesomePlayer::finishSetDataSource_l() { ? HTTPBase::kFlagIncognito : 0); + if (mUIDValid) { + mConnectingDataSource->setUID(mUID); + } + mLock.unlock(); status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders); mLock.lock(); @@ -2009,6 +2024,10 @@ status_t AwesomePlayer::finishSetDataSource_l() { mRTSPController = new ARTSPController(mLooper); mConnectingRTSPController = mRTSPController; + if (mUIDValid) { + mConnectingRTSPController->setUID(mUID); + } + mLock.unlock(); status_t err = mRTSPController->connect(mUri.string()); mLock.lock(); diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index bdffce9c10fe..ed8149abbbd3 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -158,9 +158,12 @@ CameraSource::CameraSource( mVideoSize.width = -1; mVideoSize.height = -1; + int64_t token = IPCThreadState::self()->clearCallingIdentity(); mInitCheck = init(camera, proxy, cameraId, videoSize, frameRate, storeMetaDataInVideoBuffers); + if (mInitCheck != OK) releaseCamera(); + IPCThreadState::self()->restoreCallingIdentity(token); } status_t CameraSource::initCheck() const { @@ -295,7 +298,6 @@ status_t CameraSource::configureCamera( if (width != -1 && height != -1) { if (!isVideoSizeSupported(width, height, sizes)) { LOGE("Video dimension (%dx%d) is unsupported", width, height); - releaseCamera(); return BAD_VALUE; } if (isSetVideoSizeSupportedByCamera) { @@ -309,7 +311,6 @@ status_t CameraSource::configureCamera( // If one and only one of the width and height is -1 // we reject such a request. LOGE("Requested video size (%dx%d) is not supported", width, height); - releaseCamera(); return BAD_VALUE; } else { // width == -1 && height == -1 // Do not configure the camera. @@ -327,7 +328,6 @@ status_t CameraSource::configureCamera( if (strstr(supportedFrameRates, buf) == NULL) { LOGE("Requested frame rate (%d) is not supported: %s", frameRate, supportedFrameRates); - releaseCamera(); return BAD_VALUE; } @@ -463,7 +463,6 @@ status_t CameraSource::init( bool storeMetaDataInVideoBuffers) { status_t err = OK; - int64_t token = IPCThreadState::self()->clearCallingIdentity(); if ((err = isCameraAvailable(camera, proxy, cameraId)) != OK) { LOGE("Camera connection could not be established."); @@ -505,8 +504,6 @@ status_t CameraSource::init( } } - IPCThreadState::self()->restoreCallingIdentity(token); - int64_t glitchDurationUs = (1000000LL / mVideoFrameRate); if (glitchDurationUs > mGlitchDurationThresholdUs) { mGlitchDurationThresholdUs = glitchDurationUs; @@ -573,13 +570,21 @@ void CameraSource::stopCameraRecording() { void CameraSource::releaseCamera() { LOGV("releaseCamera"); - if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) { - LOGV("Camera was cold when we started, stopping preview"); - mCamera->stopPreview(); + if (mCamera != 0) { + if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) { + LOGV("Camera was cold when we started, stopping preview"); + mCamera->stopPreview(); + mCamera->disconnect(); + } else { + // Unlock the camera so the application can lock it back. + mCamera->unlock(); + } + mCamera.clear(); + } + if (mCameraRecordingProxy != 0) { + mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier); + mCameraRecordingProxy.clear(); } - mCamera.clear(); - mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier); - mCameraRecordingProxy.clear(); mCameraFlags = 0; } diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp index c0ae29dc1ac7..0d2455108a23 100644 --- a/media/libstagefright/HTTPBase.cpp +++ b/media/libstagefright/HTTPBase.cpp @@ -37,7 +37,8 @@ HTTPBase::HTTPBase() mTotalTransferBytes(0), mPrevBandwidthMeasureTimeUs(0), mPrevEstimatedBandWidthKbps(0), - mBandWidthCollectFreqMs(5000) { + mBandWidthCollectFreqMs(5000), + mUIDValid(false) { } // static @@ -119,4 +120,19 @@ status_t HTTPBase::setBandwidthStatCollectFreq(int32_t freqMs) { return OK; } +void HTTPBase::setUID(uid_t uid) { + mUIDValid = true; + mUID = uid; +} + +bool HTTPBase::getUID(uid_t *uid) const { + if (!mUIDValid) { + return false; + } + + *uid = mUID; + + return true; +} + } // namespace android diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp index a156da69b6cd..d526ebd8bc1a 100644 --- a/media/libstagefright/HTTPStream.cpp +++ b/media/libstagefright/HTTPStream.cpp @@ -43,6 +43,7 @@ const char *HTTPStream::kStatusKey = ":status:"; // MUST be lowercase. HTTPStream::HTTPStream() : mState(READY), + mUIDValid(false), mSocket(-1), mSSLContext(NULL), mSSL(NULL) { @@ -57,6 +58,11 @@ HTTPStream::~HTTPStream() { } } +void HTTPStream::setUID(uid_t uid) { + mUIDValid = true; + mUID = uid; +} + static bool MakeSocketBlocking(int s, bool blocking) { // Make socket non-blocking. int flags = fcntl(s, F_GETFL, 0); @@ -250,6 +256,10 @@ status_t HTTPStream::connect(const char *server, int port, bool https) { continue; } + if (mUIDValid) { + RegisterSocketUser(mSocket, mUID); + } + setReceiveTimeout(30); // Time out reads after 30 secs by default. int s = mSocket; @@ -596,5 +606,18 @@ void HTTPStream::setReceiveTimeout(int seconds) { CHECK_EQ(0, setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))); } +// static +void HTTPStream::RegisterSocketUser(int s, uid_t uid) { + // Lower bits MUST be 0. + static const uint64_t kTag = 0xdeadbeef00000000ll; + + AString line = StringPrintf("t %d %llu %d", s, kTag, uid); + + int fd = open("/proc/net/xt_qtaguid/ctrl", O_WRONLY); + write(fd, line.c_str(), line.size()); + close(fd); + fd = -1; +} + } // namespace android diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp index dac2ee4766d0..29497673fbd4 100644 --- a/media/libstagefright/NuHTTPDataSource.cpp +++ b/media/libstagefright/NuHTTPDataSource.cpp @@ -140,6 +140,11 @@ status_t NuHTTPDataSource::connect( return ERROR_MALFORMED; } + uid_t uid; + if (getUID(&uid)) { + mHTTP.setUID(uid); + } + return connect(host, port, path, https, headers, offset); } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index cd97302c290b..1ac2c1fbbfb4 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -477,6 +477,15 @@ sp<MediaSource> OMXCodec::Create( const char *matchComponentName, uint32_t flags, const sp<ANativeWindow> &nativeWindow) { + int32_t requiresSecureBuffers; + if (source->getFormat()->findInt32( + kKeyRequiresSecureBuffers, + &requiresSecureBuffers) + && requiresSecureBuffers) { + flags |= kIgnoreCodecSpecificData; + flags |= kUseSecureInputBuffers; + } + const char *mime; bool success = meta->findCString(kKeyMIMEType, &mime); CHECK(success); @@ -530,17 +539,17 @@ sp<MediaSource> OMXCodec::Create( LOGV("Successfully allocated OMX node '%s'", componentName); sp<OMXCodec> codec = new OMXCodec( - omx, node, quirks, + omx, node, quirks, flags, createEncoder, mime, componentName, source, nativeWindow); observer->setCodec(codec); - err = codec->configureCodec(meta, flags); + err = codec->configureCodec(meta); if (err == OK) { if (!strcmp("OMX.Nvidia.mpeg2v.decode", componentName)) { - codec->mOnlySubmitOneBufferAtOneTime = true; + codec->mFlags |= kOnlySubmitOneInputBufferAtOneTime; } return codec; @@ -553,24 +562,11 @@ sp<MediaSource> OMXCodec::Create( return NULL; } -status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) { - mIsMetaDataStoredInVideoBuffers = false; - if (flags & kStoreMetaDataInVideoBuffers) { - mIsMetaDataStoredInVideoBuffers = true; - } - - mOnlySubmitOneBufferAtOneTime = false; - if (flags & kOnlySubmitOneInputBufferAtOneTime) { - mOnlySubmitOneBufferAtOneTime = true; - } +status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { + LOGV("configureCodec protected=%d", + (mFlags & kEnableGrallocUsageProtected) ? 1 : 0); - mEnableGrallocUsageProtected = false; - if (flags & kEnableGrallocUsageProtected) { - mEnableGrallocUsageProtected = true; - } - LOGV("configureCodec protected=%d", mEnableGrallocUsageProtected); - - if (!(flags & kIgnoreCodecSpecificData)) { + if (!(mFlags & kIgnoreCodecSpecificData)) { uint32_t type; const void *data; size_t size; @@ -745,7 +741,7 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) { initOutputFormat(meta); - if ((flags & kClientNeedsFramebuffer) + if ((mFlags & kClientNeedsFramebuffer) && !strncmp(mComponentName, "OMX.SEC.", 8)) { OMX_INDEXTYPE index; @@ -1468,7 +1464,8 @@ status_t OMXCodec::setVideoOutputFormat( } OMXCodec::OMXCodec( - const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks, + const sp<IOMX> &omx, IOMX::node_id node, + uint32_t quirks, uint32_t flags, bool isEncoder, const char *mime, const char *componentName, @@ -1478,6 +1475,7 @@ OMXCodec::OMXCodec( mOMXLivesLocally(omx->livesLocally(getpid())), mNode(node), mQuirks(quirks), + mFlags(flags), mIsEncoder(isEncoder), mMIME(strdup(mime)), mComponentName(strdup(componentName)), @@ -1645,13 +1643,14 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { return allocateOutputBuffersFromNativeWindow(); } - if (mEnableGrallocUsageProtected && portIndex == kPortIndexOutput) { + if ((mFlags & kEnableGrallocUsageProtected) && portIndex == kPortIndexOutput) { LOGE("protected output buffers must be stent to an ANativeWindow"); return PERMISSION_DENIED; } status_t err = OK; - if (mIsMetaDataStoredInVideoBuffers && portIndex == kPortIndexInput) { + if ((mFlags & kStoreMetaDataInVideoBuffers) + && portIndex == kPortIndexInput) { err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexInput, OMX_TRUE); if (err != OK) { LOGE("Storing meta data in video buffers is not supported"); @@ -1687,7 +1686,8 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { IOMX::buffer_id buffer; if (portIndex == kPortIndexInput - && (mQuirks & kRequiresAllocateBufferOnInputPorts)) { + && ((mQuirks & kRequiresAllocateBufferOnInputPorts) + || (mFlags & kUseSecureInputBuffers))) { if (mOMXLivesLocally) { mem.clear(); @@ -1748,6 +1748,31 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { // dumpPortStatus(portIndex); + if (portIndex == kPortIndexInput && (mFlags & kUseSecureInputBuffers)) { + Vector<MediaBuffer *> buffers; + for (size_t i = 0; i < def.nBufferCountActual; ++i) { + const BufferInfo &info = mPortBuffers[kPortIndexInput].itemAt(i); + + MediaBuffer *mbuf = new MediaBuffer(info.mData, info.mSize); + buffers.push(mbuf); + } + + status_t err = mSource->setBuffers(buffers); + + if (err != OK) { + for (size_t i = 0; i < def.nBufferCountActual; ++i) { + buffers.editItemAt(i)->release(); + } + buffers.clear(); + + CODEC_LOGE( + "Codec requested to use secure input buffers but " + "upstream source didn't support that."); + + return err; + } + } + return OK; } @@ -1815,7 +1840,7 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { // XXX: Currently this error is logged, but not fatal. usage = 0; } - if (mEnableGrallocUsageProtected) { + if (mFlags & kEnableGrallocUsageProtected) { usage |= GRALLOC_USAGE_PROTECTED; } @@ -1838,7 +1863,7 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { } } - LOGV("native_window_set_usage usage=0x%x", usage); + LOGV("native_window_set_usage usage=0x%lx", usage); err = native_window_set_usage( mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP); if (err != 0) { @@ -2067,7 +2092,12 @@ void OMXCodec::on_message(const omx_message &msg) { } else if (mState != ERROR && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) { CHECK_EQ((int)mPortStatus[kPortIndexInput], (int)ENABLED); - drainInputBuffer(&buffers->editItemAt(i)); + + if (mFlags & kUseSecureInputBuffers) { + drainAnyInputBuffer(); + } else { + drainInputBuffer(&buffers->editItemAt(i)); + } } break; } @@ -2804,32 +2834,81 @@ void OMXCodec::fillOutputBuffers() { void OMXCodec::drainInputBuffers() { CHECK(mState == EXECUTING || mState == RECONFIGURING); - Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput]; - for (size_t i = 0; i < buffers->size(); ++i) { - BufferInfo *info = &buffers->editItemAt(i); + if (mFlags & kUseSecureInputBuffers) { + Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput]; + for (size_t i = 0; i < buffers->size(); ++i) { + if (!drainAnyInputBuffer() + || (mFlags & kOnlySubmitOneInputBufferAtOneTime)) { + break; + } + } + } else { + Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput]; + for (size_t i = 0; i < buffers->size(); ++i) { + BufferInfo *info = &buffers->editItemAt(i); - if (info->mStatus != OWNED_BY_US) { - continue; + if (info->mStatus != OWNED_BY_US) { + continue; + } + + if (!drainInputBuffer(info)) { + break; + } + + if (mFlags & kOnlySubmitOneInputBufferAtOneTime) { + break; + } } + } +} - if (!drainInputBuffer(info)) { - break; +bool OMXCodec::drainAnyInputBuffer() { + return drainInputBuffer((BufferInfo *)NULL); +} + +OMXCodec::BufferInfo *OMXCodec::findInputBufferByDataPointer(void *ptr) { + Vector<BufferInfo> *infos = &mPortBuffers[kPortIndexInput]; + for (size_t i = 0; i < infos->size(); ++i) { + BufferInfo *info = &infos->editItemAt(i); + + if (info->mData == ptr) { + CODEC_LOGV( + "input buffer data ptr = %p, buffer_id = %p", + ptr, + info->mBuffer); + + return info; } + } - if (mOnlySubmitOneBufferAtOneTime) { - break; + TRESPASS(); +} + +OMXCodec::BufferInfo *OMXCodec::findEmptyInputBuffer() { + Vector<BufferInfo> *infos = &mPortBuffers[kPortIndexInput]; + for (size_t i = 0; i < infos->size(); ++i) { + BufferInfo *info = &infos->editItemAt(i); + + if (info->mStatus == OWNED_BY_US) { + return info; } } + + TRESPASS(); } bool OMXCodec::drainInputBuffer(BufferInfo *info) { - CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US); + if (info != NULL) { + CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US); + } if (mSignalledEOS) { return false; } if (mCodecSpecificDataIndex < mCodecSpecificData.size()) { + CHECK(!(mFlags & kUseSecureInputBuffers)); + const CodecSpecificData *specific = mCodecSpecificData[mCodecSpecificDataIndex]; @@ -2925,6 +3004,11 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) { break; } + if (mFlags & kUseSecureInputBuffers) { + info = findInputBufferByDataPointer(srcBuffer->data()); + CHECK(info != NULL); + } + size_t remainingBytes = info->mSize - offset; if (srcBuffer->range_length() > remainingBytes) { @@ -2960,14 +3044,24 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) { releaseBuffer = false; info->mMediaBuffer = srcBuffer; } else { - if (mIsMetaDataStoredInVideoBuffers) { + if (mFlags & kStoreMetaDataInVideoBuffers) { releaseBuffer = false; info->mMediaBuffer = srcBuffer; } - memcpy((uint8_t *)info->mData + offset, - (const uint8_t *)srcBuffer->data() - + srcBuffer->range_offset(), - srcBuffer->range_length()); + + if (mFlags & kUseSecureInputBuffers) { + // Data in "info" is already provided at this time. + + releaseBuffer = false; + + CHECK(info->mMediaBuffer == NULL); + info->mMediaBuffer = srcBuffer; + } else { + memcpy((uint8_t *)info->mData + offset, + (const uint8_t *)srcBuffer->data() + + srcBuffer->range_offset(), + srcBuffer->range_length()); + } } int64_t lastBufferTimeUs; @@ -3036,6 +3130,16 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) { info->mBuffer, offset, timestampUs, timestampUs / 1E6); + if (info == NULL) { + CHECK(mFlags & kUseSecureInputBuffers); + CHECK(signalEOS); + + // This is fishy, there's still a MediaBuffer corresponding to this + // info available to the source at this point even though we're going + // to use it to signal EOS to the codec. + info = findEmptyInputBuffer(); + } + err = mOMX->emptyBuffer( mNode, info->mBuffer, 0, offset, flags, timestampUs); diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp index 7072d58d6cce..26eda0c4cfb1 100644 --- a/media/libstagefright/WVMExtractor.cpp +++ b/media/libstagefright/WVMExtractor.cpp @@ -33,25 +33,26 @@ #include <utils/Errors.h> +/* The extractor lifetime is short - just long enough to get + * the media sources constructed - so the shared lib needs to remain open + * beyond the lifetime of the extractor. So keep the handle as a global + * rather than a member of the extractor + */ +void *gVendorLibHandle = NULL; + namespace android { -Mutex WVMExtractor::sMutex; -uint32_t WVMExtractor::sActiveExtractors = 0; -void *WVMExtractor::sVendorLibHandle = NULL; +static Mutex gWVMutex; WVMExtractor::WVMExtractor(const sp<DataSource> &source) : mDataSource(source) { { - Mutex::Autolock autoLock(sMutex); - - if (sVendorLibHandle == NULL) { - CHECK(sActiveExtractors == 0); - sVendorLibHandle = dlopen("libwvm.so", RTLD_NOW); + Mutex::Autolock autoLock(gWVMutex); + if (gVendorLibHandle == NULL) { + gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW); } - sActiveExtractors++; - - if (sVendorLibHandle == NULL) { + if (gVendorLibHandle == NULL) { LOGE("Failed to open libwvm.so"); return; } @@ -59,7 +60,7 @@ WVMExtractor::WVMExtractor(const sp<DataSource> &source) typedef WVMLoadableExtractor *(*GetInstanceFunc)(sp<DataSource>); GetInstanceFunc getInstanceFunc = - (GetInstanceFunc) dlsym(sVendorLibHandle, + (GetInstanceFunc) dlsym(gVendorLibHandle, "_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE"); if (getInstanceFunc) { @@ -71,17 +72,6 @@ WVMExtractor::WVMExtractor(const sp<DataSource> &source) } WVMExtractor::~WVMExtractor() { - Mutex::Autolock autoLock(sMutex); - - CHECK(sActiveExtractors > 0); - sActiveExtractors--; - - // Close lib after last use - if (sActiveExtractors == 0) { - if (sVendorLibHandle != NULL) - dlclose(sVendorLibHandle); - sVendorLibHandle = NULL; - } } size_t WVMExtractor::countTracks() { diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp index ed6846c4e406..f4b36688f4cd 100644 --- a/media/libstagefright/chromium_http/support.cpp +++ b/media/libstagefright/chromium_http/support.cpp @@ -41,6 +41,7 @@ namespace android { static Mutex gNetworkThreadLock; static base::Thread *gNetworkThread = NULL; static scoped_refptr<net::URLRequestContext> gReqContext; +static scoped_ptr<net::NetworkChangeNotifier> gNetworkChangeNotifier; static void InitializeNetworkThreadIfNecessary() { Mutex::Autolock autoLock(gNetworkThreadLock); @@ -52,6 +53,8 @@ static void InitializeNetworkThreadIfNecessary() { gReqContext = new SfRequestContext; + gNetworkChangeNotifier.reset(net::NetworkChangeNotifier::Create()); + net::AndroidNetworkLibrary::RegisterSharedInstance( new SfNetworkLibrary); } @@ -112,31 +115,31 @@ SfRequestContext::SfRequestContext() { mUserAgent = ua.c_str(); - net_log_ = new SfNetLog; + set_net_log(new SfNetLog()); - host_resolver_ = + set_host_resolver( net::CreateSystemHostResolver( net::HostResolver::kDefaultParallelism, NULL /* resolver_proc */, - net_log_); + net_log())); - ssl_config_service_ = - net::SSLConfigService::CreateSystemSSLConfigService(); + set_ssl_config_service( + net::SSLConfigService::CreateSystemSSLConfigService()); - proxy_service_ = net::ProxyService::CreateWithoutProxyResolver( - new net::ProxyConfigServiceAndroid, net_log_); + set_proxy_service(net::ProxyService::CreateWithoutProxyResolver( + new net::ProxyConfigServiceAndroid, net_log())); - http_transaction_factory_ = new net::HttpCache( - host_resolver_, + set_http_transaction_factory(new net::HttpCache( + host_resolver(), new net::CertVerifier(), - dnsrr_resolver_, - dns_cert_checker_.get(), - proxy_service_.get(), - ssl_config_service_.get(), - net::HttpAuthHandlerFactory::CreateDefault(host_resolver_), - network_delegate_, - net_log_, - NULL); // backend_factory + dnsrr_resolver(), + dns_cert_checker(), + proxy_service(), + ssl_config_service(), + net::HttpAuthHandlerFactory::CreateDefault(host_resolver()), + network_delegate(), + net_log(), + NULL)); // backend_factory } const std::string &SfRequestContext::GetUserAgent(const GURL &url) const { diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp index e3292e63c679..009676011714 100644 --- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp +++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp @@ -475,7 +475,9 @@ status_t AVCEncoder::read( } status_t err = mSource->read(&mInputBuffer, options); if (err != OK) { - LOGE("Failed to read input video frame: %d", err); + if (err != ERROR_END_OF_STREAM) { + LOGE("Failed to read input video frame: %d", err); + } outputBuffer->release(); return err; } diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp index 15ed2193ae61..d7249c1ebb4f 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp @@ -398,10 +398,13 @@ status_t M4vH263Encoder::read( } // Ready for accepting an input video frame - if (OK != mSource->read(&mInputBuffer, options)) { - LOGE("Failed to read from data source"); + status_t err = mSource->read(&mInputBuffer, options); + if (OK != err) { + if (err != ERROR_END_OF_STREAM) { + LOGE("Failed to read from data source"); + } outputBuffer->release(); - return UNKNOWN_ERROR; + return err; } if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) { diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 165683e986b6..8ecc17ce5580 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -41,8 +41,10 @@ namespace android { const int64_t LiveSession::kMaxPlaylistAgeUs = 15000000ll; -LiveSession::LiveSession(uint32_t flags) +LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid) : mFlags(flags), + mUIDValid(uidValid), + mUID(uid), mDataSource(new LiveDataSource), mHTTPDataSource( HTTPBase::Create( @@ -58,6 +60,9 @@ LiveSession::LiveSession(uint32_t flags) mSeekDone(false), mDisconnectPending(false), mMonitorQueueGeneration(0) { + if (mUIDValid) { + mHTTPDataSource->setUID(mUID); + } } LiveSession::~LiveSession() { @@ -408,13 +413,20 @@ rinse_repeat: if (firstTime) { Mutex::Autolock autoLock(mLock); - int32_t targetDuration; - if (!mPlaylist->isComplete() - || !mPlaylist->meta()->findInt32( - "target-duration", &targetDuration)) { + if (!mPlaylist->isComplete()) { mDurationUs = -1; } else { - mDurationUs = 1000000ll * targetDuration * mPlaylist->size(); + mDurationUs = 0; + for (size_t i = 0; i < mPlaylist->size(); ++i) { + sp<AMessage> itemMeta; + CHECK(mPlaylist->itemAt( + i, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + mDurationUs += itemDurationUs; + } } } @@ -431,14 +443,26 @@ rinse_repeat: bool bandwidthChanged = false; if (mSeekTimeUs >= 0) { - int32_t targetDuration; - if (mPlaylist->isComplete() && - mPlaylist->meta()->findInt32( - "target-duration", &targetDuration)) { - int64_t seekTimeSecs = (mSeekTimeUs + 500000ll) / 1000000ll; - int64_t index = seekTimeSecs / targetDuration; - - if (index >= 0 && index < mPlaylist->size()) { + if (mPlaylist->isComplete()) { + size_t index = 0; + int64_t segmentStartUs = 0; + while (index < mPlaylist->size()) { + sp<AMessage> itemMeta; + CHECK(mPlaylist->itemAt( + index, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + if (mSeekTimeUs < segmentStartUs + itemDurationUs) { + break; + } + + segmentStartUs += itemDurationUs; + ++index; + } + + if (index < mPlaylist->size()) { int32_t newSeqNumber = firstSeqNumberInPlaylist + index; if (newSeqNumber != mSeqNumber) { @@ -652,6 +676,10 @@ status_t LiveSession::decryptBuffer( ? HTTPBase::kFlagIncognito : 0); + if (mUIDValid) { + keySource->setUID(mUID); + } + status_t err = keySource->connect(keyURI.c_str()); if (err == OK) { diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 765f79565d5a..123fbf8b23e4 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -64,14 +64,21 @@ size_t M3UParser::size() { } bool M3UParser::itemAt(size_t index, AString *uri, sp<AMessage> *meta) { - uri->clear(); - if (meta) { *meta = NULL; } + if (uri) { + uri->clear(); + } + + if (meta) { + *meta = NULL; + } if (index >= mItems.size()) { return false; } - *uri = mItems.itemAt(index).mURI; + if (uri) { + *uri = mItems.itemAt(index).mURI; + } if (meta) { *meta = mItems.itemAt(index).mMeta; diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h index ce7ffe5c2352..2bd5be632a26 100644 --- a/media/libstagefright/include/ARTSPController.h +++ b/media/libstagefright/include/ARTSPController.h @@ -30,6 +30,8 @@ struct MyHandler; struct ARTSPController : public MediaExtractor { ARTSPController(const sp<ALooper> &looper); + void setUID(uid_t uid); + status_t connect(const char *url); void disconnect(); @@ -80,6 +82,9 @@ private: sp<MyHandler> mHandler; sp<AHandlerReflector<ARTSPController> > mReflector; + bool mUIDValid; + uid_t mUID; + void (*mSeekDoneCb)(void *); void *mSeekDoneCookie; int64_t mLastSeekCompletedTimeUs; diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index f6df3801c2fe..e069b4dbaf65 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -62,6 +62,7 @@ struct AwesomePlayer { ~AwesomePlayer(); void setListener(const wp<MediaPlayerBase> &listener); + void setUID(uid_t uid); status_t setDataSource( const char *uri, @@ -150,6 +151,8 @@ private: TimedEventQueue mQueue; bool mQueueStarted; wp<MediaPlayerBase> mListener; + bool mUIDValid; + uid_t mUID; sp<Surface> mSurface; sp<ANativeWindow> mNativeWindow; diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h index 3a7fbb690d16..2e25dd918086 100644 --- a/media/libstagefright/include/HTTPBase.h +++ b/media/libstagefright/include/HTTPBase.h @@ -48,13 +48,15 @@ struct HTTPBase : public DataSource { virtual status_t setBandwidthStatCollectFreq(int32_t freqMs); + void setUID(uid_t uid); + bool getUID(uid_t *uid) const; + static sp<HTTPBase> Create(uint32_t flags = 0); protected: void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); private: - struct BandwidthEntry { int64_t mDelayUs; size_t mNumBytes; @@ -76,6 +78,8 @@ private: int32_t mPrevEstimatedBandWidthKbps; int32_t mBandWidthCollectFreqMs; + bool mUIDValid; + uid_t mUID; DISALLOW_EVIL_CONSTRUCTORS(HTTPBase); }; diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h index 09e6a5fbdf52..88ba9d6e1435 100644 --- a/media/libstagefright/include/HTTPStream.h +++ b/media/libstagefright/include/HTTPStream.h @@ -32,6 +32,8 @@ public: HTTPStream(); ~HTTPStream(); + void setUID(uid_t uid); + status_t connect(const char *server, int port = -1, bool https = false); status_t disconnect(); @@ -58,6 +60,8 @@ public: // _excluding_ the termianting CRLF. status_t receive_line(char *line, size_t size); + static void RegisterSocketUser(int s, uid_t uid); + private: enum State { READY, @@ -67,6 +71,10 @@ private: State mState; Mutex mLock; + + bool mUIDValid; + uid_t mUID; + int mSocket; KeyedVector<AString, AString> mHeaders; diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h index 99abe64c2fea..188ef5e605b3 100644 --- a/media/libstagefright/include/LiveSession.h +++ b/media/libstagefright/include/LiveSession.h @@ -35,7 +35,7 @@ struct LiveSession : public AHandler { // Don't log any URLs. kFlagIncognito = 1, }; - LiveSession(uint32_t flags = 0); + LiveSession(uint32_t flags = 0, bool uidValid = false, uid_t uid = 0); sp<DataSource> getDataSource(); @@ -77,6 +77,8 @@ private: }; uint32_t mFlags; + bool mUIDValid; + uid_t mUID; sp<LiveDataSource> mDataSource; diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h index 0817babb45e0..deecd2543a92 100644 --- a/media/libstagefright/include/WVMExtractor.h +++ b/media/libstagefright/include/WVMExtractor.h @@ -18,7 +18,6 @@ #define WVM_EXTRACTOR_H_ -#include <media/stagefright/DataSource.h> #include <media/stagefright/MediaExtractor.h> #include <utils/Errors.h> @@ -68,10 +67,6 @@ private: WVMExtractor(const WVMExtractor &); WVMExtractor &operator=(const WVMExtractor &); - - static Mutex sMutex; - static uint32_t sActiveExtractors; - static void *sVendorLibHandle; }; } // namespace android diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index c4e0cdcab5d3..072d6b2fd46d 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -34,13 +34,17 @@ #include <openssl/md5.h> #include <sys/socket.h> +#include "HTTPStream.h" + namespace android { // static const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll; -ARTSPConnection::ARTSPConnection() - : mState(DISCONNECTED), +ARTSPConnection::ARTSPConnection(bool uidValid, uid_t uid) + : mUIDValid(uidValid), + mUID(uid), + mState(DISCONNECTED), mAuthType(NONE), mSocket(-1), mConnectionID(0), @@ -246,6 +250,10 @@ void ARTSPConnection::onConnect(const sp<AMessage> &msg) { mSocket = socket(AF_INET, SOCK_STREAM, 0); + if (mUIDValid) { + HTTPStream::RegisterSocketUser(mSocket, mUID); + } + MakeSocketBlocking(mSocket, false); struct sockaddr_in remote; diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h index ac2e3ae1b856..5cb84fd08f96 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.h +++ b/media/libstagefright/rtsp/ARTSPConnection.h @@ -33,7 +33,7 @@ struct ARTSPResponse : public RefBase { }; struct ARTSPConnection : public AHandler { - ARTSPConnection(); + ARTSPConnection(bool uidValid = false, uid_t uid = 0); void connect(const char *url, const sp<AMessage> &reply); void disconnect(const sp<AMessage> &reply); @@ -74,6 +74,8 @@ private: static const int64_t kSelectTimeoutUs; + bool mUIDValid; + uid_t mUID; State mState; AString mUser, mPass; AuthType mAuthType; diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp index 1328d2ec595c..2ebae7e37abc 100644 --- a/media/libstagefright/rtsp/ARTSPController.cpp +++ b/media/libstagefright/rtsp/ARTSPController.cpp @@ -28,6 +28,7 @@ namespace android { ARTSPController::ARTSPController(const sp<ALooper> &looper) : mState(DISCONNECTED), mLooper(looper), + mUIDValid(false), mSeekDoneCb(NULL), mSeekDoneCookie(NULL), mLastSeekCompletedTimeUs(-1) { @@ -40,6 +41,11 @@ ARTSPController::~ARTSPController() { mLooper->unregisterHandler(mReflector->id()); } +void ARTSPController::setUID(uid_t uid) { + mUIDValid = true; + mUID = uid; +} + status_t ARTSPController::connect(const char *url) { Mutex::Autolock autoLock(mLock); @@ -49,7 +55,7 @@ status_t ARTSPController::connect(const char *url) { sp<AMessage> msg = new AMessage(kWhatConnectDone, mReflector->id()); - mHandler = new MyHandler(url, mLooper); + mHandler = new MyHandler(url, mLooper, mUIDValid, mUID); mState = CONNECTING; diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index fd0505eb267c..f03f7a268fb2 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -301,9 +301,6 @@ void ASessionDescription::ParseFormatDesc( // static bool ASessionDescription::parseNTPRange( const char *s, float *npt1, float *npt2) { - *npt1 = 0.0f; - *npt2 = 0.0f; - if (s[0] == '-') { return false; // no start time available. } diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index f89f8e20054d..3188959564a0 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -40,6 +40,8 @@ #include <sys/socket.h> #include <netdb.h> +#include "HTTPStream.h" + // If no access units are received within 5 secs, assume that the rtp // stream has ended and signal end of stream. static int64_t kAccessUnitTimeoutUs = 5000000ll; @@ -92,10 +94,14 @@ static bool GetAttribute(const char *s, const char *key, AString *value) { } struct MyHandler : public AHandler { - MyHandler(const char *url, const sp<ALooper> &looper) - : mLooper(looper), + MyHandler( + const char *url, const sp<ALooper> &looper, + bool uidValid = false, uid_t uid = 0) + : mUIDValid(uidValid), + mUID(uid), + mLooper(looper), mNetLooper(new ALooper), - mConn(new ARTSPConnection), + mConn(new ARTSPConnection(mUIDValid, mUID)), mRTPConn(new ARTPConnection), mOriginalSessionURL(url), mSessionURL(url), @@ -995,12 +1001,10 @@ struct MyHandler : public AHandler { AString val; CHECK(GetAttribute(range.c_str(), "npt", &val)); - bool seekable = true; - float npt1, npt2; if (!ASessionDescription::parseNTPRange(val.c_str(), &npt1, &npt2)) { // This is a live stream and therefore not seekable. - seekable = false; + return; } i = response->mHeaders.indexOfKey("rtp-info"); @@ -1046,7 +1050,7 @@ struct MyHandler : public AHandler { ++n; } - mSeekable = seekable; + mSeekable = true; } sp<APacketSource> getPacketSource(size_t index) { @@ -1080,6 +1084,8 @@ private: List<sp<ABuffer> > mPackets; }; + bool mUIDValid; + uid_t mUID; sp<ALooper> mLooper; sp<ALooper> mNetLooper; sp<ARTSPConnection> mConn; @@ -1174,6 +1180,11 @@ private: ARTPConnection::MakePortPair( &info->mRTPSocket, &info->mRTCPSocket, &rtpPort); + if (mUIDValid) { + HTTPStream::RegisterSocketUser(info->mRTPSocket, mUID); + HTTPStream::RegisterSocketUser(info->mRTCPSocket, mUID); + } + request.append("Transport: RTP/AVP/UDP;unicast;client_port="); request.append(rtpPort); request.append("-"); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 6d8eab63c074..f42cbbfc4f7a 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -25,6 +25,11 @@ android:exported="true" /> + <!-- started from PhoneWindowManager + TODO: Should have an android:permission attribute --> + <service android:name=".screenshot.TakeScreenshotService" + android:exported="false" /> + <activity android:name=".usb.UsbPreferenceActivity" android:theme="@*android:style/Theme.Holo.Dialog.Alert" android:excludeFromRecents="true"> diff --git a/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png Binary files differnew file mode 100644 index 000000000000..e14111dae168 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png Binary files differnew file mode 100644 index 000000000000..e14111dae168 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml new file mode 100644 index 000000000000..6cb8799eaeb3 --- /dev/null +++ b/packages/SystemUI/res/layout/global_screenshot.xml @@ -0,0 +1,40 @@ +<?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:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView android:id="@+id/global_screenshot_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#FF000000" + android:visibility="gone" /> + <FrameLayout + android:id="@+id/global_screenshot_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/global_screenshot_background" + android:visibility="gone"> + <ImageView android:id="@+id/global_screenshot" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:adjustViewBounds="true" /> + </FrameLayout> + <ImageView android:id="@+id/global_screenshot_flash" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#FFFFFFFF" + android:visibility="gone" /> +</FrameLayout> diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index bc2f7ee3ae44..51e7d97ef1ec 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -25,14 +25,13 @@ android:layout_width="match_parent" > - <FrameLayout - android:id="@+id/background" + <FrameLayout android:id="@+id/rot0" android:layout_height="match_parent" android:layout_width="match_parent" android:background="#FF000000" > - <LinearLayout android:id="@+id/rot0" + <LinearLayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="horizontal" @@ -40,12 +39,12 @@ <!-- navigation controls --> <View - android:layout_width="32dp" + android:layout_width="40dp" android:layout_height="match_parent" android:layout_weight="0" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back" - android:layout_width="54dp" + android:layout_width="80dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_back_default" systemui:keyCode="4" @@ -57,7 +56,7 @@ android:layout_weight="1" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home" - android:layout_width="54dp" + android:layout_width="80dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_home_default" systemui:keyCode="3" @@ -69,13 +68,13 @@ android:layout_weight="1" /> <ImageView android:id="@+id/recent_apps" - android:layout_width="54dp" + android:layout_width="80dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_recent_default" android:layout_weight="0" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu" - android:layout_width="32dp" + android:layout_width="40dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_menu_default" systemui:keyCode="82" @@ -84,17 +83,31 @@ /> </LinearLayout> - <LinearLayout android:id="@+id/rot90" + <View android:id="@+id/deadzone" + android:layout_height="@dimen/navigation_bar_deadzone_size" + android:layout_width="match_parent" + android:layout_gravity="top" + android:clickable="true" + /> + </FrameLayout> + + <FrameLayout android:id="@+id/rot90" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:background="#FF000000" + android:visibility="gone" + android:paddingTop="24dp" + > + + <LinearLayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" - android:visibility="gone" - android:paddingTop="24dp" > <!-- navigation controls --> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu" - android:layout_height="32dp" + android:layout_height="40dp" android:layout_width="match_parent" android:src="@drawable/ic_sysbar_menu_default_land" systemui:keyCode="82" @@ -102,7 +115,7 @@ android:visibility="invisible" /> <ImageView android:id="@+id/recent_apps" - android:layout_height="54dp" + android:layout_height="80dp" android:layout_width="match_parent" android:src="@drawable/ic_sysbar_recent_default_land" android:layout_weight="0" @@ -113,7 +126,7 @@ android:layout_weight="1" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home" - android:layout_height="54dp" + android:layout_height="80dp" android:layout_width="match_parent" android:src="@drawable/ic_sysbar_home_default_land" systemui:keyCode="3" @@ -125,28 +138,32 @@ android:layout_weight="1" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back" - android:layout_height="54dp" + android:layout_height="80dp" android:layout_width="match_parent" android:src="@drawable/ic_sysbar_back_default_land" systemui:keyCode="4" android:layout_weight="0" /> <View - android:layout_height="32dp" + android:layout_height="40dp" android:layout_width="match_parent" android:layout_weight="0" /> </LinearLayout> - <LinearLayout android:id="@+id/rot270" + <View android:id="@+id/deadzone" + android:layout_width="@dimen/navigation_bar_deadzone_size" android:layout_height="match_parent" - android:layout_width="match_parent" - android:orientation="vertical" - android:visibility="gone" - > - - <!-- not used --> - </LinearLayout> - + android:layout_gravity="left" + android:clickable="true" + /> </FrameLayout> + + <!-- not used --> + <View android:id="@+id/rot270" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:visibility="gone" + /> + </com.android.systemui.statusbar.phone.NavigationBarView> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 7a4ac5d816f6..5298f2e66927 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -36,10 +36,6 @@ <!-- Whether or not we show the number in the bar. --> <bool name="config_statusBarShowNumber">true</bool> - <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be - autodetected from the Configuration. --> - <bool name="config_showNavigationBar">false</bool> - <!-- How many icons may be shown at once in the system bar. Includes any slots that may be reused for things like IME control. --> <integer name="config_maxNotificationIcons">5</integer> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 3944c203cc3d..da28e1eac25d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -40,7 +40,11 @@ <dimen name="peek_window_y_offset">-12dp</dimen> <!-- thickness (height) of the navigation bar on phones that require it --> - <dimen name="navigation_bar_size">32dp</dimen> + <dimen name="navigation_bar_size">48dp</dimen> + + <!-- thickness (height) of the dead zone at the top of the navigation bar, + reducing false presses on navbar buttons; approx 2mm --> + <dimen name="navigation_bar_deadzone_size">12dp</dimen> <!-- thickness (height) of each notification row, including any separators or padding --> <dimen name="notification_height">65dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 86e0cd014302..70f9b7521959 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -165,4 +165,9 @@ <string name="use_ptp_button_title">Mount as a camera (PTP)</string> <!-- Label for the installer CD image option in UsbPreferenceActivity. [CHAR LIMIT=50] --> <string name="installer_cd_button_title">Install Android File Transfer application for Mac</string> + + <!-- toast message displayed when a screenshot is saved to the Gallery. --> + <string name="screenshot_saving_toast">Screenshot saved to Gallery</string> + <!-- toast message displayed when we fail to take a screenshot. --> + <string name="screenshot_failed_toast">Could not save screenshot</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java new file mode 100644 index 000000000000..83a5578cfa36 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -0,0 +1,384 @@ +/* + * 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.systemui.screenshot; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.app.Activity; +import android.content.ContentValues; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.PixelFormat; +import android.media.MediaScannerConnection; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Binder; +import android.os.Environment; +import android.os.ServiceManager; +import android.provider.MediaStore; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Display; +import android.view.IWindowManager; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.android.systemui.R; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.Thread; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * POD used in the AsyncTask which saves an image in the background. + */ +class SaveImageInBackgroundData { + Context context; + Bitmap image; + int result; +} + +/** + * An AsyncTask that saves an image to the media store in the background. + */ +class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void, + SaveImageInBackgroundData> { + private static final String TAG = "SaveImageInBackgroundTask"; + private static final String SCREENSHOTS_DIR_NAME = "Screenshots"; + private static final String SCREENSHOT_FILE_PATH_TEMPLATE = "%s/%s/Screenshot_%s-%d.png"; + + @Override + protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) { + if (params.length != 1) return null; + + Context context = params[0].context; + Bitmap image = params[0].image; + + try{ + long currentTime = System.currentTimeMillis(); + String date = new SimpleDateFormat("MM-dd-yy-kk-mm-ss").format(new Date(currentTime)); + String imageDir = Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES).getAbsolutePath(); + String imageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, + imageDir, SCREENSHOTS_DIR_NAME, + date, currentTime % 1000); + + // Save the screenshot to the MediaStore + ContentValues values = new ContentValues(); + values.put(MediaStore.Images.ImageColumns.DATA, imageFilePath); + values.put(MediaStore.Images.ImageColumns.TITLE, "Screenshot"); + values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "Screenshot"); + values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, currentTime); + values.put(MediaStore.Images.ImageColumns.DATE_ADDED, currentTime); + values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, currentTime); + values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png"); + Uri uri = context.getContentResolver().insert( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + + OutputStream out = context.getContentResolver().openOutputStream(uri); + image.compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + out.close(); + + params[0].result = 0; + }catch(IOException e){ + params[0].result = 1; + } + + return params[0]; + }; + + @Override + protected void onPostExecute(SaveImageInBackgroundData params) { + if (params.result > 0) { + // Show a message that we've failed to save the image to disk + Toast.makeText(params.context, R.string.screenshot_failed_toast, + Toast.LENGTH_SHORT).show(); + } else { + // Show a message that we've saved the screenshot to disk + Toast.makeText(params.context, R.string.screenshot_saving_toast, + Toast.LENGTH_SHORT).show(); + } + }; +} + +/** + * TODO: + * - Performance when over gl surfaces? Ie. Gallery + * - what do we say in the Toast? Which icon do we get if the user uses another + * type of gallery? + */ +class GlobalScreenshot { + private static final String TAG = "GlobalScreenshot"; + private static final int SCREENSHOT_FADE_IN_DURATION = 900; + private static final int SCREENSHOT_FADE_OUT_DELAY = 1000; + private static final int SCREENSHOT_FADE_OUT_DURATION = 450; + private static final int TOAST_FADE_IN_DURATION = 500; + private static final int TOAST_FADE_OUT_DELAY = 1000; + private static final int TOAST_FADE_OUT_DURATION = 500; + private static final float BACKGROUND_ALPHA = 0.65f; + private static final float SCREENSHOT_SCALE = 0.85f; + private static final float SCREENSHOT_MIN_SCALE = 0.7f; + private static final float SCREENSHOT_ROTATION = -6.75f; // -12.5f; + + private Context mContext; + private LayoutInflater mLayoutInflater; + private IWindowManager mIWindowManager; + private WindowManager mWindowManager; + private WindowManager.LayoutParams mWindowLayoutParams; + private Display mDisplay; + private DisplayMetrics mDisplayMetrics; + private Matrix mDisplayMatrix; + + private Bitmap mScreenBitmap; + private View mScreenshotLayout; + private ImageView mBackgroundView; + private FrameLayout mScreenshotContainerView; + private ImageView mScreenshotView; + + private AnimatorSet mScreenshotAnimation; + + // General use cubic interpolator + final TimeInterpolator mCubicInterpolator = new TimeInterpolator() { + public float getInterpolation(float t) { + return t*t*t; + } + }; + // The interpolator used to control the background alpha at the start of the animation + final TimeInterpolator mBackgroundViewAlphaInterpolator = new TimeInterpolator() { + public float getInterpolation(float t) { + float tStep = 0.35f; + if (t < tStep) { + return t * (1f / tStep); + } else { + return 1f; + } + } + }; + + /** + * @param context everything needs a context :( + */ + public GlobalScreenshot(Context context) { + mContext = context; + mLayoutInflater = (LayoutInflater) + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + // Inflate the screenshot layout + mDisplayMetrics = new DisplayMetrics(); + mDisplayMatrix = new Matrix(); + mScreenshotLayout = mLayoutInflater.inflate(R.layout.global_screenshot, null); + mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background); + mScreenshotContainerView = (FrameLayout) mScreenshotLayout.findViewById(R.id.global_screenshot_container); + mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot); + mScreenshotLayout.setFocusable(true); + mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + // Intercept and ignore all touch events + return true; + } + }); + + // Setup the window that we are going to use + mIWindowManager = IWindowManager.Stub.asInterface( + ServiceManager.getService(Context.WINDOW_SERVICE)); + mWindowLayoutParams = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0, + WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY, + WindowManager.LayoutParams.FLAG_FULLSCREEN + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED_SYSTEM + | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED, + PixelFormat.TRANSLUCENT); + mWindowLayoutParams.token = new Binder(); + mWindowLayoutParams.setTitle("ScreenshotAnimation"); + mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mDisplay = mWindowManager.getDefaultDisplay(); + } + + /** + * Creates a new worker thread and saves the screenshot to the media store. + */ + private void saveScreenshotInWorkerThread() { + SaveImageInBackgroundData data = new SaveImageInBackgroundData(); + data.context = mContext; + data.image = mScreenBitmap; + new SaveImageInBackgroundTask().execute(data); + } + + /** + * @return the current display rotation in degrees + */ + private float getDegreesForRotation(int value) { + switch (value) { + case Surface.ROTATION_90: + return 90f; + case Surface.ROTATION_180: + return 180f; + case Surface.ROTATION_270: + return 270f; + } + return 0f; + } + + /** + * Takes a screenshot of the current display and shows an animation. + */ + void takeScreenshot() { + // We need to orient the screenshot correctly (and the Surface api seems to take screenshots + // only in the natural orientation of the device :!) + mDisplay.getRealMetrics(mDisplayMetrics); + float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels}; + float degrees = getDegreesForRotation(mDisplay.getRotation()); + boolean requiresRotation = (degrees > 0); + if (requiresRotation) { + // Get the dimensions of the device in its native orientation + mDisplayMatrix.reset(); + mDisplayMatrix.preRotate(-degrees); + mDisplayMatrix.mapPoints(dims); + dims[0] = Math.abs(dims[0]); + dims[1] = Math.abs(dims[1]); + } + mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]); + if (requiresRotation) { + // Rotate the screenshot to the current orientation + Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels, + mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(ss); + c.translate(ss.getWidth() / 2, ss.getHeight() / 2); + c.rotate(360f - degrees); + c.translate(-dims[0] / 2, -dims[1] / 2); + c.drawBitmap(mScreenBitmap, 0, 0, null); + mScreenBitmap = ss; + } + + // If we couldn't take the screenshot, notify the user + if (mScreenBitmap == null) { + Toast.makeText(mContext, R.string.screenshot_failed_toast, + Toast.LENGTH_SHORT).show(); + return; + } + + // Start the post-screenshot animation + startAnimation(); + } + + + /** + * Starts the animation after taking the screenshot + */ + private void startAnimation() { + // Add the view for the animation + mScreenshotView.setImageBitmap(mScreenBitmap); + mScreenshotLayout.requestFocus(); + + // Setup the animation with the screenshot just taken + if (mScreenshotAnimation != null) { + mScreenshotAnimation.end(); + } + + mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); + ValueAnimator screenshotFadeInAnim = createScreenshotFadeInAnimation(); + ValueAnimator screenshotFadeOutAnim = createScreenshotFadeOutAnimation(); + mScreenshotAnimation = new AnimatorSet(); + mScreenshotAnimation.play(screenshotFadeInAnim).before(screenshotFadeOutAnim); + mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Save the screenshot once we have a bit of time now + saveScreenshotInWorkerThread(); + + mWindowManager.removeView(mScreenshotLayout); + } + }); + mScreenshotAnimation.start(); + } + private ValueAnimator createScreenshotFadeInAnimation() { + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.setInterpolator(mCubicInterpolator); + anim.setDuration(SCREENSHOT_FADE_IN_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mBackgroundView.setVisibility(View.VISIBLE); + mScreenshotContainerView.setVisibility(View.VISIBLE); + } + }); + anim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = ((Float) animation.getAnimatedValue()).floatValue(); + mBackgroundView.setAlpha(mBackgroundViewAlphaInterpolator.getInterpolation(t) * + BACKGROUND_ALPHA); + float scaleT = SCREENSHOT_SCALE + (1f - t) * SCREENSHOT_SCALE; + mScreenshotContainerView.setAlpha(t*t*t*t); + mScreenshotContainerView.setScaleX(scaleT); + mScreenshotContainerView.setScaleY(scaleT); + mScreenshotContainerView.setRotation(t * SCREENSHOT_ROTATION); + } + }); + return anim; + } + private ValueAnimator createScreenshotFadeOutAnimation() { + ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f); + anim.setInterpolator(mCubicInterpolator); + anim.setStartDelay(SCREENSHOT_FADE_OUT_DELAY); + anim.setDuration(SCREENSHOT_FADE_OUT_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBackgroundView.setVisibility(View.GONE); + mScreenshotContainerView.setVisibility(View.GONE); + } + }); + anim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = ((Float) animation.getAnimatedValue()).floatValue(); + float scaleT = SCREENSHOT_MIN_SCALE + + t*(SCREENSHOT_SCALE - SCREENSHOT_MIN_SCALE); + mScreenshotContainerView.setAlpha(t); + mScreenshotContainerView.setScaleX(scaleT); + mScreenshotContainerView.setScaleY(scaleT); + mBackgroundView.setAlpha(t * t * BACKGROUND_ALPHA); + } + }); + return anim; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java new file mode 100644 index 000000000000..35eaedf1f941 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -0,0 +1,50 @@ +/* + * 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.systemui.screenshot; + +import android.app.Service; +import android.app.AlertDialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbManager; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; + +import com.android.systemui.R; + +public class TakeScreenshotService extends Service { + private static final String TAG = "TakeScreenshotService"; + + private static GlobalScreenshot mScreenshot; + + @Override + public IBinder onBind(Intent intent) { + if (mScreenshot == null) { + mScreenshot = new GlobalScreenshot(this); + } + mScreenshot.takeScreenshot(); + return null; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 22181b8337ae..550fc57e9a78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -36,13 +36,14 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; public class NavigationBarView extends LinearLayout { + final static boolean DEBUG_DEADZONE = false; + final static boolean NAVBAR_ALWAYS_AT_RIGHT = true; protected IStatusBarService mBarService; final Display mDisplay; View mCurrentView = null; View[] mRotatedViews = new View[4]; - View mBackground; Animator mLastAnimator = null; public View getRecentsButton() { @@ -74,13 +75,13 @@ public class NavigationBarView extends LinearLayout { } private void setLights(final boolean on) { - float oldAlpha = mBackground.getAlpha(); + float oldAlpha = mCurrentView.getAlpha(); android.util.Log.d("NavigationBarView", "animating alpha: " + oldAlpha + " -> " + (on ? 1f : 0f)); if (mLastAnimator != null && mLastAnimator.isRunning()) mLastAnimator.cancel(); - mLastAnimator = ObjectAnimator.ofFloat(mBackground, "alpha", oldAlpha, on ? 1f : 0f) + mLastAnimator = ObjectAnimator.ofFloat(mCurrentView, "alpha", oldAlpha, on ? 1f : 0f) .setDuration(on ? 250 : 1500); mLastAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -92,8 +93,6 @@ public class NavigationBarView extends LinearLayout { } public void onFinishInflate() { - mBackground = findViewById(R.id.background); - mRotatedViews[Surface.ROTATION_0] = mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0); @@ -121,6 +120,10 @@ public class NavigationBarView extends LinearLayout { mCurrentView = mRotatedViews[rot]; mCurrentView.setVisibility(View.VISIBLE); + if (DEBUG_DEADZONE) { + mCurrentView.findViewById(R.id.deadzone).setBackgroundColor(0x808080FF); + } + android.util.Log.d("NavigationBarView", "reorient(): rot=" + mDisplay.getRotation()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index d8474db3c2ef..4c7b0dd2ca54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -246,7 +246,7 @@ public class PhoneStatusBar extends StatusBar { mIntruderAlertView.setClickable(true); try { - boolean showNav = res.getBoolean(R.bool.config_showNavigationBar); + boolean showNav = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar); if (showNav) { mNavigationBarView = (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null); diff --git a/packages/VpnDialogs/Android.mk b/packages/VpnDialogs/Android.mk index 89f010aaebcb..ac84125f8f4e 100644 --- a/packages/VpnDialogs/Android.mk +++ b/packages/VpnDialogs/Android.mk @@ -20,6 +20,8 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional +LOCAL_CERTIFICATE := platform + LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := VpnDialogs diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml index 4e6784ce0884..c0b0a08067cf 100644 --- a/packages/VpnDialogs/AndroidManifest.xml +++ b/packages/VpnDialogs/AndroidManifest.xml @@ -1,5 +1,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.vpndialogs"> + package="com.android.vpndialogs" + android:sharedUserId="android.uid.system"> <application android:label="VpnDialogs"> <activity android:name=".ConfirmDialog" diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml index 8186e2629173..df6d36bd2814 100644 --- a/packages/VpnDialogs/res/values/strings.xml +++ b/packages/VpnDialogs/res/values/strings.xml @@ -29,6 +29,7 @@ <string name="accept">I trust this application.</string> + <string name="legacy_title">VPN is connected</string> <string name="configure">Configure</string> <string name="disconnect">Disconnect</string> diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java index ba3f3448f066..c076ba0bfd2f 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java @@ -64,9 +64,6 @@ public class ManageDialog extends Activity implements Handler.Callback, mService = IConnectivityManager.Stub.asInterface( ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - PackageManager pm = getPackageManager(); - ApplicationInfo app = pm.getApplicationInfo(mConfig.packageName, 0); - View view = View.inflate(this, R.layout.manage, null); if (mConfig.sessionName != null) { ((TextView) view.findViewById(R.id.session)).setText(mConfig.sessionName); @@ -75,15 +72,29 @@ public class ManageDialog extends Activity implements Handler.Callback, mDataTransmitted = (TextView) view.findViewById(R.id.data_transmitted); mDataReceived = (TextView) view.findViewById(R.id.data_received); - mDialog = new AlertDialog.Builder(this) - .setIcon(app.loadIcon(pm)) - .setTitle(app.loadLabel(pm)) - .setView(view) - .setNeutralButton(R.string.disconnect, this) - .setNegativeButton(android.R.string.cancel, this) - .create(); + if (mConfig.packageName == null) { + // Legacy VPN does not have a package name. + mDialog = new AlertDialog.Builder(this) + .setIcon(android.R.drawable.ic_dialog_info) + .setTitle(R.string.legacy_title) + .setView(view) + .setNeutralButton(R.string.disconnect, this) + .setNegativeButton(android.R.string.cancel, this) + .create(); + } else { + PackageManager pm = getPackageManager(); + ApplicationInfo app = pm.getApplicationInfo(mConfig.packageName, 0); + + mDialog = new AlertDialog.Builder(this) + .setIcon(app.loadIcon(pm)) + .setTitle(app.loadLabel(pm)) + .setView(view) + .setNeutralButton(R.string.disconnect, this) + .setNegativeButton(android.R.string.cancel, this) + .create(); + } - if (mConfig.configureActivity != null) { + if (mConfig.configureIntent != null) { mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getText(R.string.configure), this); } @@ -113,9 +124,7 @@ public class ManageDialog extends Activity implements Handler.Callback, public void onClick(DialogInterface dialog, int which) { try { if (which == AlertDialog.BUTTON_POSITIVE) { - Intent intent = new Intent(); - intent.setClassName(mConfig.packageName, mConfig.configureActivity); - startActivity(intent); + mConfig.configureIntent.send(); } else if (which == AlertDialog.BUTTON_NEUTRAL) { mService.prepareVpn(""); } diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java index 8b7a61e21ce7..1c4084c77cd1 100644 --- a/policy/src/com/android/internal/policy/impl/LockScreen.java +++ b/policy/src/com/android/internal/policy/impl/LockScreen.java @@ -64,7 +64,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen, private KeyguardUpdateMonitor mUpdateMonitor; private KeyguardScreenCallback mCallback; - private SlidingTab mSlidingTab; private TextView mScreenLocked; private TextView mEmergencyCallText; private Button mEmergencyCallButton; @@ -89,11 +88,9 @@ class LockScreen extends LinearLayout implements KeyguardScreen, private boolean mEnableMenuKeyInLockScreen; private StatusView mStatusView; - private WaveView mEnergyWave; - private SlidingTabMethods mSlidingTabMethods; - private WaveViewMethods mWaveViewMethods; - private MultiWaveView mMultiWaveView; - private MultiWaveViewMethods mMultiWaveViewMethods; + private UnlockWidgetCommonMethods mUnlockWidgetMethods; + private View mUnlockWidget; + /** * The status of this lock screen. @@ -151,9 +148,28 @@ class LockScreen extends LinearLayout implements KeyguardScreen, } } - class SlidingTabMethods implements SlidingTab.OnTriggerListener { + private interface UnlockWidgetCommonMethods { + // Update resources based on phone state + public void updateResources(); + + // Get the view associated with this widget + public View getView(); - private void updateRightTabResources() { + // Reset the view + public void reset(boolean animate); + + // Animate the widget if it supports ping() + public void ping(); + } + + class SlidingTabMethods implements SlidingTab.OnTriggerListener, UnlockWidgetCommonMethods { + private final SlidingTab mSlidingTab; + + SlidingTabMethods(SlidingTab slidingTab) { + mSlidingTab = slidingTab; + } + + public void updateResources() { boolean vibe = mSilentMode && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); @@ -175,7 +191,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mCallback.goToUnlockScreen(); } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) { toggleRingMode(); - updateRightTabResources(); doSilenceRingToast(); mCallback.pokeWakelock(); } @@ -195,12 +210,29 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mCallback.pokeWakelock(); } } + + public View getView() { + return mSlidingTab; + } + + public void reset(boolean animate) { + mSlidingTab.reset(animate); + } + + public void ping() { + } } private static final int WAIT_FOR_ANIMATION_TIMEOUT = 0; private static final int STAY_ON_WHILE_GRABBED_TIMEOUT = 30000; - class WaveViewMethods implements WaveView.OnTriggerListener { + class WaveViewMethods implements WaveView.OnTriggerListener, UnlockWidgetCommonMethods { + + private final WaveView mWaveView; + + WaveViewMethods(WaveView waveView) { + mWaveView = waveView; + } /** {@inheritDoc} */ public void onTrigger(View v, int whichHandle) { if (whichHandle == WaveView.OnTriggerListener.CENTER_HANDLE) { @@ -210,8 +242,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen, /** {@inheritDoc} */ public void onGrabbedStateChange(View v, int grabbedState) { - if (DBG) Log.v(TAG, "*** LockScreen accel is " - + (mEnergyWave.isHardwareAccelerated() ? "on":"off")); // Don't poke the wake lock when returning to a state where the handle is // not grabbed since that can happen when the system (instead of the user) // cancels the grab. @@ -219,30 +249,51 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT); } } + + public void updateResources() { + } + + public View getView() { + return mWaveView; + } + public void reset(boolean animate) { + mWaveView.reset(); + } + public void ping() { + } } - class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener { + class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener, + UnlockWidgetCommonMethods { + + private final MultiWaveView mMultiWaveView; + + MultiWaveViewMethods(MultiWaveView multiWaveView) { + mMultiWaveView = multiWaveView; + } + + public void updateResources() { + mMultiWaveView.setTargetResources(mSilentMode ? R.array.lockscreen_targets_when_silent + : R.array.lockscreen_targets_when_soundon); + } + public void onGrabbed(View v, int handle) { } + public void onReleased(View v, int handle) { } + public void onTrigger(View v, int target) { if (target == 0) { // TODO: Use resources to determine which handle was used mCallback.goToUnlockScreen(); } else if (target == 2) { toggleRingMode(); - updateResources(); doSilenceRingToast(); + mUnlockWidgetMethods.updateResources(); mCallback.pokeWakelock(); } - - } - - private void updateResources() { - mMultiWaveView.setTargetResources(mSilentMode ? R.array.lockscreen_targets_when_silent - : R.array.lockscreen_targets_when_soundon); } public void onGrabbedStateChange(View v, int handle) { @@ -253,6 +304,18 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mCallback.pokeWakelock(); } } + + public View getView() { + return mMultiWaveView; + } + + public void reset(boolean animate) { + mMultiWaveView.reset(animate); + } + + public void ping() { + mMultiWaveView.ping(); + } } private void requestUnlockScreen() { @@ -371,32 +434,39 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); mSilentMode = isSilentMode(); - View unlockWidget = findViewById(R.id.unlock_widget); - if (unlockWidget instanceof SlidingTab) { - mSlidingTab = (SlidingTab) unlockWidget; - mSlidingTab.setHoldAfterTrigger(true, false); - mSlidingTab.setLeftHintText(R.string.lockscreen_unlock_label); - mSlidingTab.setLeftTabResources( + mUnlockWidget = findViewById(R.id.unlock_widget); + if (mUnlockWidget instanceof SlidingTab) { + SlidingTab slidingTabView = (SlidingTab) mUnlockWidget; + slidingTabView.setHoldAfterTrigger(true, false); + slidingTabView.setLeftHintText(R.string.lockscreen_unlock_label); + slidingTabView.setLeftTabResources( R.drawable.ic_jog_dial_unlock, R.drawable.jog_tab_target_green, R.drawable.jog_tab_bar_left_unlock, R.drawable.jog_tab_left_unlock); - mSlidingTabMethods = new SlidingTabMethods(); - mSlidingTab.setOnTriggerListener(mSlidingTabMethods); - mSlidingTabMethods.updateRightTabResources(); - } else if (unlockWidget instanceof WaveView) { - mEnergyWave = (WaveView) unlockWidget; - mWaveViewMethods = new WaveViewMethods(); - mEnergyWave.setOnTriggerListener(mWaveViewMethods); - } else if (unlockWidget instanceof MultiWaveView) { - mMultiWaveView = (MultiWaveView) unlockWidget; - mMultiWaveViewMethods = new MultiWaveViewMethods(); - mMultiWaveViewMethods.updateResources(); // update silence/ring resources - mMultiWaveView.setOnTriggerListener(mMultiWaveViewMethods); + SlidingTabMethods slidingTabMethods = new SlidingTabMethods(slidingTabView); + slidingTabView.setOnTriggerListener(slidingTabMethods); + mUnlockWidgetMethods = slidingTabMethods; + } else if (mUnlockWidget instanceof WaveView) { + WaveView waveView = (WaveView) mUnlockWidget; + WaveViewMethods waveViewMethods = new WaveViewMethods(waveView); + waveView.setOnTriggerListener(waveViewMethods); + mUnlockWidgetMethods = waveViewMethods; + } else if (mUnlockWidget instanceof MultiWaveView) { + MultiWaveView multiWaveView = (MultiWaveView) mUnlockWidget; + MultiWaveViewMethods multiWaveViewMethods = new MultiWaveViewMethods(multiWaveView); + multiWaveView.setOnTriggerListener(multiWaveViewMethods); + mUnlockWidgetMethods = multiWaveViewMethods; } else { - throw new IllegalStateException("Unrecognized unlock widget: " + unlockWidget); + throw new IllegalStateException("Unrecognized unlock widget: " + mUnlockWidget); } + // Update widget with initial ring state + mUnlockWidgetMethods.updateResources(); + + if (DBG) Log.v(TAG, "*** LockScreen accel is " + + (mUnlockWidget.isHardwareAccelerated() ? "on":"off")); + resetStatusInfo(updateMonitor); } @@ -540,16 +610,14 @@ class LockScreen extends LinearLayout implements KeyguardScreen, * Enables unlocking of this screen. Typically just shows the unlock widget. */ private void enableUnlock() { - if (mEnergyWave != null) mEnergyWave.setVisibility(View.VISIBLE); - if (mSlidingTab != null) mSlidingTab.setVisibility(View.VISIBLE); + mUnlockWidgetMethods.getView().setVisibility(View.VISIBLE); } /** * Disable unlocking of this screen. Typically just hides the unlock widget. */ private void disableUnlock() { - if (mEnergyWave != null) mEnergyWave.setVisibility(View.GONE); - if (mSlidingTab != null) mSlidingTab.setVisibility(View.GONE); + mUnlockWidgetMethods.getView().setVisibility(View.GONE); } /** @@ -728,20 +796,13 @@ class LockScreen extends LinearLayout implements KeyguardScreen, /** {@inheritDoc} */ public void onPause() { - if (mEnergyWave != null) { - mEnergyWave.reset(); - } - if (mMultiWaveView != null) { - mMultiWaveView.reset(false); - } + mUnlockWidgetMethods.reset(false); } /** {@inheritDoc} */ public void onResume() { resetStatusInfo(mUpdateMonitor); - if (mMultiWaveView != null) { - mMultiWaveView.ping(); - } + mUnlockWidgetMethods.ping(); } /** {@inheritDoc} */ @@ -757,7 +818,7 @@ class LockScreen extends LinearLayout implements KeyguardScreen, boolean silent = AudioManager.RINGER_MODE_NORMAL != state; if (silent != mSilentMode) { mSilentMode = silent; - if (mSlidingTabMethods != null) mSlidingTabMethods.updateRightTabResources(); + mUnlockWidgetMethods.updateResources(); } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index b52e7e1c42a2..ad6cebb60b78 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -28,6 +28,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; @@ -372,6 +373,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { // What we do when the user long presses on home private int mLongPressOnHomeBehavior = -1; + // Screenshot trigger states + private boolean mVolumeDownTriggered; + private boolean mPowerDownTriggered; + ShortcutManager mShortcutManager; PowerManager.WakeLock mBroadcastWakeLock; @@ -2339,6 +2344,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void takeScreenshot() { + mHandler.post(new Runnable() { + @Override + public void run() { + ComponentName cn = new ComponentName("com.android.systemui", + "com.android.systemui.screenshot.TakeScreenshotService"); + Intent intent = new Intent(); + intent.setComponent(cn); + ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) {} + @Override + public void onServiceDisconnected(ComponentName name) {} + }; + mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE); + mContext.unbindService(conn); + } + }); + } + /** {@inheritDoc} */ @Override public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { @@ -2398,6 +2423,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Handle special keys. switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: + if (down) { + // If the power key down was already triggered, take the screenshot + if (mPowerDownTriggered) { + // Dismiss the power-key longpress + mHandler.removeCallbacks(mPowerLongPress); + mPowerKeyHandled = true; + + // Take the screenshot + takeScreenshot(); + + // Prevent the event from being passed through to the current activity + result &= ~ACTION_PASS_TO_USER; + break; + } + mVolumeDownTriggered = true; + } else { + mVolumeDownTriggered = false; + } case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_MUTE: { if (down) { @@ -2478,6 +2521,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_POWER: { result &= ~ACTION_PASS_TO_USER; if (down) { + // If the volume down key has been triggered, then just take the screenshot + if (mVolumeDownTriggered) { + // Take the screenshot + takeScreenshot(); + mPowerKeyHandled = true; + + // Prevent the event from being passed through to the current activity + break; + } + mPowerDownTriggered = true; + + ITelephony telephonyService = getTelephonyService(); boolean hungUp = false; if (telephonyService != null) { @@ -2499,6 +2554,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } interceptPowerKeyDown(!isScreenOn || hungUp); } else { + mPowerDownTriggered = false; if (interceptPowerKeyUp(canceled)) { result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP; } diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 95b8a5723e2f..ca2540bfad20 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -212,8 +212,8 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, struct input_absinfo info; if(ioctl(device->fd, EVIOCGABS(axis), &info)) { - LOGW("Error reading absolute controller %d for device %s fd %d\n", - axis, device->identifier.name.string(), device->fd); + LOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", + axis, device->identifier.name.string(), device->fd, errno); return -errno; } @@ -335,6 +335,33 @@ int32_t EventHub::getSwitchStateLocked(Device* device, int32_t sw) const { return AKEY_STATE_UNKNOWN; } +status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const { + if (axis >= 0 && axis <= ABS_MAX) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device != NULL) { + return getAbsoluteAxisValueLocked(device, axis, outValue); + } + } + *outValue = 0; + return -1; +} + +status_t EventHub::getAbsoluteAxisValueLocked(Device* device, int32_t axis, + int32_t* outValue) const { + struct input_absinfo info; + + if(ioctl(device->fd, EVIOCGABS(axis), &info)) { + LOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", + axis, device->identifier.name.string(), device->fd, errno); + return -errno; + } + + *outValue = info.value; + return OK; +} + bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { AutoMutex _l(mLock); diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 0a34e45a565c..695dfdfb25e6 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -186,6 +186,8 @@ public: virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0; virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0; virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0; + virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, + int32_t* outValue) const = 0; /* * Examine key input devices for specific framework keycode support @@ -237,6 +239,7 @@ public: virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const; virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const; virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const; + virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const; virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; @@ -305,6 +308,7 @@ private: int32_t getScanCodeStateLocked(Device* device, int32_t scanCode) const; int32_t getKeyCodeStateLocked(Device* device, int32_t keyCode) const; int32_t getSwitchStateLocked(Device* device, int32_t sw) const; + int32_t getAbsoluteAxisValueLocked(Device* device, int32_t axis, int32_t* outValue) const; bool markSupportedKeyCodesLocked(Device* device, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 4a50d8a7eda8..10b9083549f2 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -569,9 +569,9 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR break; case DROP_REASON_BLOCKED: LOGI("Dropped event because the current application is not responding and the user " - "has started interating with a different application."); + "has started interacting with a different application."); reason = "inbound event was dropped because the current application is not responding " - "and the user has started interating with a different application"; + "and the user has started interacting with a different application"; break; case DROP_REASON_STALE: LOGI("Dropped event because it is stale."); @@ -1239,7 +1239,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const InputWindow* newHoverWindow = NULL; bool isSplit = mTouchState.split; - bool wrongDevice = mTouchState.down + bool switchedDevice = mTouchState.deviceId >= 0 && (mTouchState.deviceId != entry->deviceId || mTouchState.source != entry->source); bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE @@ -1248,28 +1248,27 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, 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 +1597,38 @@ Failed: // Update final pieces of touch state if the injector had permission. if (injectionPermission == INJECTION_PERMISSION_GRANTED) { if (!wrongDevice) { - if (maskedAction == AMOTION_EVENT_ACTION_UP - || maskedAction == AMOTION_EVENT_ACTION_CANCEL - || isHoverAction) { + if (switchedDevice) { +#if DEBUG_FOCUS + LOGD("Conflicting pointer actions: Switched to a different device."); +#endif + *outConflictingPointerActions = true; + } + + if (isHoverAction) { + // Started hovering, therefore no longer down. + if (mTouchState.down) { +#if DEBUG_FOCUS + LOGD("Conflicting pointer actions: Hover received while pointer was down."); +#endif + *outConflictingPointerActions = true; + } + mTouchState.reset(); + if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER + || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { + mTouchState.deviceId = entry->deviceId; + mTouchState.source = entry->source; + } + } else if (maskedAction == AMOTION_EVENT_ACTION_UP + || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { // All pointers up or canceled. mTouchState.reset(); } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. if (mTouchState.down) { - *outConflictingPointerActions = true; #if DEBUG_FOCUS - LOGD("Pointer down received while already down."); + LOGD("Conflicting pointer actions: Down received while already down."); #endif + *outConflictingPointerActions = true; } mTouchState.copyFrom(mTempTouchState); } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { @@ -1868,6 +1887,18 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, return; } + // If the motion event was modified in flight, then we cannot stream the sample. + if ((motionEventDispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_MASK) + != InputTarget::FLAG_DISPATCH_AS_IS) { +#if DEBUG_BATCHING + LOGD("channel '%s' ~ Not streaming because the motion event was not " + "being dispatched as-is. " + "(Waiting for next dispatch cycle to start.)", + connection->getInputChannelName()); +#endif + return; + } + // The dispatch entry is in progress and is still potentially open for streaming. // Try to stream the new motion sample. This might fail if the consumer has already // consumed the motion event (or if the channel is broken). @@ -1972,6 +2003,66 @@ void InputDispatcher::enqueueDispatchEntryLocked( dispatchEntry->headMotionSample = appendedMotionSample; } + // Apply target flags and update the connection's input state. + switch (eventEntry->type) { + case EventEntry::TYPE_KEY: { + KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); + dispatchEntry->resolvedAction = keyEntry->action; + dispatchEntry->resolvedFlags = keyEntry->flags; + + if (!connection->inputState.trackKey(keyEntry, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", + connection->getInputChannelName()); +#endif + return; // skip the inconsistent event + } + break; + } + + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); + if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; + } else { + dispatchEntry->resolvedAction = motionEntry->action; + } + if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE + && !connection->inputState.isHovering( + motionEntry->deviceId, motionEntry->source)) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event", + connection->getInputChannelName()); +#endif + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; + } + + dispatchEntry->resolvedFlags = motionEntry->flags; + if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { + dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + } + + if (!connection->inputState.trackMotion(motionEntry, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event", + connection->getInputChannelName()); +#endif + return; // skip the inconsistent event + } + break; + } + } + // Enqueue the dispatch entry. connection->outboundQueue.enqueueAtTail(dispatchEntry); } @@ -1999,16 +2090,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::TYPE_KEY: { KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); - // Apply target flags. - int32_t action = keyEntry->action; - int32_t flags = keyEntry->flags; - - // Update the connection's input state. - connection->inputState.trackKey(keyEntry, action); - // Publish the key event. - status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source, - action, flags, keyEntry->keyCode, keyEntry->scanCode, + status = connection->inputPublisher.publishKeyEvent( + keyEntry->deviceId, keyEntry->source, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, + keyEntry->keyCode, keyEntry->scanCode, keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, keyEntry->eventTime); @@ -2024,24 +2110,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::TYPE_MOTION: { MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); - // Apply target flags. - int32_t action = motionEntry->action; - int32_t flags = motionEntry->flags; - if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { - action = AMOTION_EVENT_ACTION_OUTSIDE; - } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { - action = AMOTION_EVENT_ACTION_HOVER_EXIT; - } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { - action = AMOTION_EVENT_ACTION_HOVER_ENTER; - } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { - action = AMOTION_EVENT_ACTION_CANCEL; - } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { - action = AMOTION_EVENT_ACTION_DOWN; - } - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { - flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - } - // If headMotionSample is non-NULL, then it points to the first new sample that we // were unable to dispatch during the previous cycle so we resume dispatching from // that point in the list of motion samples. @@ -2082,13 +2150,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } } - // Update the connection's input state. - connection->inputState.trackMotion(motionEntry, action); - // Publish the motion event and the first motion sample. - status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId, - motionEntry->source, action, flags, motionEntry->edgeFlags, - motionEntry->metaState, motionEntry->buttonState, + status = connection->inputPublisher.publishMotionEvent( + motionEntry->deviceId, motionEntry->source, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, + motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->downTime, firstMotionSample->eventTime, @@ -2102,8 +2168,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, return; } - if (action == AMOTION_EVENT_ACTION_MOVE - || action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_MOVE + || dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { // Append additional motion samples. MotionSample* nextMotionSample = firstMotionSample->next; for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) { @@ -2355,23 +2421,22 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( break; } - int32_t xOffset, yOffset; - float scaleFactor; + InputTarget target; const InputWindow* window = getWindowLocked(connection->inputChannel); if (window) { - xOffset = -window->frameLeft; - yOffset = -window->frameTop; - scaleFactor = window->scaleFactor; + target.xOffset = -window->frameLeft; + target.yOffset = -window->frameTop; + target.scaleFactor = window->scaleFactor; } else { - xOffset = 0; - yOffset = 0; - scaleFactor = 1.0f; + target.xOffset = 0; + target.yOffset = 0; + target.scaleFactor = 1.0f; } + target.inputChannel = connection->inputChannel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - DispatchEntry* cancelationDispatchEntry = - mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref - 0, xOffset, yOffset, scaleFactor); - connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry); + enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref + &target, false, InputTarget::FLAG_DISPATCH_AS_IS); mAllocator.releaseEventEntry(cancelationEventEntry); } @@ -3327,6 +3392,7 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { resetTargetsLocked(); mTouchState.reset(); + mLastHoverWindow = NULL; } void InputDispatcher::logDispatchStateLocked() { @@ -4125,113 +4191,188 @@ bool InputDispatcher::InputState::isNeutral() const { return mKeyMementos.isEmpty() && mMotionMementos.isEmpty(); } -void InputDispatcher::InputState::trackEvent(const EventEntry* entry, int32_t action) { - switch (entry->type) { - case EventEntry::TYPE_KEY: - trackKey(static_cast<const KeyEntry*>(entry), action); - break; - - case EventEntry::TYPE_MOTION: - trackMotion(static_cast<const MotionEntry*>(entry), action); - break; +bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos.itemAt(i); + if (memento.deviceId == deviceId + && memento.source == source + && memento.hovering) { + return true; + } } + return false; } -void InputDispatcher::InputState::trackKey(const KeyEntry* entry, int32_t action) { - if (action == AKEY_EVENT_ACTION_UP - && (entry->flags & AKEY_EVENT_FLAG_FALLBACK)) { - for (size_t i = 0; i < mFallbackKeys.size(); ) { - if (mFallbackKeys.valueAt(i) == entry->keyCode) { - mFallbackKeys.removeItemsAt(i); - } else { - i += 1; +bool InputDispatcher::InputState::trackKey(const KeyEntry* entry, + int32_t action, int32_t flags) { + switch (action) { + case AKEY_EVENT_ACTION_UP: { + if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) { + for (size_t i = 0; i < mFallbackKeys.size(); ) { + if (mFallbackKeys.valueAt(i) == entry->keyCode) { + mFallbackKeys.removeItemsAt(i); + } else { + i += 1; + } } } + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.removeAt(index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " + "keyCode=%d, scanCode=%d", + entry->deviceId, entry->source, entry->keyCode, entry->scanCode); +#endif + return false; } - for (size_t i = 0; i < mKeyMementos.size(); i++) { - KeyMemento& memento = mKeyMementos.editItemAt(i); - if (memento.deviceId == entry->deviceId - && memento.source == entry->source - && memento.keyCode == entry->keyCode - && memento.scanCode == entry->scanCode) { - switch (action) { - case AKEY_EVENT_ACTION_UP: - mKeyMementos.removeAt(i); - return; - - case AKEY_EVENT_ACTION_DOWN: - mKeyMementos.removeAt(i); - goto Found; - - default: - return; - } + case AKEY_EVENT_ACTION_DOWN: { + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.removeAt(index); } + addKeyMemento(entry, flags); + return true; } -Found: - if (action == AKEY_EVENT_ACTION_DOWN) { - mKeyMementos.push(); - KeyMemento& memento = mKeyMementos.editTop(); - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.keyCode = entry->keyCode; - memento.scanCode = entry->scanCode; - memento.flags = entry->flags; - memento.downTime = entry->downTime; + default: + return true; } } -void InputDispatcher::InputState::trackMotion(const MotionEntry* entry, int32_t action) { +bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, + int32_t action, int32_t flags) { int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; - for (size_t i = 0; i < mMotionMementos.size(); i++) { - MotionMemento& memento = mMotionMementos.editItemAt(i); - if (memento.deviceId == entry->deviceId - && memento.source == entry->source) { - switch (actionMasked) { - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: - case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - case AMOTION_EVENT_ACTION_HOVER_EXIT: - mMotionMementos.removeAt(i); - return; + switch (actionMasked) { + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.removeAt(index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " + "actionMasked=%d", + entry->deviceId, entry->source, actionMasked); +#endif + return false; + } - case AMOTION_EVENT_ACTION_DOWN: - mMotionMementos.removeAt(i); - goto Found; + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.removeAt(index); + } + addMotionMemento(entry, flags, false /*hovering*/); + return true; + } - case AMOTION_EVENT_ACTION_POINTER_UP: - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_MOVE: - memento.setPointers(entry); - return; + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_MOVE: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + MotionMemento& memento = mMotionMementos.editItemAt(index); + memento.setPointers(entry); + return true; + } + if (actionMasked == AMOTION_EVENT_ACTION_MOVE + && (entry->source & (AINPUT_SOURCE_CLASS_JOYSTICK + | AINPUT_SOURCE_CLASS_NAVIGATION))) { + // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP. + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Dropping inconsistent motion pointer up/down or move event: " + "deviceId=%d, source=%08x, actionMasked=%d", + entry->deviceId, entry->source, actionMasked); +#endif + return false; + } - default: - return; - } + case AMOTION_EVENT_ACTION_HOVER_EXIT: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.removeAt(index); + return true; } +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x", + entry->deviceId, entry->source); +#endif + return false; } -Found: - switch (actionMasked) { - case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - case AMOTION_EVENT_ACTION_HOVER_EXIT: - mMotionMementos.push(); - MotionMemento& memento = mMotionMementos.editTop(); - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.xPrecision = entry->xPrecision; - memento.yPrecision = entry->yPrecision; - memento.downTime = entry->downTime; - memento.setPointers(entry); - memento.hovering = actionMasked != AMOTION_EVENT_ACTION_DOWN; + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.removeAt(index); + } + addMotionMemento(entry, flags, true /*hovering*/); + return true; + } + + default: + return true; } } +ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const { + for (size_t i = 0; i < mKeyMementos.size(); i++) { + const KeyMemento& memento = mKeyMementos.itemAt(i); + if (memento.deviceId == entry->deviceId + && memento.source == entry->source + && memento.keyCode == entry->keyCode + && memento.scanCode == entry->scanCode) { + return i; + } + } + return -1; +} + +ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry, + bool hovering) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos.itemAt(i); + if (memento.deviceId == entry->deviceId + && memento.source == entry->source + && memento.hovering == hovering) { + return i; + } + } + return -1; +} + +void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) { + mKeyMementos.push(); + KeyMemento& memento = mKeyMementos.editTop(); + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.keyCode = entry->keyCode; + memento.scanCode = entry->scanCode; + memento.flags = flags; + memento.downTime = entry->downTime; +} + +void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, + int32_t flags, bool hovering) { + mMotionMementos.push(); + MotionMemento& memento = mMotionMementos.editTop(); + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.flags = flags; + memento.xPrecision = entry->xPrecision; + memento.yPrecision = entry->yPrecision; + memento.downTime = entry->downTime; + memento.setPointers(entry); + memento.hovering = hovering; +} + void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { pointerCount = entry->pointerCount; for (uint32_t i = 0; i < entry->pointerCount; i++) { @@ -4243,20 +4384,17 @@ void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator, Vector<EventEntry*>& outEvents, const CancelationOptions& options) { - for (size_t i = 0; i < mKeyMementos.size(); ) { + for (size_t i = 0; i < mKeyMementos.size(); i++) { const KeyMemento& memento = mKeyMementos.itemAt(i); if (shouldCancelKey(memento, options)) { outEvents.push(allocator->obtainKeyEntry(currentTime, memento.deviceId, memento.source, 0, AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode, memento.scanCode, 0, 0, memento.downTime)); - mKeyMementos.removeAt(i); - } else { - i += 1; } } - for (size_t i = 0; i < mMotionMementos.size(); ) { + for (size_t i = 0; i < mMotionMementos.size(); i++) { const MotionMemento& memento = mMotionMementos.itemAt(i); if (shouldCancelMotion(memento, options)) { outEvents.push(allocator->obtainMotionEntry(currentTime, @@ -4264,12 +4402,9 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL, - 0, 0, 0, 0, + memento.flags, 0, 0, 0, memento.xPrecision, memento.yPrecision, memento.downTime, memento.pointerCount, memento.pointerProperties, memento.pointerCoords)); - mMotionMementos.removeAt(i); - } else { - i += 1; } } } diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h index 676d162daaac..bdd1922b9339 100644 --- a/services/input/InputDispatcher.h +++ b/services/input/InputDispatcher.h @@ -522,6 +522,10 @@ private: // True if dispatch has started. bool inProgress; + // Set to the resolved action and flags when the event is enqueued. + int32_t resolvedAction; + int32_t resolvedFlags; + // For motion events: // Pointer to the first motion sample to dispatch in this cycle. // Usually NULL to indicate that the list of motion samples begins at @@ -709,14 +713,19 @@ private: // Returns true if there is no state to be canceled. bool isNeutral() const; - // Records tracking information for an event that has just been published. - void trackEvent(const EventEntry* entry, int32_t action); + // Returns true if the specified source is known to have received a hover enter + // motion event. + bool isHovering(int32_t deviceId, uint32_t source) const; // Records tracking information for a key event that has just been published. - void trackKey(const KeyEntry* entry, int32_t action); + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); // Records tracking information for a motion event that has just been published. - void trackMotion(const MotionEntry* entry, int32_t action); + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); // Synthesizes cancelation events for the current state and resets the tracked state. void synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator, @@ -756,6 +765,7 @@ private: struct MotionMemento { int32_t deviceId; uint32_t source; + int32_t flags; float xPrecision; float yPrecision; nsecs_t downTime; @@ -771,6 +781,12 @@ private: Vector<MotionMemento> mMotionMementos; KeyedVector<int32_t, int32_t> mFallbackKeys; + ssize_t findKeyMemento(const KeyEntry* entry) const; + ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; + + void addKeyMemento(const KeyEntry* entry, int32_t flags); + void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); + static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options); static bool shouldCancelMotion(const MotionMemento& memento, diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 5a25f8cc9584..79218a5581ca 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 { @@ -921,7 +922,7 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) { #if DEBUG_RAW_EVENTS LOGD("Input event: device=%d type=0x%04x scancode=0x%04x " - "keycode=0x%04x value=0x%04x flags=0x%08x", + "keycode=0x%04x value=0x%08x flags=0x%08x", rawEvent->deviceId, rawEvent->type, rawEvent->scanCode, rawEvent->keyCode, rawEvent->value, rawEvent->flags); #endif @@ -1921,8 +1922,17 @@ void TouchInputMapper::dump(String8& dump) { dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mLocked.distanceScale); dump.appendFormat(INDENT3 "Last Touch:\n"); - dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount); dump.appendFormat(INDENT4 "Button State: 0x%08x\n", mLastTouch.buttonState); + dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount); + for (uint32_t i = 0; i < mLastTouch.pointerCount; i++) { + const PointerData& pointer = mLastTouch.pointers[i]; + dump.appendFormat(INDENT5 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " + "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " + "orientation=%d, distance=%d, isStylus=%s\n", i, + pointer.id, pointer.x, pointer.y, pointer.pressure, + pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, pointer.toolMinor, + pointer.orientation, pointer.distance, toString(pointer.isStylus)); + } if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n"); @@ -3704,6 +3714,29 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); + } else if (dispatchedGestureIdBits.isEmpty() + && !mPointerGesture.lastGestureIdBits.isEmpty()) { + // Synthesize a hover move event after all pointers go up to indicate that + // the pointer is hovering again even if the user is not currently touching + // the touch pad. This ensures that a view will receive a fresh hover enter + // event after a tap. + float x, y; + mPointerController->getPosition(&x, &y); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER; + + PointerCoords pointerCoords; + pointerCoords.clear(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + + getDispatcher()->notifyMotion(when, getDeviceId(), mPointerSource, policyFlags, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, &pointerProperties, &pointerCoords, 0, 0, mPointerGesture.downTime); } // Update state. @@ -5361,7 +5394,6 @@ void SingleTouchInputMapper::configureRawAxes() { MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device), mSlotCount(0), mUsingSlotsProtocol(false) { - clearState(); } MultiTouchInputMapper::~MultiTouchInputMapper() { @@ -5371,6 +5403,24 @@ void MultiTouchInputMapper::clearState() { mAccumulator.clearSlots(mSlotCount); mAccumulator.clearButtons(); mButtonState = 0; + + if (mUsingSlotsProtocol) { + // Query the driver for the current slot index and use it as the initial slot + // before we start reading events from the device. It is possible that the + // current slot index will not be the same as it was when the first event was + // written into the evdev buffer, which means the input mapper could start + // out of sync with the initial state of the events in the evdev buffer. + // In the extremely unlikely case that this happens, the data from + // two slots will be confused until the next ABS_MT_SLOT event is received. + // This can cause the touch point to "jump", but at least there will be + // no stuck touches. + status_t status = getEventHub()->getAbsoluteAxisValue(getDeviceId(), ABS_MT_SLOT, + &mAccumulator.currentSlot); + if (status) { + LOGW("Could not retrieve current multitouch slot index. status=%d", status); + mAccumulator.currentSlot = -1; + } + } } void MultiTouchInputMapper::reset() { @@ -5649,6 +5699,8 @@ void MultiTouchInputMapper::configureRawAxes() { } mAccumulator.allocateSlots(mSlotCount); + + clearState(); } diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp index 08cc75e631eb..0ae2ab898d67 100644 --- a/services/input/SpriteController.cpp +++ b/services/input/SpriteController.cpp @@ -252,11 +252,7 @@ void SpriteController::doUpdateSprites() { | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) { status_t status; if (!haveTransaction) { - status = mSurfaceComposerClient->openTransaction(); - if (status) { - LOGE("Error %d opening transation to update sprite surface.", status); - break; - } + SurfaceComposerClient::openGlobalTransaction(); haveTransaction = true; } @@ -322,10 +318,7 @@ void SpriteController::doUpdateSprites() { } if (haveTransaction) { - status_t status = mSurfaceComposerClient->closeTransaction(); - if (status) { - LOGE("Error %d closing transaction to update sprite surface.", status); - } + SurfaceComposerClient::closeGlobalTransaction(); } // If any surfaces were changed, write back the new surface properties to the sprites. diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index e349248b1c5a..d3c5ece7cbef 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -429,6 +429,7 @@ class FakeEventHub : public EventHubInterface { KeyedVector<int32_t, int32_t> keyCodeStates; KeyedVector<int32_t, int32_t> scanCodeStates; KeyedVector<int32_t, int32_t> switchStates; + KeyedVector<int32_t, int32_t> absoluteAxisValue; KeyedVector<int32_t, KeyInfo> keys; KeyedVector<int32_t, bool> leds; Vector<VirtualKeyDefinition> virtualKeys; @@ -514,6 +515,11 @@ public: device->switchStates.replaceValueFor(switchCode, state); } + void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) { + Device* device = getDevice(deviceId); + device->absoluteAxisValue.replaceValueFor(axis, value); + } + void addKey(int32_t deviceId, int32_t scanCode, int32_t keyCode, uint32_t flags) { Device* device = getDevice(deviceId); KeyInfo info; @@ -677,6 +683,20 @@ private: return AKEY_STATE_UNKNOWN; } + virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, + int32_t* outValue) const { + Device* device = getDevice(deviceId); + if (device) { + ssize_t index = device->absoluteAxisValue.indexOfKey(axis); + if (index >= 0) { + *outValue = device->absoluteAxisValue.valueAt(index); + return OK; + } + } + *outValue = 0; + return -1; + } + virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { bool result = false; diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 8fb6274a7afb..663f4f47f56e 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -2533,7 +2533,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private VpnCallback() { } - public synchronized void override(String[] dnsServers) { + public synchronized void override(List<String> dnsServers, List<String> searchDomains) { // TODO: override DNS servers and http proxy. } diff --git a/services/java/com/android/server/DnsPinger.java b/services/java/com/android/server/DnsPinger.java index 05de53a2810c..4e33938b2ca8 100644 --- a/services/java/com/android/server/DnsPinger.java +++ b/services/java/com/android/server/DnsPinger.java @@ -16,16 +16,18 @@ package com.android.server; -import android.content.ContentResolver; import android.content.Context; import android.net.ConnectivityManager; import android.net.LinkProperties; +import android.net.NetworkUtils; import android.os.SystemClock; +import android.provider.Settings; import android.util.Slog; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; +import java.net.NetworkInterface; import java.net.SocketTimeoutException; import java.util.Collection; import java.util.Random; @@ -38,7 +40,7 @@ import java.util.Random; * API may not differentiate between a time out and a failure lookup (which we * really care about). * <p> - * TODO : More general API. Socket does not bind to specified connection type + * TODO : More general API. Socket does not bind to specified connection type * TODO : Choice of DNS query location - current looks up www.android.com * * @hide @@ -47,7 +49,7 @@ public final class DnsPinger { private static final boolean V = true; /** Number of bytes for the query */ - private static final int DNS_QUERY_BASE_SIZE = 33; + private static final int DNS_QUERY_BASE_SIZE = 32; /** The DNS port */ private static final int DNS_PORT = 53; @@ -56,46 +58,71 @@ public final class DnsPinger { private static Random sRandom = new Random(); private ConnectivityManager mConnectivityManager = null; - private ContentResolver mContentResolver; private Context mContext; private int mConnectionType; + private InetAddress mDefaultDns; private String TAG; - /** - * @param connectionType The connection type from @link {@link ConnectivityManager} + * @param connectionType The connection type from {@link ConnectivityManager} */ public DnsPinger(String TAG, Context context, int connectionType) { mContext = context; - mContentResolver = context.getContentResolver(); mConnectionType = connectionType; + if (!ConnectivityManager.isNetworkTypeValid(connectionType)) { + Slog.e(TAG, "Invalid connectionType in constructor: " + connectionType); + } this.TAG = TAG; + + mDefaultDns = getDefaultDns(); } /** - * @return The first DNS in the link properties of the specified connection type + * @return The first DNS in the link properties of the specified connection + * type or the default system DNS if the link properties has null + * dns set. Should not be null. */ public InetAddress getDns() { - LinkProperties linkProperties = getCurLinkProperties(); - if (linkProperties == null) - return null; + LinkProperties curLinkProps = getCurrentLinkProperties(); + if (curLinkProps == null) { + Slog.e(TAG, "getCurLinkProperties:: LP for type" + mConnectionType + " is null!"); + return mDefaultDns; + } - Collection<InetAddress> dnses = linkProperties.getDnses(); - if (dnses == null || dnses.size() == 0) - return null; + Collection<InetAddress> dnses = curLinkProps.getDnses(); + if (dnses == null || dnses.size() == 0) { + Slog.v(TAG, "getDns::LinkProps has null dns - returning default"); + return mDefaultDns; + } return dnses.iterator().next(); } - private LinkProperties getCurLinkProperties() { + private LinkProperties getCurrentLinkProperties() { if (mConnectivityManager == null) { mConnectivityManager = (ConnectivityManager) mContext.getSystemService( Context.CONNECTIVITY_SERVICE); } + return mConnectivityManager.getLinkProperties(mConnectionType); } + private InetAddress getDefaultDns() { + String dns = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_DNS_SERVER); + if (dns == null || dns.length() == 0) { + dns = mContext.getResources().getString( + com.android.internal.R.string.config_default_dns_server); + } + try { + return NetworkUtils.numericToInetAddress(dns); + } catch (IllegalArgumentException e) { + Slog.w(TAG, "getDefaultDns::malformed default dns address"); + return null; + } + } + /** * @return time to response. Negative value on error. */ @@ -107,8 +134,15 @@ public final class DnsPinger { // Set some socket properties socket.setSoTimeout(timeout); - byte[] buf = new byte[DNS_QUERY_BASE_SIZE]; - fillQuery(buf); + // Try to bind but continue ping if bind fails + try { + socket.setNetworkInterface(NetworkInterface.getByName( + getCurrentLinkProperties().getInterfaceName())); + } catch (Exception e) { + Slog.d(TAG,"pingDns::Error binding to socket", e); + } + + byte[] buf = constructQuery(); // Send the DNS query @@ -141,48 +175,47 @@ public final class DnsPinger { } - private static void fillQuery(byte[] buf) { - - /* - * See RFC2929 (though the bit tables in there are misleading for us. - * For example, the recursion desired bit is the 0th bit for us, but - * looking there it would appear as the 7th bit of the byte - */ - - // Make sure it's all zeroed out - for (int i = 0; i < buf.length; i++) - buf[i] = 0; - - // Form a query for www.android.com + /** + * @return google.com DNS query packet + */ + private static byte[] constructQuery() { + byte[] buf = new byte[DNS_QUERY_BASE_SIZE]; // [0-1] bytes are an ID, generate random ID for this query buf[0] = (byte) sRandom.nextInt(256); buf[1] = (byte) sRandom.nextInt(256); // [2-3] bytes are for flags. - buf[2] = 1; // Recursion desired + buf[2] = 0x01; // Recursion desired - // [4-5] bytes are for the query count - buf[5] = 1; // One query + // [4-5] bytes are for number of queries (QCOUNT) + buf[5] = 0x01; // [6-7] [8-9] [10-11] are all counts of other fields we don't use // [12-15] for www writeString(buf, 12, "www"); - // [16-23] for android - writeString(buf, 16, "android"); + // [16-22] for google + writeString(buf, 16, "google"); + + // [23-26] for com + writeString(buf, 23, "com"); - // [24-27] for com - writeString(buf, 24, "com"); + // [27] is a null byte terminator byte for the url - // [29-30] bytes are for QTYPE, set to 1 - buf[30] = 1; + // [28-29] bytes are for QTYPE, set to 1 = A (host address) + buf[29] = 0x01; - // [31-32] bytes are for QCLASS, set to 1 - buf[32] = 1; + // [30-31] bytes are for QCLASS, set to 1 = IN (internet) + buf[31] = 0x01; + + return buf; } + /** + * Writes the string's length and its contents to the buffer + */ private static void writeString(byte[] buf, int startPos, String string) { int pos = startPos; diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index b03bd4d28df0..14abf808b71f 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1641,7 +1641,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final InputMethodInfo imi = mMethodMap.get(mCurMethodId); if (imi == null) return false; final int N = subtypes.length; - mFileManager.addInputMethodSubtypes(mCurMethodId, subtypes); + mFileManager.addInputMethodSubtypes(imi, subtypes); buildInputMethodListLocked(mMethodList, mMethodMap); return true; } @@ -2023,25 +2023,26 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final CharSequence label = imi.loadLabel(pm); if (showSubtypes && enabledSubtypeSet.size() > 0) { final int subtypeCount = imi.getSubtypeCount(); + if (DEBUG) { + Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); + } for (int j = 0; j < subtypeCount; ++j) { - InputMethodSubtype subtype = imi.getSubtypeAt(j); - if (enabledSubtypeSet.contains(String.valueOf(subtype.hashCode())) - && !subtype.isAuxiliary()) { + final InputMethodSubtype subtype = imi.getSubtypeAt(j); + final String subtypeHashCode = String.valueOf(subtype.hashCode()); + // We show all enabled IMEs and subtypes when an IME is shown. + if (enabledSubtypeSet.contains(subtypeHashCode) + && (mInputShown || !subtype.isAuxiliary())) { final CharSequence title; - int nameResId = subtype.getNameResId(); - String mode = subtype.getMode(); - if (nameResId != 0) { - title = TextUtils.concat(subtype.getDisplayName(context, - imi.getPackageName(), imi.getServiceInfo().applicationInfo), - (TextUtils.isEmpty(label) ? "" : " (" + label + ")")); - } else { - CharSequence language = subtype.getLocale(); - // TODO: Use more friendly Title and UI - title = label + "," + (mode == null ? "" : mode) + "," - + (language == null ? "" : language); - } + final String mode = subtype.getMode(); + title = TextUtils.concat(subtype.getDisplayName(context, + imi.getPackageName(), imi.getServiceInfo().applicationInfo), + (TextUtils.isEmpty(label) ? "" : " (" + label + ")")); imList.add(new Pair<CharSequence, Pair<InputMethodInfo, Integer>>( title, new Pair<InputMethodInfo, Integer>(imi, j))); + // Removing this subtype from enabledSubtypeSet because we no longer + // need to add an entry of this subtype to imList to avoid duplicated + // entries. + enabledSubtypeSet.remove(subtypeHashCode); } } } else { @@ -2338,7 +2339,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } - ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>( + final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>( applicableModeAndSubtypesMap.values()); if (!containsKeyboardSubtype) { InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked( @@ -3016,17 +3017,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public void addInputMethodSubtypes( - String imiId, InputMethodSubtype[] additionalSubtypes) { + InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) { synchronized (mMethodMap) { + final HashSet<InputMethodSubtype> existingSubtypes = + new HashSet<InputMethodSubtype>(); + for (int i = 0; i < imi.getSubtypeCount(); ++i) { + existingSubtypes.add(imi.getSubtypeAt(i)); + } + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); final int N = additionalSubtypes.length; for (int i = 0; i < N; ++i) { final InputMethodSubtype subtype = additionalSubtypes[i]; - if (!subtypes.contains(subtype)) { + if (!subtypes.contains(subtype) && !existingSubtypes.contains(subtype)) { subtypes.add(subtype); } } - mSubtypesMap.put(imiId, subtypes); + mSubtypesMap.put(imi.getId(), subtypes); writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap); } @@ -3133,8 +3140,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE); final String imeSubtypeExtraValue = parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE); - final boolean isAuxiliary = - Boolean.valueOf(parser.getAttributeValue(null, ATTR_IS_AUXILIARY)); + final boolean isAuxiliary = "1".equals(String.valueOf( + parser.getAttributeValue(null, ATTR_IS_AUXILIARY))); final InputMethodSubtype subtype = new InputMethodSubtype(label, icon, imeSubtypeLocale, imeSubtypeMode, imeSubtypeExtraValue, isAuxiliary); diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index 1d3e3ac23c8b..b3d72200909c 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -41,7 +41,7 @@ import android.content.IntentFilter; /** * {@hide} */ -public class IntentResolver<F extends IntentFilter, R extends Object> { +public abstract class IntentResolver<F extends IntentFilter, R extends Object> { final private static String TAG = "IntentResolver"; final private static boolean DEBUG = false; final private static boolean localLOGV = DEBUG || false; @@ -333,14 +333,19 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return false; } - protected String packageForFilter(F filter) { - return null; - } + /** + * Return the package that owns this filter. This must be implemented to + * provide correct filtering of Intents that have specified a package name + * they are to be delivered to. + */ + protected abstract String packageForFilter(F filter); + @SuppressWarnings("unchecked") protected R newResult(F filter, int match) { return (R)filter; } + @SuppressWarnings("unchecked") protected void sortResults(List<R> results) { Collections.sort(results, mResolvePrioritySorter); } @@ -502,6 +507,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { String resolvedType, String scheme, List<F> src, List<R> dest) { final String action = intent.getAction(); final Uri data = intent.getData(); + final String packageName = intent.getPackage(); final boolean excludingStopped = intent.isExcludingStopped(); @@ -520,6 +526,14 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { continue; } + // Is delivery being limited to filters owned by a particular package? + if (packageName != null && !packageName.equals(packageForFilter(filter))) { + if (debug) { + Slog.v(TAG, " Filter is not from package " + packageName + "; skipping"); + } + continue; + } + // Do we already have this one? if (!allowFilterResult(filter, dest)) { if (debug) { @@ -561,6 +575,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { } // Sorts a List of IntentFilter objects into descending priority order. + @SuppressWarnings("rawtypes") private static final Comparator mResolvePrioritySorter = new Comparator() { public int compare(Object o1, Object o2) { final int q1 = ((IntentFilter) o1).getPriority(); diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 656ec4d266dd..56afe7f51414 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -195,6 +195,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final Object mKey; final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>(); int mPendingBroadcasts; + String requiredPermissions; Receiver(ILocationListener listener) { mListener = listener; @@ -284,7 +285,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler); + mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler, + requiredPermissions); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -319,7 +321,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler); + mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler, + requiredPermissions); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -358,7 +361,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler); + mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler, + requiredPermissions); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -572,22 +576,30 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return Settings.Secure.isLocationProviderEnabled(resolver, provider); } - private void checkPermissionsSafe(String provider) { - if ((LocationManager.GPS_PROVIDER.equals(provider) - || LocationManager.PASSIVE_PROVIDER.equals(provider)) - && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED)) { - throw new SecurityException("Provider " + provider - + " requires ACCESS_FINE_LOCATION permission"); + private String checkPermissionsSafe(String provider, String lastPermission) { + if (LocationManager.GPS_PROVIDER.equals(provider) + || LocationManager.PASSIVE_PROVIDER.equals(provider)) { + if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Provider " + provider + + " requires ACCESS_FINE_LOCATION permission"); + } + return ACCESS_FINE_LOCATION; } - if (LocationManager.NETWORK_PROVIDER.equals(provider) - && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED) - && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) - != PackageManager.PERMISSION_GRANTED)) { - throw new SecurityException("Provider " + provider - + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); + + // Assume any other provider requires the coarse or fine permission. + if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + return ACCESS_FINE_LOCATION.equals(lastPermission) + ? lastPermission : ACCESS_COARSE_LOCATION; } + if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + return ACCESS_FINE_LOCATION; + } + + throw new SecurityException("Provider " + provider + + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); } private boolean isAllowedProviderSafe(String provider) { @@ -1099,8 +1111,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + void validatePendingIntent(PendingIntent intent) { + if (intent.isTargetedToPackage()) { + return; + } + Slog.i(TAG, "Given Intent does not require a specific package: " + + intent); + // XXX we should really throw a security exception, if the caller's + // targetSdkVersion is high enough. + //throw new SecurityException("Given Intent does not require a specific package: " + // + intent); + } + public void requestLocationUpdatesPI(String provider, Criteria criteria, long minTime, float minDistance, boolean singleShot, PendingIntent intent) { + validatePendingIntent(intent); if (criteria != null) { // FIXME - should we consider using multiple providers simultaneously // rather than only the best one? @@ -1132,7 +1157,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run throw new IllegalArgumentException("provider=" + provider); } - checkPermissionsSafe(provider); + receiver.requiredPermissions = checkPermissionsSafe(provider, + receiver.requiredPermissions); // so wakelock calls will succeed final int callingUid = Binder.getCallingUid(); @@ -1300,7 +1326,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } // first check for permission to the provider - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); // and check for ACCESS_LOCATION_EXTRA_COMMANDS if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS) != PackageManager.PERMISSION_GRANTED)) { @@ -1432,7 +1458,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcasts() // is called before decrementPendingBroadcasts() - intent.send(mContext, 0, enteredIntent, this, mLocationHandler); + intent.send(mContext, 0, enteredIntent, this, mLocationHandler, + ACCESS_FINE_LOCATION); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcasts(); @@ -1457,7 +1484,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcasts() // is called before decrementPendingBroadcasts() - intent.send(mContext, 0, exitedIntent, this, mLocationHandler); + intent.send(mContext, 0, exitedIntent, this, mLocationHandler, + ACCESS_FINE_LOCATION); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcasts(); @@ -1526,6 +1554,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public void addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent) { + validatePendingIntent(intent); try { synchronized (mLock) { addProximityAlertLocked(latitude, longitude, radius, expiration, intent); @@ -1626,7 +1655,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return null; } - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); Bundle b = new Bundle(); b.putBoolean("network", p.requiresNetwork()); @@ -1668,7 +1697,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private boolean _isProviderEnabledLocked(String provider) { - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { @@ -1694,7 +1723,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private Location _getLastKnownLocationLocked(String provider) { - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index d3244ecc376b..54e54327f8f7 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -1626,6 +1626,30 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC } } + public String getSecureContainerFilesystemPath(String id) { + validatePermission(android.Manifest.permission.ASEC_ACCESS); + waitForReady(); + warnOnNotMounted(); + + try { + ArrayList<String> rsp = mConnector.doCommand(String.format("asec fspath %s", id)); + String []tok = rsp.get(0).split(" "); + int code = Integer.parseInt(tok[0]); + if (code != VoldResponseCode.AsecPathResult) { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + return tok[1]; + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedStorageNotFound) { + Slog.i(TAG, String.format("Container '%s' not found", id)); + return null; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + } + public void finishMediaUpdate() { mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 630aaf9edafe..1c150f8a65ea 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -16,10 +16,11 @@ package com.android.server; +import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.Manifest.permission.MANAGE_NETWORK_POLICY; +import static android.provider.Settings.Secure.NETSTATS_ENABLED; import android.content.Context; import android.content.pm.PackageManager; @@ -35,13 +36,14 @@ import android.os.Binder; import android.os.INetworkManagementService; import android.os.SystemClock; import android.os.SystemProperties; +import android.provider.Settings; 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 dalvik.system.BlockGuard; +import com.google.android.collect.Sets; import java.io.BufferedReader; import java.io.DataInputStream; @@ -54,6 +56,7 @@ 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; @@ -117,6 +120,13 @@ class NetworkManagementService extends INetworkManagementService.Stub { private ArrayList<INetworkManagementEventObserver> mObservers; + /** Set of interfaces with active quotas. */ + private HashSet<String> mInterfaceQuota = Sets.newHashSet(); + /** Set of UIDs with active reject rules. */ + private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray(); + + private boolean mBandwidthControlEnabled; + /** * Constructs a new NetworkManagementService instance * @@ -155,6 +165,29 @@ class NetworkManagementService extends INetworkManagementService.Stub { return new NetworkManagementService(context, procRoot); } + public void systemReady() { + + // only enable bandwidth control when support exists, and requested by + // system setting. + // TODO: eventually migrate to be always enabled + final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists(); + final boolean shouldEnable = + Settings.Secure.getInt(mContext.getContentResolver(), NETSTATS_ENABLED, 0) != 0; + + mBandwidthControlEnabled = false; + if (hasKernelSupport && shouldEnable) { + Slog.d(TAG, "enabling bandwidth control"); + try { + mConnector.doCommand("bandwidth enable"); + mBandwidthControlEnabled = true; + } catch (NativeDaemonConnectorException e) { + Slog.e(TAG, "problem enabling bandwidth controls", e); + } + } else { + Slog.d(TAG, "not enabling bandwidth control"); + } + } + public void registerObserver(INetworkManagementEventObserver obs) { Slog.d(TAG, "Registering observer"); mObservers.add(obs); @@ -913,7 +946,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - if (mProcStatsNetfilter.exists()) { + if (mBandwidthControlEnabled) { return getNetworkStatsDetailNetfilter(UID_ALL); } else { return getNetworkStatsDetailUidstat(UID_ALL); @@ -921,13 +954,103 @@ class NetworkManagementService extends INetworkManagementService.Stub { } @Override + public void setInterfaceQuota(String iface, long quota) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + // silently discard when control disabled + // TODO: eventually migrate to be always enabled + if (!mBandwidthControlEnabled) return; + + 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); + + // silently discard when control disabled + // TODO: eventually migrate to be always enabled + if (!mBandwidthControlEnabled) return; + + 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); + + // silently discard when control disabled + // TODO: eventually migrate to be always enabled + if (!mBandwidthControlEnabled) return; + + 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 (mProcStatsNetfilter.exists()) { + if (mBandwidthControlEnabled) { return getNetworkStatsDetailNetfilter(uid); } else { return getNetworkStatsDetailUidstat(uid); @@ -958,7 +1081,8 @@ class NetworkManagementService extends INetworkManagementService.Stub { try { final String iface = parsed.get(KEY_IFACE); - final int tag = BlockGuard.kernelToTag(parsed.get(KEY_TAG_HEX)); + 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)); @@ -1066,12 +1190,6 @@ class NetworkManagementService extends INetworkManagementService.Stub { return getInterfaceThrottle(iface, false); } - @Override - public void setBandwidthControlEnabled(boolean enabled) { - mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - mConnector.doCommand(String.format("bandwidth %s", (enabled ? "enable" : "disable"))); - } - /** * Split given line into {@link ArrayList}. */ diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cd68c6854153..8c7e279b7132 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -283,7 +283,8 @@ class ServerThread extends Thread { try { Slog.i(TAG, "NetworkPolicy Service"); networkPolicy = new NetworkPolicyManagerService( - context, ActivityManagerService.self(), power, networkStats); + context, ActivityManagerService.self(), power, + networkStats, networkManagement); ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy); } catch (Throwable e) { Slog.e(TAG, "Failure starting NetworkPolicy Service", e); @@ -522,6 +523,7 @@ class ServerThread extends Thread { // These are needed to propagate to the runnable below. final Context contextF = context; final BatteryService batteryF = battery; + final NetworkManagementService networkManagementF = networkManagement; final NetworkStatsService networkStatsF = networkStats; final NetworkPolicyManagerService networkPolicyF = networkPolicy; final ConnectivityService connectivityF = connectivity; @@ -549,6 +551,7 @@ class ServerThread extends Thread { startSystemUi(contextF); if (batteryF != null) batteryF.systemReady(); + if (networkManagementF != null) networkManagementF.systemReady(); if (networkStatsF != null) networkStatsF.systemReady(); if (networkPolicyF != null) networkPolicyF.systemReady(); if (connectivityF != null) connectivityF.systemReady(); diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index 3ba9c14cf4d7..1356e2a9b23f 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -95,14 +95,14 @@ public class WifiWatchdogService { private static final long MIN_SINGLE_DNS_CHECK_INTERVAL = 10 * 60 * 1000; private static final long MIN_WALLED_GARDEN_INTERVAL = 15 * 60 * 1000; - private static final int MAX_CHECKS_PER_SSID = 7; - private static final int NUM_DNS_PINGS = 5; + private static final int MAX_CHECKS_PER_SSID = 9; + private static final int NUM_DNS_PINGS = 7; private static double MIN_RESPONSE_RATE = 0.50; // TODO : Adjust multiple DNS downward to 250 on repeated failure // private static final int MULTI_DNS_PING_TIMEOUT_MS = 250; - private static final int DNS_PING_TIMEOUT_MS = 1000; + private static final int DNS_PING_TIMEOUT_MS = 800; private static final long DNS_PING_INTERVAL = 250; private static final long BLACKLIST_FOLLOWUP_INTERVAL = 15 * 1000; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 4ec71c180e87..bf877f6e9d20 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -622,6 +622,11 @@ public final class ActivityManagerService extends ActivityManagerNative } return true; } + + @Override + protected String packageForFilter(BroadcastFilter filter) { + return filter.packageName; + } }; /** @@ -1825,6 +1830,8 @@ public final class ActivityManagerService extends ActivityManagerNative // We already have the app running, or are waiting for it to // come up (we have a pid but not yet its thread), so keep it. if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app); + // If this is a new package in the process, add the package to the list + app.addPackage(info.packageName); return app; } else { // An application record is attached to a previous process, @@ -2278,7 +2285,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - return pir.sendInner(0, fillInIntent, resolvedType, + return pir.sendInner(0, fillInIntent, resolvedType, null, null, resultTo, resultWho, requestCode, flagsMask, flagsValues); } @@ -4162,6 +4169,27 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } + public boolean isIntentSenderTargetedToPackage(IIntentSender pendingResult) { + if (!(pendingResult instanceof PendingIntentRecord)) { + return false; + } + try { + PendingIntentRecord res = (PendingIntentRecord)pendingResult; + if (res.key.allIntents == null) { + return false; + } + for (int i=0; i<res.key.allIntents.length; i++) { + Intent intent = res.key.allIntents[i]; + if (intent.getPackage() != null && intent.getComponent() != null) { + return false; + } + } + return true; + } catch (ClassCastException e) { + } + return false; + } + public void setProcessLimit(int max) { enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, "setProcessLimit()"); @@ -9895,6 +9923,7 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid); if (app != null && app.thread != null) { try { + app.addPackage(r.appInfo.packageName); realStartServiceLocked(r, app); return true; } catch (RemoteException e) { @@ -10945,7 +10974,7 @@ public final class ActivityManagerService extends ActivityManagerNative mBroadcastsScheduled = true; } - public Intent registerReceiver(IApplicationThread caller, + public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission) { synchronized(this) { ProcessRecord callerApp = null; @@ -10957,6 +10986,13 @@ public final class ActivityManagerService extends ActivityManagerNative + " (pid=" + Binder.getCallingPid() + ") when registering receiver " + receiver); } + if (callerApp.info.uid != Process.SYSTEM_UID && + !callerApp.pkgList.contains(callerPackage)) { + throw new SecurityException("Given caller package " + callerPackage + + " is not running in process " + callerApp); + } + } else { + callerPackage = null; } List allSticky = null; @@ -11001,7 +11037,7 @@ public final class ActivityManagerService extends ActivityManagerNative } mRegisteredReceivers.put(receiver.asBinder(), rl); } - BroadcastFilter bf = new BroadcastFilter(filter, rl, permission); + BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission); rl.add(bf); if (!bf.debugCheck()) { Slog.w(TAG, "==> For Dynamic broadast"); @@ -12155,6 +12191,7 @@ public final class ActivityManagerService extends ActivityManagerNative info.activityInfo.applicationInfo.uid); if (app != null && app.thread != null) { try { + app.addPackage(info.activityInfo.packageName); processCurBroadcastLocked(r, app); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index b94ee587793c..b1da69f90326 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -652,6 +652,7 @@ final class ActivityStack { if (app != null && app.thread != null) { try { + app.addPackage(r.info.packageName); realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java index 2e784d30caa7..b49bc220ac03 100644 --- a/services/java/com/android/server/am/BroadcastFilter.java +++ b/services/java/com/android/server/am/BroadcastFilter.java @@ -25,12 +25,14 @@ import java.io.PrintWriter; class BroadcastFilter extends IntentFilter { // Back-pointer to the list this filter is in. final ReceiverList receiverList; + final String packageName; final String requiredPermission; BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList, - String _requiredPermission) { + String _packageName, String _requiredPermission) { super(_filter); receiverList = _receiverList; + packageName = _packageName; requiredPermission = _requiredPermission; } diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index ee6e4204af39..8ed0cc11abd8 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -177,13 +177,13 @@ class PendingIntentRecord extends IIntentSender.Stub { } public int send(int code, Intent intent, String resolvedType, - IIntentReceiver finishedReceiver) { + IIntentReceiver finishedReceiver, String requiredPermission) { return sendInner(code, intent, resolvedType, finishedReceiver, - null, null, 0, 0, 0); + requiredPermission, null, null, 0, 0, 0); } int sendInner(int code, Intent intent, String resolvedType, - IIntentReceiver finishedReceiver, + IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues) { synchronized(owner) { @@ -246,8 +246,8 @@ class PendingIntentRecord extends IIntentSender.Stub { // that the broadcast be delivered synchronously owner.broadcastIntentInPackage(key.packageName, uid, finalIntent, resolvedType, - finishedReceiver, code, null, null, null, - (finishedReceiver != null), false); + finishedReceiver, code, null, null, + requiredPermission, (finishedReceiver != null), false); sendFinish = false; } catch (RuntimeException e) { Slog.w(ActivityManagerService.TAG, diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index db3b61e5cb17..a8be916427f3 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -27,15 +27,23 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.INetworkManagementEventObserver; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; import android.os.Binder; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; +import android.os.Process; +import android.os.SystemClock; +import android.os.SystemProperties; import android.util.Log; import com.android.internal.R; import com.android.internal.net.VpnConfig; import com.android.server.ConnectivityService.VpnCallback; +import java.io.OutputStream; +import java.nio.charset.Charsets; +import java.util.Arrays; + /** * @hide */ @@ -49,7 +57,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub { private String mPackageName; private String mInterfaceName; - private String mDnsPropertyPrefix; + + private LegacyVpnRunner mLegacyVpnRunner; public Vpn(Context context, VpnCallback callback) { mContext = context; @@ -68,11 +77,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub { return mPackageName; } - // Check the permission of the caller. - PackageManager pm = mContext.getPackageManager(); - VpnConfig.enforceCallingPackage(pm.getNameForUid(Binder.getCallingUid())); + // Only system user can call this method. + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Unauthorized Caller"); + } // Check the permission of the given package. + PackageManager pm = mContext.getPackageManager(); if (packageName.isEmpty()) { packageName = null; } else if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) { @@ -81,7 +92,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // Reset the interface and hide the notification. if (mInterfaceName != null) { - nativeReset(mInterfaceName); + jniResetInterface(mInterfaceName); mCallback.restore(); hideNotification(); mInterfaceName = null; @@ -95,6 +106,12 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mContext.sendBroadcast(intent); } + // Stop legacy VPN if it has been started. + if (mLegacyVpnRunner != null) { + mLegacyVpnRunner.exit(); + mLegacyVpnRunner = null; + } + Log.i(TAG, "Switched from " + mPackageName + " to " + packageName); mPackageName = packageName; return mPackageName; @@ -110,7 +127,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { public void protect(ParcelFileDescriptor socket, String name) { try { mContext.enforceCallingPermission(VPN, "protect"); - nativeProtect(socket.getFd(), name); + jniProtectSocket(socket.getFd(), name); } finally { try { socket.close(); @@ -123,7 +140,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { /** * Configure a TUN interface and return its file descriptor. * - * @param configuration The parameters to configure the interface. + * @param config The parameters to configure the interface. * @return The file descriptor of the interface. */ public synchronized ParcelFileDescriptor establish(VpnConfig config) { @@ -142,18 +159,37 @@ public class Vpn extends INetworkManagementEventObserver.Stub { return null; } - // Create and configure the interface. - ParcelFileDescriptor descriptor = ParcelFileDescriptor.adoptFd( - nativeEstablish(config.mtu, config.addresses, config.routes)); + // Load the label. + String label = app.loadLabel(pm).toString(); + + // Load the icon and convert it into a bitmap. + Drawable icon = app.loadIcon(pm); + Bitmap bitmap = null; + if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) { + int width = mContext.getResources().getDimensionPixelSize( + android.R.dimen.notification_large_icon_width); + int height = mContext.getResources().getDimensionPixelSize( + android.R.dimen.notification_large_icon_height); + icon.setBounds(0, 0, width, height); + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + icon.draw(new Canvas(bitmap)); + } - // Replace the interface and abort if it fails. + // Create the interface and abort if any of the following steps fails. + ParcelFileDescriptor descriptor = + ParcelFileDescriptor.adoptFd(jniCreateInterface(config.mtu)); try { - String interfaceName = nativeGetName(descriptor.getFd()); - - if (mInterfaceName != null && !mInterfaceName.equals(interfaceName)) { - nativeReset(mInterfaceName); + String name = jniGetInterfaceName(descriptor.getFd()); + if (jniSetAddresses(name, config.addresses) < 1) { + throw new IllegalArgumentException("At least one address must be specified"); + } + if (config.routes != null) { + jniSetRoutes(name, config.routes); + } + if (mInterfaceName != null && !mInterfaceName.equals(name)) { + jniResetInterface(mInterfaceName); } - mInterfaceName = interfaceName; + mInterfaceName = name; } catch (RuntimeException e) { try { descriptor.close(); @@ -163,12 +199,15 @@ public class Vpn extends INetworkManagementEventObserver.Stub { throw e; } - String dnsServers = (config.dnsServers == null) ? "" : config.dnsServers.trim(); - mCallback.override(dnsServers.isEmpty() ? null : dnsServers.split(" ")); + // Override DNS servers and search domains. + mCallback.override(config.dnsServers, config.searchDomains); + // Fill more values. config.packageName = mPackageName; config.interfaceName = mInterfaceName; - showNotification(pm, app, config); + + // Show the notification! + showNotification(config, label, bitmap); return descriptor; } @@ -186,43 +225,28 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // INetworkManagementEventObserver.Stub public synchronized void interfaceRemoved(String name) { - if (name.equals(mInterfaceName) && nativeCheck(name) == 0) { + if (name.equals(mInterfaceName) && jniCheckInterface(name) == 0) { hideNotification(); - mInterfaceName = null; mCallback.restore(); + mInterfaceName = null; } } - private void showNotification(PackageManager pm, ApplicationInfo app, VpnConfig config) { + private void showNotification(VpnConfig config, String label, Bitmap icon) { NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); if (nm != null) { - // Load the icon and convert it into a bitmap. - Drawable icon = app.loadIcon(pm); - Bitmap bitmap = null; - if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) { - int width = mContext.getResources().getDimensionPixelSize( - android.R.dimen.notification_large_icon_width); - int height = mContext.getResources().getDimensionPixelSize( - android.R.dimen.notification_large_icon_height); - icon.setBounds(0, 0, width, height); - bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - icon.draw(new Canvas(bitmap)); - } - - // Load the label. - String label = app.loadLabel(pm).toString(); - - // Build the notification. + String title = (label == null) ? mContext.getString(R.string.vpn_title) : + mContext.getString(R.string.vpn_title_long, label); String text = (config.sessionName == null) ? mContext.getString(R.string.vpn_text) : mContext.getString(R.string.vpn_text_long, config.sessionName); + long identity = Binder.clearCallingIdentity(); Notification notification = new Notification.Builder(mContext) .setSmallIcon(R.drawable.vpn_connected) - .setLargeIcon(bitmap) - .setTicker(mContext.getString(R.string.vpn_ticker, label)) - .setContentTitle(mContext.getString(R.string.vpn_title, label)) + .setLargeIcon(icon) + .setContentTitle(title) .setContentText(text) .setContentIntent(VpnConfig.getIntentForNotification(mContext, config)) .setDefaults(Notification.DEFAULT_ALL) @@ -244,9 +268,227 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } } - private native int nativeEstablish(int mtu, String addresses, String routes); - private native String nativeGetName(int fd); - private native void nativeReset(String name); - private native int nativeCheck(String name); - private native void nativeProtect(int fd, String name); + private native int jniCreateInterface(int mtu); + private native String jniGetInterfaceName(int fd); + private native int jniSetAddresses(String name, String addresses); + private native int jniSetRoutes(String name, String routes); + private native void jniResetInterface(String name); + private native int jniCheckInterface(String name); + private native void jniProtectSocket(int fd, String name); + + /** + * Handle legacy VPN requests. This method stops the daemons 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 startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { + // Stop the current VPN just like a normal VPN application. + prepare(""); + + // Legacy VPN does not have a package name. + config.packageName = null; + + // Start a new runner and we are done! + mLegacyVpnRunner = new LegacyVpnRunner(config, 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 VpnConfig mConfig; + private final String[] mDaemons; + private final String[][] mArguments; + private long mTimer = -1; + + public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) { + super(TAG); + mConfig = config; + mDaemons = new String[] {"racoon", "mtpd"}; + mArguments = new String[][] {racoon, mtpd}; + } + + public void exit() { + for (String daemon : mDaemons) { + SystemProperties.set("ctl.stop", daemon); + } + interrupt(); + } + + @Override + public void run() { + // Wait for the previous thread since it has been interrupted. + Log.v(TAG, "wait"); + synchronized (TAG) { + Log.v(TAG, "begin"); + execute(); + Log.v(TAG, "end"); + } + } + + 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 daemons. + for (String daemon : mDaemons) { + SystemProperties.set("ctl.stop", daemon); + } + + // Wait for the daemons to stop. + for (String daemon : mDaemons) { + String key = "init.svc." + daemon; + 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 daemons. + boolean restart = false; + for (String[] arguments : mArguments) { + restart = restart || (arguments != null); + } + if (!restart) { + return; + } + + // Start the daemon with arguments. + for (int i = 0; i < mDaemons.length; ++i) { + String[] arguments = mArguments[i]; + if (arguments == null) { + continue; + } + + // Start the daemon. + String daemon = mDaemons[i]; + SystemProperties.set("ctl.start", daemon); + + // Wait for the daemon to start. + String key = "init.svc." + daemon; + while (!"running".equals(SystemProperties.get(key))) { + checkpoint(true); + } + + // Create the control socket. + LocalSocket socket = new LocalSocket(); + LocalSocketAddress address = new LocalSocketAddress( + daemon, 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 out = socket.getOutputStream(); + for (String argument : arguments) { + byte[] bytes = argument.getBytes(Charsets.UTF_8); + if (bytes.length >= 0xFFFF) { + throw new IllegalArgumentException("argument too large"); + } + out.write(bytes.length >> 8); + out.write(bytes.length); + out.write(bytes); + checkpoint(false); + } + + // Send End-Of-Arguments. + out.write(0xFF); + out.write(0xFF); + out.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 daemon is dead. + for (int i = 0; i < mDaemons.length; ++i) { + String daemon = mDaemons[i]; + if (mArguments[i] != null && !"running".equals( + SystemProperties.get("init.svc." + daemon))) { + throw new IllegalArgumentException(daemon + " is dead"); + } + } + checkpoint(true); + } + + // Now we are connected. Get the interface. + mConfig.interfaceName = SystemProperties.get("vpn.via"); + + // Get the DNS servers if they are not set in config. + if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) { + String dnsServers = SystemProperties.get("vpn.dns").trim(); + if (!dnsServers.isEmpty()) { + mConfig.dnsServers = Arrays.asList(dnsServers.split(" ")); + } + } + + // TODO: support routes and search domains for IPSec Mode-CFG. + + // This is it! Here is the end of our journey! + synchronized (Vpn.this) { + // Check if the thread is interrupted while we are waiting. + checkpoint(false); + + if (mConfig.routes != null) { + jniSetRoutes(mConfig.interfaceName, mConfig.routes); + } + mInterfaceName = mConfig.interfaceName; + mCallback.override(mConfig.dnsServers, mConfig.searchDomains); + showNotification(mConfig, null, null); + } + Log.i(TAG, "Connected!"); + } catch (Exception e) { + Log.i(TAG, "Abort due to " + e.getMessage()); + for (String daemon : mDaemons) { + SystemProperties.set("ctl.stop", daemon); + } + } + } + } } diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 67e73f5fa10c..4fa3bda0b13f 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -132,7 +132,7 @@ public class GpsLocationProvider implements LocationProviderInterface { private static final int GPS_CAPABILITY_MSB = 0x0000002; private static final int GPS_CAPABILITY_MSA = 0x0000004; private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008; - + private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010; // these need to match AGpsType enum in gps.h private static final int AGPS_TYPE_SUPL = 1; @@ -200,6 +200,9 @@ public class GpsLocationProvider implements LocationProviderInterface { private boolean mInjectNtpTimePending = true; private boolean mDownloadXtraDataPending = true; + // set to true if the GPS engine does not do on-demand NTP time requests + private boolean mPeriodicTimeInjection; + // true if GPS is navigating private boolean mNavigating; @@ -549,10 +552,12 @@ public class GpsLocationProvider implements LocationProviderInterface { delay = RETRY_INTERVAL; } - // send delayed message for next NTP injection - // since this is delayed and not urgent we do not hold a wake lock here - mHandler.removeMessages(INJECT_NTP_TIME); - mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay); + if (mPeriodicTimeInjection) { + // send delayed message for next NTP injection + // since this is delayed and not urgent we do not hold a wake lock here + mHandler.removeMessages(INJECT_NTP_TIME); + mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay); + } } private void handleDownloadXtraData() { @@ -1305,6 +1310,11 @@ public class GpsLocationProvider implements LocationProviderInterface { */ private void setEngineCapabilities(int capabilities) { mEngineCapabilities = capabilities; + + if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) { + mPeriodicTimeInjection = true; + requestUtcTime(); + } } /** @@ -1438,6 +1448,14 @@ public class GpsLocationProvider implements LocationProviderInterface { } /** + * Called from native code to request utc time info + */ + + private void requestUtcTime() { + sendMessage(INJECT_NTP_TIME, 0, null); + } + + /** * Called from native code to request reference location info */ diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 12d3ed8b4dc5..2a17cbe7afda 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -25,6 +25,7 @@ import static android.Manifest.permission.READ_PHONE_STATE; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.*; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -57,6 +58,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.IConnectivityManager; @@ -71,6 +74,7 @@ import android.net.NetworkTemplate; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; +import android.os.INetworkManagementService; import android.os.IPowerManager; import android.os.Message; import android.os.RemoteCallbackList; @@ -109,6 +113,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import libcore.io.IoUtils; @@ -156,6 +161,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final IActivityManager mActivityManager; private final IPowerManager mPowerManager; private final INetworkStatsService mNetworkStats; + private final INetworkManagementService mNetworkManagement; private final TrustedTime mTime; private IConnectivityManager mConnManager; @@ -164,6 +170,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final Object mRulesLock = new Object(); private boolean mScreenOn; + private boolean mBackgroundData; /** Current policy for network templates. */ private ArrayList<NetworkPolicy> mNetworkPolicy = Lists.newArrayList(); @@ -192,11 +199,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // TODO: keep whitelist of system-critical services that should never have // rules enforced, such as system, phone, and radio UIDs. + // TODO: watch for package added broadcast to catch new UIDs. + public NetworkPolicyManagerService(Context context, IActivityManager activityManager, - IPowerManager powerManager, INetworkStatsService networkStats) { + IPowerManager powerManager, INetworkStatsService networkStats, + INetworkManagementService networkManagement) { // TODO: move to using cached NtpTrustedTime - this(context, activityManager, powerManager, networkStats, new NtpTrustedTime(), - getSystemDir()); + this(context, activityManager, powerManager, networkStats, networkManagement, + new NtpTrustedTime(), getSystemDir()); } private static File getSystemDir() { @@ -204,12 +214,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } public NetworkPolicyManagerService(Context context, IActivityManager activityManager, - IPowerManager powerManager, INetworkStatsService networkStats, TrustedTime time, - File systemDir) { + IPowerManager powerManager, INetworkStatsService networkStats, + INetworkManagementService networkManagement, + TrustedTime time, File systemDir) { mContext = checkNotNull(context, "missing context"); mActivityManager = checkNotNull(activityManager, "missing activityManager"); mPowerManager = checkNotNull(powerManager, "missing powerManager"); mNetworkStats = checkNotNull(networkStats, "missing networkStats"); + mNetworkManagement = checkNotNull(networkManagement, "missing networkManagement"); mTime = checkNotNull(time, "missing TrustedTime"); mHandlerThread = new HandlerThread(TAG); @@ -235,6 +247,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } updateScreenOn(); + updateBackgroundData(true); try { mActivityManager.registerProcessObserver(mProcessObserver); @@ -260,11 +273,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED); mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler); - // listen for warning polling events; currently dispatched by + // listen for stats update events final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED); mContext.registerReceiver( mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler); + // listen for changes to background data flag + final IntentFilter bgFilter = new IntentFilter(ACTION_BACKGROUND_DATA_SETTING_CHANGED); + mContext.registerReceiver(mBgReceiver, bgFilter, CONNECTIVITY_INTERNAL, mHandler); + } private IProcessObserver mProcessObserver = new IProcessObserver.Stub() { @@ -347,6 +364,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** + * Receiver that watches for + * {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}. + */ + private BroadcastReceiver mBgReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // on background handler thread, and verified CONNECTIVITY_INTERNAL + // permission above. + + synchronized (mRulesLock) { + updateBackgroundData(false); + } + } + }; + + /** * Check {@link NetworkPolicy} against current {@link INetworkStatsService} * to show visible notifications as needed. */ @@ -559,7 +592,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); - mMeteredIfaces.clear(); + final HashSet<String> newMeteredIfaces = Sets.newHashSet(); // apply each policy that we found ifaces for; compute remaining data // based on current cycle and historical stats, and push to kernel. @@ -589,14 +622,28 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { // remaining "quota" is based on usage in current cycle final long quota = Math.max(0, policy.limitBytes - total); - //kernelSetIfacesQuota(ifaces, quota); + + if (ifaces.length > 1) { + // TODO: switch to shared quota once NMS supports + Slog.w(TAG, "shared quota unsupported; generating rule for each iface"); + } for (String iface : ifaces) { - mMeteredIfaces.add(iface); + removeInterfaceQuota(iface); + setInterfaceQuota(iface, quota); + newMeteredIfaces.add(iface); } } } + // remove quota on any trailing interfaces + for (String iface : mMeteredIfaces) { + if (!newMeteredIfaces.contains(iface)) { + removeInterfaceQuota(iface); + } + } + mMeteredIfaces = newMeteredIfaces; + final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]); mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget(); } @@ -924,6 +971,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void updateBackgroundData(boolean systemReady) { + synchronized (mRulesLock) { + try { + mBackgroundData = mConnManager.getBackgroundDataSetting(); + } catch (RemoteException e) { + } + if (systemReady && mBackgroundData) { + // typical behavior of background enabled during systemReady; + // no need to clear rules for all UIDs. + } else { + updateRulesForBackgroundDataLocked(); + } + } + } + /** * Update rules that might be changed by {@link #mScreenOn} value. */ @@ -938,6 +1000,33 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + /** + * 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); @@ -948,14 +1037,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // uid in background, and policy says to block metered data uidRules = RULE_REJECT_METERED; } + if (!uidForeground && !mBackgroundData) { + // uid in background, and global background disabled + uidRules = RULE_REJECT_METERED; + } // TODO: only dispatch when rules actually change - // record rule locally to dispatch to new listeners - mUidRules.put(uid, uidRules); + if (uidRules == RULE_ALLOW_ALL) { + mUidRules.delete(uid); + } else { + mUidRules.put(uid, uidRules); + } final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0; - //kernelSetUidRejectPaid(uid, rejectPaid); + setUidNetworkRules(uid, rejectMetered); // dispatch changed rule to existing listeners mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget(); @@ -1003,6 +1099,36 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; + 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() { final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService( Context.TELEPHONY_SERVICE); diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 7610a1181d60..b4bd17690605 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -134,7 +134,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Settings that can be changed externally. */ public interface NetworkStatsSettings { - public boolean getEnabled(); public long getPollInterval(); public long getPersistThreshold(); public long getNetworkBucketDuration(); @@ -207,20 +206,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } public void systemReady() { - if (mSettings.getEnabled()) { - try { - // enable low-level bandwidth stats and control - // TODO: consider shipping with this enabled by default - mNetworkManager.setBandwidthControlEnabled(true); - } catch (RemoteException e) { - Slog.e(TAG, "problem talking to netd while enabling bandwidth controls", e); - } catch (NativeDaemonConnectorException ndce) { - Slog.e(TAG, "problem enabling bandwidth controls", ndce); - } - } else { - Slog.w(TAG, "detailed network stats disabled"); - } - synchronized (mStatsLock) { // read historical network stats from disk, since policy service // might need them right away. we delay loading detailed UID stats @@ -389,6 +374,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + @Override + public void forceUpdate() { + mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); + + synchronized (mStatsLock) { + performPollLocked(true, false); + } + } + /** * Receiver that watches for {@link IConnectivityManager} to claim network * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()} @@ -905,6 +899,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { argSet.add(arg); } + final boolean fullHistory = argSet.contains("full"); + synchronized (mStatsLock) { // TODO: remove this testing code, since it corrupts stats if (argSet.contains("generate")) { @@ -930,7 +926,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { for (NetworkIdentitySet ident : mNetworkStats.keySet()) { final NetworkStatsHistory history = mNetworkStats.get(ident); pw.print(" ident="); pw.println(ident.toString()); - history.dump(" ", pw); + history.dump(" ", pw, fullHistory); } if (argSet.contains("detail")) { @@ -950,7 +946,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStatsHistory history = uidStats.valueAt(i); pw.print(" UID="); pw.print(uid); pw.print(" tag="); pw.println(tag); - history.dump(" ", pw); + history.dump(" ", pw, fullHistory); } } } @@ -1058,15 +1054,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return Settings.Secure.getLong(mResolver, name, def); } - public boolean getEnabled() { - if (!new File("/proc/net/xt_qtaguid/ctrl").exists()) { - Slog.w(TAG, "kernel does not support bandwidth control"); - return false; - } - // TODO: once things stabilize, enable by default. - // For now: ./vendor/google/tools/override-gservices secure:netstats_enabled=1 - return Settings.Secure.getInt(mResolver, NETSTATS_ENABLED, 0) != 0; - } public long getPollInterval() { return getSecureLong(NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS); } diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java index d10aa97b23ad..11ccd60e566c 100644 --- a/services/java/com/android/server/pm/Installer.java +++ b/services/java/com/android/server/pm/Installer.java @@ -307,7 +307,7 @@ class Installer { } public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, - PackageStats pStats) { + String asecPath, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); @@ -315,17 +315,20 @@ class Installer { builder.append(apkPath); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); + builder.append(' '); + builder.append(asecPath != null ? asecPath : "!"); String s = transaction(builder.toString()); String res[] = s.split(" "); - if ((res == null) || (res.length != 4)) { + if ((res == null) || (res.length != 5)) { return -1; } try { pStats.codeSize = Long.parseLong(res[1]); pStats.dataSize = Long.parseLong(res[2]); pStats.cacheSize = Long.parseLong(res[3]); + pStats.externalCodeSize = Long.parseLong(res[4]); return Integer.parseInt(res[0]); } catch (NumberFormatException e) { return -1; diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 5a9dae9d0ab6..ea5d26b99056 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -75,6 +75,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileObserver; import android.os.FileUtils; +import android.os.FileUtils.FileStatus; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -156,7 +157,6 @@ public class PackageManagerService extends IPackageManager.Stub { private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; private static final int NFC_UID = Process.NFC_UID; - private static final int KEYCHAIN_UID = Process.KEYCHAIN_UID; static final int FIRST_APPLICATION_UID = Process.FIRST_APPLICATION_UID; static final int MAX_APPLICATION_UIDS = 1000; @@ -760,10 +760,6 @@ public class PackageManagerService extends IPackageManager.Stub { MULTIPLE_APPLICATION_UIDS ? NFC_UID : FIRST_APPLICATION_UID, ApplicationInfo.FLAG_SYSTEM); - mSettings.addSharedUserLPw("android.uid.keychain", - MULTIPLE_APPLICATION_UIDS - ? KEYCHAIN_UID : FIRST_APPLICATION_UID, - ApplicationInfo.FLAG_SYSTEM); String separateProcesses = SystemProperties.get("debug.separate_processes"); if (separateProcesses != null && separateProcesses.length() > 0) { @@ -4887,8 +4883,7 @@ public class PackageManagerService extends IPackageManager.Stub { private final IPackageStatsObserver mObserver; - public MeasureParams(PackageStats stats, boolean success, - IPackageStatsObserver observer) { + public MeasureParams(PackageStats stats, boolean success, IPackageStatsObserver observer) { mObserver = observer; mStats = stats; mSuccess = success; @@ -5480,6 +5475,17 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** + * Extract the MountService "container ID" from the full code path of an + * .apk. + */ + static String cidFromCodePath(String fullCodePath) { + int eidx = fullCodePath.lastIndexOf("/"); + String subStr1 = fullCodePath.substring(0, eidx); + int sidx = subStr1.lastIndexOf("/"); + return subStr1.substring(sidx+1, eidx); + } + class SdInstallArgs extends InstallArgs { static final String RES_FILE_NAME = "pkg.apk"; @@ -6831,6 +6837,7 @@ public class PackageManagerService extends IPackageManager.Stub { } PackageParser.Package p; boolean dataOnly = false; + String asecPath = null; synchronized (mPackages) { p = mPackages.get(packageName); if(p == null) { @@ -6842,6 +6849,12 @@ public class PackageManagerService extends IPackageManager.Stub { } p = ps.pkg; } + if (p != null && isExternal(p)) { + String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir); + if (secureContainerId != null) { + asecPath = PackageHelper.getSdFilesystem(secureContainerId); + } + } } String publicSrcDir = null; if(!dataOnly) { @@ -6850,10 +6863,13 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } - publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null; + if (isForwardLocked(p)) { + publicSrcDir = applicationInfo.publicSourceDir; + } } if (mInstaller != null) { - int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, pStats); + int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, + asecPath, pStats); if (res < 0) { return false; } else { diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index 918f1b6b8355..13a76b379264 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -45,7 +45,6 @@ import android.os.storage.StorageVolume; import android.os.SystemProperties; import android.os.UEventObserver; import android.provider.Settings; -import android.util.Log; import android.util.Slog; import java.io.File; @@ -62,7 +61,7 @@ import java.util.List; public class UsbDeviceManager { private static final String TAG = UsbDeviceManager.class.getSimpleName(); - private static final boolean LOG = false; + private static final boolean DEBUG = false; private static final String USB_STATE_MATCH = "DEVPATH=/devices/virtual/android_usb/android0"; @@ -93,18 +92,9 @@ public class UsbDeviceManager { private final UsbSettingsManager mSettingsManager; private NotificationManager mNotificationManager; private final boolean mHasUsbAccessory; - - // for USB connected notification - private boolean mUsbNotificationShown; private boolean mUseUsbNotification; - private Notification mUsbNotification; - - // for adb connected notification - private boolean mAdbNotificationShown; - private Notification mAdbNotification; private boolean mAdbEnabled; - private class AdbSettingsObserver extends ContentObserver { public AdbSettingsObserver() { super(null); @@ -117,115 +107,20 @@ public class UsbDeviceManager { } } - private void updateUsbNotification(boolean connected) { - if (mNotificationManager == null || !mUseUsbNotification) return; - if (connected) { - if (!mUsbNotificationShown) { - Resources r = mContext.getResources(); - CharSequence title = r.getText( - com.android.internal.R.string.usb_preferences_notification_title); - CharSequence message = r.getText( - com.android.internal.R.string.usb_preferece_notification_message); - - if (mUsbNotification == null) { - mUsbNotification = new Notification(); - mUsbNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb; - mUsbNotification.when = 0; - mUsbNotification.flags = Notification.FLAG_ONGOING_EVENT; - mUsbNotification.tickerText = title; - mUsbNotification.defaults = 0; // please be quiet - mUsbNotification.sound = null; - mUsbNotification.vibrate = null; - } - - Intent intent = new Intent(); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - intent.setClassName("com.android.systemui", - "com.android.systemui.usb.UsbPreferenceActivity"); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, - intent, 0); - - mUsbNotification.setLatestEventInfo(mContext, title, message, pi); - - mUsbNotificationShown = true; - mNotificationManager.notify( - com.android.internal.R.string.usb_preferences_notification_title, - mUsbNotification); - } - - } else if (mUsbNotificationShown) { - mUsbNotificationShown = false; - mNotificationManager.cancel( - com.android.internal.R.string.usb_preferences_notification_title); - } - } - - private void updateAdbNotification(boolean adbEnabled) { - if (mNotificationManager == null) return; - if (adbEnabled) { - if ("0".equals(SystemProperties.get("persist.adb.notify"))) return; - - if (!mAdbNotificationShown) { - Resources r = mContext.getResources(); - CharSequence title = r.getText( - com.android.internal.R.string.adb_active_notification_title); - CharSequence message = r.getText( - com.android.internal.R.string.adb_active_notification_message); - - if (mAdbNotification == null) { - mAdbNotification = new Notification(); - mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb; - mAdbNotification.when = 0; - mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; - mAdbNotification.tickerText = title; - mAdbNotification.defaults = 0; // please be quiet - mAdbNotification.sound = null; - mAdbNotification.vibrate = null; - } - - Intent intent = new Intent( - Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - // Note: we are hard-coding the component because this is - // an important security UI that we don't want anyone - // intercepting. - intent.setComponent(new ComponentName("com.android.settings", - "com.android.settings.DevelopmentSettings")); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, - intent, 0); - - mAdbNotification.setLatestEventInfo(mContext, title, message, pi); - - mAdbNotificationShown = true; - mNotificationManager.notify( - com.android.internal.R.string.adb_active_notification_title, - mAdbNotification); - } - } else if (mAdbNotificationShown) { - mAdbNotificationShown = false; - mNotificationManager.cancel( - com.android.internal.R.string.adb_active_notification_title); - } - } - /* * Listens for uevent messages from the kernel to monitor the USB state */ private final UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Slog.v(TAG, "USB UEVENT: " + event.toString()); - } + if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); String state = event.get("USB_STATE"); String accessory = event.get("ACCESSORY"); if (state != null) { mHandler.updateState(state); } else if ("START".equals(accessory)) { - Slog.d(TAG, "got accessory start"); + if (DEBUG) Slog.d(TAG, "got accessory start"); setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false); } } @@ -319,16 +214,32 @@ public class UsbDeviceManager { private String mDefaultFunctions; private UsbAccessory mCurrentAccessory; private boolean mDeferAccessoryAttached; + private int mUsbNotificationId; + private boolean mAdbNotificationShown; + + private static final int NOTIFICATION_NONE = 0; + private static final int NOTIFICATION_MTP = 1; + private static final int NOTIFICATION_PTP = 2; + private static final int NOTIFICATION_INSTALLER = 3; + private static final int NOTIFICATION_ADB = 4; public UsbHandler() { - // Read initial USB state try { + // sanity check the sys.usb.config system property + // this may be necessary if we crashed while switching USB configurations + String config = SystemProperties.get("sys.usb.config", "none"); + if (config.equals("none")) { + String persistConfig = SystemProperties.get("persist.sys.usb.config", "none"); + Slog.w(TAG, "resetting config to persistent property: " + persistConfig); + SystemProperties.set("sys.usb.config", persistConfig); + } + + // Read initial USB state mCurrentFunctions = FileUtils.readTextFile( new File(FUNCTIONS_PATH), 0, null).trim(); mDefaultFunctions = mCurrentFunctions; String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); - mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB); // Upgrade step for previous versions that used persist.service.adb.enable @@ -414,12 +325,12 @@ public class UsbDeviceManager { } catch (InterruptedException e) { } } - Log.e(TAG, "waitForState(" + state + ") FAILED"); + Slog.e(TAG, "waitForState(" + state + ") FAILED"); return false; } private boolean setUsbConfig(String config) { - Log.d(TAG, "setUsbConfig(" + config + ")"); + if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")"); // set the new configuration SystemProperties.set("sys.usb.config", config); return waitForState(config); @@ -428,7 +339,7 @@ public class UsbDeviceManager { private void doSetCurrentFunctions(String functions) { if (!mCurrentFunctions.equals(functions)) { if (!setUsbConfig("none") || !setUsbConfig(functions)) { - Log.e(TAG, "Failed to switch USB configuration to " + functions); + Slog.e(TAG, "Failed to switch USB configuration to " + functions); // revert to previous configuration if we fail setUsbConfig(mCurrentFunctions); } else { @@ -438,6 +349,7 @@ public class UsbDeviceManager { } private void setAdbEnabled(boolean enable) { + if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); if (enable != mAdbEnabled) { mAdbEnabled = enable; String functions; @@ -449,7 +361,7 @@ public class UsbDeviceManager { functions = removeFunction(mDefaultFunctions, UsbManager.USB_FUNCTION_ADB); } setCurrentFunction(functions, true); - updateAdbNotification(mAdbEnabled && mConnected); + updateAdbNotification(); } } @@ -469,7 +381,7 @@ public class UsbDeviceManager { String[] strings = nativeGetAccessoryStrings(); if (strings != null) { mCurrentAccessory = new UsbAccessory(strings); - Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); + Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); // defer accessoryAttached if system is not ready if (mSystemReady) { mSettingsManager.accessoryAttached(mCurrentAccessory); @@ -477,12 +389,12 @@ public class UsbDeviceManager { mDeferAccessoryAttached = true; } } else { - Log.e(TAG, "nativeGetAccessoryStrings failed"); + Slog.e(TAG, "nativeGetAccessoryStrings failed"); } } else if (!mConnected) { // make sure accessory mode is off // and restore default functions - Log.d(TAG, "exited USB accessory mode"); + Slog.d(TAG, "exited USB accessory mode"); setEnabledFunctions(mDefaultFunctions); if (mCurrentAccessory != null) { @@ -517,8 +429,8 @@ public class UsbDeviceManager { case MSG_UPDATE_STATE: mConnected = (msg.arg1 == 1); mConfigured = (msg.arg2 == 1); - updateUsbNotification(mConnected); - updateAdbNotification(mAdbEnabled && mConnected); + updateUsbNotification(); + updateAdbNotification(); if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { updateCurrentAccessory(); @@ -562,8 +474,8 @@ public class UsbDeviceManager { } break; case MSG_SYSTEM_READY: - updateUsbNotification(mConnected); - updateAdbNotification(mAdbEnabled && mConnected); + updateUsbNotification(); + updateAdbNotification(); updateUsbState(); if (mCurrentAccessory != null && mDeferAccessoryAttached) { mSettingsManager.accessoryAttached(mCurrentAccessory); @@ -576,6 +488,105 @@ public class UsbDeviceManager { return mCurrentAccessory; } + private void updateUsbNotification() { + if (mNotificationManager == null || !mUseUsbNotification) return; + if (mConnected) { + Resources r = mContext.getResources(); + CharSequence title = null; + int id = NOTIFICATION_NONE; + if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) { + title = r.getText( + com.android.internal.R.string.usb_mtp_notification_title); + id = NOTIFICATION_MTP; + } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) { + title = r.getText( + com.android.internal.R.string.usb_ptp_notification_title); + id = NOTIFICATION_PTP; + } else if (containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MASS_STORAGE)) { + title = r.getText( + com.android.internal.R.string.usb_cd_installer_notification_title); + id = NOTIFICATION_INSTALLER; + } else { + Slog.e(TAG, "No known USB function in updateUsbNotification"); + } + if (id != mUsbNotificationId) { + // clear notification if title needs changing + if (mUsbNotificationId != NOTIFICATION_NONE) { + mNotificationManager.cancel(mUsbNotificationId); + mUsbNotificationId = NOTIFICATION_NONE; + } + } + if (mUsbNotificationId == NOTIFICATION_NONE) { + CharSequence message = r.getText( + com.android.internal.R.string.usb_notification_message); + + Notification notification = new Notification(); + notification.icon = com.android.internal.R.drawable.stat_sys_data_usb; + notification.when = 0; + notification.flags = Notification.FLAG_ONGOING_EVENT; + notification.tickerText = title; + notification.defaults = 0; // please be quiet + notification.sound = null; + notification.vibrate = null; + + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + intent.setClassName("com.android.systemui", + "com.android.systemui.usb.UsbPreferenceActivity"); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, + intent, 0); + notification.setLatestEventInfo(mContext, title, message, pi); + mNotificationManager.notify(id, notification); + mUsbNotificationId = id; + } + + } else if (mUsbNotificationId != NOTIFICATION_NONE) { + mNotificationManager.cancel(mUsbNotificationId); + mUsbNotificationId = NOTIFICATION_NONE; + } + } + + private void updateAdbNotification() { + if (mNotificationManager == null) return; + if (mAdbEnabled && mConnected) { + if ("0".equals(SystemProperties.get("persist.adb.notify"))) return; + + if (!mAdbNotificationShown) { + Resources r = mContext.getResources(); + CharSequence title = r.getText( + com.android.internal.R.string.adb_active_notification_title); + CharSequence message = r.getText( + com.android.internal.R.string.adb_active_notification_message); + + Notification notification = new Notification(); + notification.icon = com.android.internal.R.drawable.stat_sys_adb; + notification.when = 0; + notification.flags = Notification.FLAG_ONGOING_EVENT; + notification.tickerText = title; + notification.defaults = 0; // please be quiet + notification.sound = null; + notification.vibrate = null; + + Intent intent = new Intent( + Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + intent.setComponent(new ComponentName("com.android.settings", + "com.android.settings.DevelopmentSettings")); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, + intent, 0); + notification.setLatestEventInfo(mContext, title, message, pi); + mAdbNotificationShown = true; + mNotificationManager.notify(NOTIFICATION_ADB, notification); + } + } else if (mAdbNotificationShown) { + mAdbNotificationShown = false; + mNotificationManager.cancel(NOTIFICATION_ADB); + } + } + public void dump(FileDescriptor fd, PrintWriter pw) { pw.println(" USB Device State:"); pw.println(" Current Functions: " + mCurrentFunctions); @@ -608,6 +619,7 @@ public class UsbDeviceManager { } public void setCurrentFunction(String function, boolean makeDefault) { + if (DEBUG) Slog.d(TAG, "setCurrentFunction(" + function + ") default: " + makeDefault); mHandler.sendMessage(MSG_SET_CURRENT_FUNCTION, function, makeDefault); } diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java index 923b0494f597..0a0ff598d7d8 100644 --- a/services/java/com/android/server/usb/UsbHostManager.java +++ b/services/java/com/android/server/usb/UsbHostManager.java @@ -37,7 +37,6 @@ import android.os.Parcelable; import android.os.ParcelFileDescriptor; import android.os.UEventObserver; import android.provider.Settings; -import android.util.Log; import android.util.Slog; import java.io.File; @@ -112,7 +111,7 @@ public class UsbHostManager { synchronized (mLock) { if (mDevices.get(deviceName) != null) { - Log.w(TAG, "device already on mDevices list: " + deviceName); + Slog.w(TAG, "device already on mDevices list: " + deviceName); return; } @@ -148,7 +147,7 @@ public class UsbHostManager { } catch (Exception e) { // beware of index out of bound exceptions, which might happen if // a device does not set bNumEndpoints correctly - Log.e(TAG, "error parsing USB descriptors", e); + Slog.e(TAG, "error parsing USB descriptors", e); return; } diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java index 911367711faf..0baafbb2bf72 100644 --- a/services/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/java/com/android/server/usb/UsbSettingsManager.java @@ -35,7 +35,7 @@ import android.hardware.usb.UsbManager; import android.os.Binder; import android.os.FileUtils; import android.os.Process; -import android.util.Log; +import android.util.Slog; import android.util.SparseBooleanArray; import android.util.Xml; @@ -62,6 +62,7 @@ import java.util.List; class UsbSettingsManager { private static final String TAG = "UsbSettingsManager"; + private static final boolean DEBUG = false; private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml"); private final Context mContext; @@ -410,9 +411,9 @@ class UsbSettingsManager { } } } catch (FileNotFoundException e) { - Log.w(TAG, "settings file not found"); + if (DEBUG) Slog.d(TAG, "settings file not found"); } catch (Exception e) { - Log.e(TAG, "error reading settings file, deleting to start fresh", e); + Slog.e(TAG, "error reading settings file, deleting to start fresh", e); sSettingsFile.delete(); } finally { if (stream != null) { @@ -428,7 +429,7 @@ class UsbSettingsManager { FileOutputStream fos = null; try { FileOutputStream fstr = new FileOutputStream(sSettingsFile); - Log.d(TAG, "writing settings to " + fstr); + if (DEBUG) Slog.d(TAG, "writing settings to " + fstr); BufferedOutputStream str = new BufferedOutputStream(fstr); FastXmlSerializer serializer = new FastXmlSerializer(); serializer.setOutput(str, "utf-8"); @@ -457,7 +458,7 @@ class UsbSettingsManager { FileUtils.sync(fstr); str.close(); } catch (Exception e) { - Log.e(TAG, "error writing settings file, deleting to start fresh", e); + Slog.e(TAG, "error writing settings file, deleting to start fresh", e); sSettingsFile.delete(); } } @@ -472,7 +473,7 @@ class UsbSettingsManager { try { parser = ai.loadXmlMetaData(mPackageManager, metaDataName); if (parser == null) { - Log.w(TAG, "no meta-data for " + info); + Slog.w(TAG, "no meta-data for " + info); return false; } @@ -494,7 +495,7 @@ class UsbSettingsManager { XmlUtils.nextElement(parser); } } catch (Exception e) { - Log.w(TAG, "Unable to load component info " + info.toString(), e); + Slog.w(TAG, "Unable to load component info " + info.toString(), e); } finally { if (parser != null) parser.close(); } @@ -553,7 +554,7 @@ class UsbSettingsManager { Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); intent.putExtra(UsbManager.EXTRA_DEVICE, device); - Log.d(TAG, "usbDeviceRemoved, sending " + intent); + if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent); mContext.sendBroadcast(intent); } @@ -604,7 +605,7 @@ class UsbSettingsManager { try { mContext.startActivity(dialogIntent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "unable to start UsbAccessoryUriActivity"); + Slog.e(TAG, "unable to start UsbAccessoryUriActivity"); } } } @@ -652,7 +653,7 @@ class UsbSettingsManager { defaultRI.activityInfo.name)); mContext.startActivity(intent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "startActivity failed", e); + Slog.e(TAG, "startActivity failed", e); } } else { Intent resolverIntent = new Intent(); @@ -679,7 +680,7 @@ class UsbSettingsManager { try { mContext.startActivity(resolverIntent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "unable to start activity " + resolverIntent); + Slog.e(TAG, "unable to start activity " + resolverIntent); } } } @@ -733,7 +734,7 @@ class UsbSettingsManager { XmlUtils.nextElement(parser); } } catch (Exception e) { - Log.w(TAG, "Unable to load component info " + aInfo.toString(), e); + Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e); } finally { if (parser != null) parser.close(); } @@ -751,7 +752,7 @@ class UsbSettingsManager { info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); } catch (NameNotFoundException e) { - Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e); + Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e); return; } @@ -831,7 +832,7 @@ class UsbSettingsManager { try { mContext.startActivity(intent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "unable to start UsbPermissionActivity"); + Slog.e(TAG, "unable to start UsbPermissionActivity"); } finally { Binder.restoreCallingIdentity(identity); } @@ -847,7 +848,7 @@ class UsbSettingsManager { try { pi.send(mContext, 0, intent); } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "requestPermission PendingIntent was cancelled"); + if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); } return; } @@ -867,7 +868,7 @@ class UsbSettingsManager { try { pi.send(mContext, 0, intent); } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "requestPermission PendingIntent was cancelled"); + if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); } return; } diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp index ae7fbfef80db..62d7636321c0 100644 --- a/services/jni/com_android_server_connectivity_Vpn.cpp +++ b/services/jni/com_android_server_connectivity_Vpn.cpp @@ -42,6 +42,9 @@ namespace android { +static int inet4 = -1; +static int inet6 = -1; + static inline in_addr_t *as_in_addr(sockaddr *sa) { return &((sockaddr_in *)sa)->sin_addr.s_addr; } @@ -51,11 +54,9 @@ static inline in_addr_t *as_in_addr(sockaddr *sa) { #define SYSTEM_ERROR -1 #define BAD_ARGUMENT -2 -static int create_interface(int mtu, char *name, int *index) +static int create_interface(int mtu) { - int tun = open("/dev/tun", O_RDWR); - int inet4 = socket(AF_INET, SOCK_DGRAM, 0); - int flags; + int tun = open("/dev/tun", O_RDWR | O_NONBLOCK); ifreq ifr4; memset(&ifr4, 0, sizeof(ifr4)); @@ -81,38 +82,45 @@ static int create_interface(int mtu, char *name, int *index) goto error; } - // Get interface index. - if (ioctl(inet4, SIOGIFINDEX, &ifr4)) { - LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno)); - goto error; - } - - // Make it non-blocking. - flags = fcntl(tun, F_GETFL, 0); - if (flags == -1 || fcntl(tun, F_SETFL, flags | O_NONBLOCK)) { - LOGE("Cannot set non-blocking on %s: %s", ifr4.ifr_name, strerror(errno)); - goto error; - } - - strcpy(name, ifr4.ifr_name); - *index = ifr4.ifr_ifindex; - close(inet4); return tun; error: close(tun); - close(inet4); return SYSTEM_ERROR; } -static int set_addresses(const char *name, int index, const char *addresses) +static int get_interface_name(char *name, int tun) +{ + ifreq ifr4; + if (ioctl(tun, TUNGETIFF, &ifr4)) { + LOGE("Cannot get interface name: %s", strerror(errno)); + return SYSTEM_ERROR; + } + strncpy(name, ifr4.ifr_name, IFNAMSIZ); + return 0; +} + +static int get_interface_index(const char *name) +{ + ifreq ifr4; + strncpy(ifr4.ifr_name, name, IFNAMSIZ); + if (ioctl(inet4, SIOGIFINDEX, &ifr4)) { + LOGE("Cannot get index of %s: %s", name, strerror(errno)); + return SYSTEM_ERROR; + } + return ifr4.ifr_ifindex; +} + +static int set_addresses(const char *name, const char *addresses) { - int inet4 = socket(AF_INET, SOCK_DGRAM, 0); - int inet6 = socket(AF_INET6, SOCK_DGRAM, 0); + int index = get_interface_index(name); + if (index < 0) { + return index; + } ifreq ifr4; memset(&ifr4, 0, sizeof(ifr4)); - strcpy(ifr4.ifr_name, name); + strncpy(ifr4.ifr_name, name, IFNAMSIZ); ifr4.ifr_addr.sa_family = AF_INET; in6_ifreq ifr6; @@ -121,7 +129,6 @@ static int set_addresses(const char *name, int index, const char *addresses) char address[65]; int prefix; - int chars; int count = 0; @@ -164,7 +171,7 @@ static int set_addresses(const char *name, int index, const char *addresses) break; } } - LOGV("Address added on %s: %s/%d", name, address, prefix); + LOGD("Address added on %s: %s/%d", name, address, prefix); ++count; } @@ -177,15 +184,15 @@ static int set_addresses(const char *name, int index, const char *addresses) count = BAD_ARGUMENT; } - close(inet4); - close(inet6); return count; } -static int set_routes(const char *name, int index, const char *routes) +static int set_routes(const char *name, const char *routes) { - int inet4 = socket(AF_INET, SOCK_DGRAM, 0); - int inet6 = socket(AF_INET6, SOCK_DGRAM, 0); + int index = get_interface_index(name); + if (index < 0) { + return index; + } rtentry rt4; memset(&rt4, 0, sizeof(rt4)); @@ -201,7 +208,6 @@ static int set_routes(const char *name, int index, const char *routes) char address[65]; int prefix; - int chars; int count = 0; @@ -211,32 +217,50 @@ static int set_routes(const char *name, int index, const char *routes) if (strchr(address, ':')) { // Add an IPv6 route. if (inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 || - prefix < 1 || prefix > 128) { + prefix < 0 || prefix > 128) { count = BAD_ARGUMENT; break; } - rt6.rtmsg_dst_len = prefix; + rt6.rtmsg_dst_len = prefix ? prefix : 1; if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) { count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; break; } + + if (!prefix) { + // Split the route instead of replacing the default route. + rt6.rtmsg_dst.s6_addr[0] ^= 0x80; + if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) { + count = SYSTEM_ERROR; + break; + } + } } else { // Add an IPv4 route. if (inet_pton(AF_INET, address, as_in_addr(&rt4.rt_dst)) != 1 || - prefix < 1 || prefix > 32) { + prefix < 0 || prefix > 32) { count = BAD_ARGUMENT; break; } - in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0; + in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0x80000000; *as_in_addr(&rt4.rt_genmask) = htonl(mask); if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) { count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; break; } + + if (!prefix) { + // Split the route instead of replacing the default route. + *as_in_addr(&rt4.rt_dst) ^= htonl(0x80000000); + if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) { + count = SYSTEM_ERROR; + break; + } + } } - LOGV("Route added on %s: %s/%d", name, address, prefix); + LOGD("Route added on %s: %s/%d", name, address, prefix); ++count; } @@ -250,43 +274,24 @@ static int set_routes(const char *name, int index, const char *routes) count = BAD_ARGUMENT; } - close(inet4); - close(inet6); return count; } -static int get_interface_name(char *name, int tun) -{ - ifreq ifr4; - if (ioctl(tun, TUNGETIFF, &ifr4)) { - LOGE("Cannot get interface name: %s", strerror(errno)); - return SYSTEM_ERROR; - } - strcpy(name, ifr4.ifr_name); - return 0; -} - static int reset_interface(const char *name) { - int inet4 = socket(AF_INET, SOCK_DGRAM, 0); - ifreq ifr4; - ifr4.ifr_flags = 0; strncpy(ifr4.ifr_name, name, IFNAMSIZ); + ifr4.ifr_flags = 0; if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) { LOGE("Cannot reset %s: %s", name, strerror(errno)); - close(inet4); return SYSTEM_ERROR; } - close(inet4); return 0; } static int check_interface(const char *name) { - int inet4 = socket(AF_INET, SOCK_DGRAM, 0); - ifreq ifr4; strncpy(ifr4.ifr_name, name, IFNAMSIZ); ifr4.ifr_flags = 0; @@ -294,7 +299,6 @@ static int check_interface(const char *name) if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) { LOGE("Cannot check %s: %s", name, strerror(errno)); } - close(inet4); return ifr4.ifr_flags; } @@ -318,86 +322,108 @@ static void throwException(JNIEnv *env, int error, const char *message) } } -static jint establish(JNIEnv *env, jobject thiz, - jint mtu, jstring jAddresses, jstring jRoutes) +static jint createInterface(JNIEnv *env, jobject thiz, jint mtu) { - char name[IFNAMSIZ]; - int index; - int tun = create_interface(mtu, name, &index); + int tun = create_interface(mtu); if (tun < 0) { throwException(env, tun, "Cannot create interface"); return -1; } - LOGD("%s is created", name); + return tun; +} - const char *addresses; - const char *routes; - int count; +static jstring getInterfaceName(JNIEnv *env, jobject thiz, jint tun) +{ + char name[IFNAMSIZ]; + if (get_interface_name(name, tun) < 0) { + throwException(env, SYSTEM_ERROR, "Cannot get interface name"); + return NULL; + } + return env->NewStringUTF(name); +} - // Addresses are required. +static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName, + jstring jAddresses) +{ + const char *name = NULL; + const char *addresses = NULL; + int count = -1; + + name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + goto error; + } addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL; if (!addresses) { - jniThrowNullPointerException(env, "address"); + jniThrowNullPointerException(env, "addresses"); goto error; } - count = set_addresses(name, index, addresses); - env->ReleaseStringUTFChars(jAddresses, addresses); - if (count <= 0) { + count = set_addresses(name, addresses); + if (count < 0) { throwException(env, count, "Cannot set address"); - goto error; + count = -1; } - LOGD("Configured %d address(es) on %s", count, name); - - // Routes are optional. - routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL; - if (routes) { - count = set_routes(name, index, routes); - env->ReleaseStringUTFChars(jRoutes, routes); - if (count < 0) { - throwException(env, count, "Cannot set route"); - goto error; - } - LOGD("Configured %d route(s) on %s", count, name); - } - - return tun; error: - close(tun); - LOGD("%s is destroyed", name); - return -1; + if (name) { + env->ReleaseStringUTFChars(jName, name); + } + if (addresses) { + env->ReleaseStringUTFChars(jAddresses, addresses); + } + return count; } -static jstring getName(JNIEnv *env, jobject thiz, jint fd) +static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName, + jstring jRoutes) { - char name[IFNAMSIZ]; - if (get_interface_name(name, fd) < 0) { - throwException(env, SYSTEM_ERROR, "Cannot get interface name"); - return NULL; + const char *name = NULL; + const char *routes = NULL; + int count = -1; + + name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + goto error; } - return env->NewStringUTF(name); + routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL; + if (!routes) { + jniThrowNullPointerException(env, "routes"); + goto error; + } + count = set_routes(name, routes); + if (count < 0) { + throwException(env, count, "Cannot set route"); + count = -1; + } + +error: + if (name) { + env->ReleaseStringUTFChars(jName, name); + } + if (routes) { + env->ReleaseStringUTFChars(jRoutes, routes); + } + return count; } -static void reset(JNIEnv *env, jobject thiz, jstring jName) +static void resetInterface(JNIEnv *env, jobject thiz, jstring jName) { - const char *name = jName ? - env->GetStringUTFChars(jName, NULL) : NULL; + const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; if (!name) { jniThrowNullPointerException(env, "name"); return; } if (reset_interface(name) < 0) { throwException(env, SYSTEM_ERROR, "Cannot reset interface"); - } else { - LOGD("%s is deactivated", name); } env->ReleaseStringUTFChars(jName, name); } -static jint check(JNIEnv *env, jobject thiz, jstring jName) +static jint checkInterface(JNIEnv *env, jobject thiz, jstring jName) { - const char *name = jName ? - env->GetStringUTFChars(jName, NULL) : NULL; + const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; if (!name) { jniThrowNullPointerException(env, "name"); return 0; @@ -407,10 +433,9 @@ static jint check(JNIEnv *env, jobject thiz, jstring jName) return flags; } -static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName) +static void protectSocket(JNIEnv *env, jobject thiz, jint fd, jstring jName) { - const char *name = jName ? - env->GetStringUTFChars(jName, NULL) : NULL; + const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; if (!name) { jniThrowNullPointerException(env, "name"); return; @@ -424,15 +449,23 @@ static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName) //------------------------------------------------------------------------------ static JNINativeMethod gMethods[] = { - {"nativeEstablish", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)establish}, - {"nativeGetName", "(I)Ljava/lang/String;", (void *)getName}, - {"nativeReset", "(Ljava/lang/String;)V", (void *)reset}, - {"nativeCheck", "(Ljava/lang/String;)I", (void *)check}, - {"nativeProtect", "(ILjava/lang/String;)V", (void *)protect}, + {"jniCreateInterface", "(I)I", (void *)createInterface}, + {"jniGetInterfaceName", "(I)Ljava/lang/String;", (void *)getInterfaceName}, + {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses}, + {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes}, + {"jniResetInterface", "(Ljava/lang/String;)V", (void *)resetInterface}, + {"jniCheckInterface", "(Ljava/lang/String;)I", (void *)checkInterface}, + {"jniProtectSocket", "(ILjava/lang/String;)V", (void *)protectSocket}, }; int register_android_server_connectivity_Vpn(JNIEnv *env) { + if (inet4 == -1) { + inet4 = socket(AF_INET, SOCK_DGRAM, 0); + } + if (inet6 == -1) { + inet6 = socket(AF_INET6, SOCK_DGRAM, 0); + } return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn", gMethods, NELEM(gMethods)); } diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp index 6d4ad9ac4a88..87ffcba240da 100755 --- a/services/jni/com_android_server_location_GpsLocationProvider.cpp +++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp @@ -42,6 +42,7 @@ static jmethodID method_xtraDownloadRequest; static jmethodID method_reportNiNotification; static jmethodID method_requestRefLocation; static jmethodID method_requestSetID; +static jmethodID method_requestUtcTime; static const GpsInterface* sGpsInterface = NULL; static const GpsXtraInterface* sGpsXtraInterface = NULL; @@ -122,6 +123,13 @@ static void release_wakelock_callback() release_wake_lock(WAKE_LOCK_NAME); } +static void request_utc_time_callback() +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg) { return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg); @@ -137,6 +145,7 @@ GpsCallbacks sGpsCallbacks = { acquire_wakelock_callback, release_wakelock_callback, create_thread_callback, + request_utc_time_callback, }; static void xtra_download_request_callback() @@ -232,6 +241,7 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V"); method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V"); method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V"); + method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V"); err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); if (err == 0) { diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index f0b19f26e93e..685613efef09 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -78,7 +78,6 @@ const String16 sDump("android.permission.DUMP"); SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), Thread(false), mTransactionFlags(0), - mTransactionCount(0), mResizeTransationPending(false), mLayersRemoved(false), mBootTime(systemTime()), @@ -385,13 +384,11 @@ bool SurfaceFlinger::threadLoop() handleConsoleEvents(); } - if (LIKELY(mTransactionCount == 0)) { - // if we're in a global transaction, don't do anything. - const uint32_t mask = eTransactionNeeded | eTraversalNeeded; - uint32_t transactionFlags = peekTransactionFlags(mask); - if (LIKELY(transactionFlags)) { - handleTransaction(transactionFlags); - } + // if we're in a global transaction, don't do anything. + const uint32_t mask = eTransactionNeeded | eTraversalNeeded; + uint32_t transactionFlags = peekTransactionFlags(mask); + if (UNLIKELY(transactionFlags)) { + handleTransaction(transactionFlags); } // post surfaces (if needed) @@ -1176,28 +1173,33 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) return old; } -void SurfaceFlinger::openGlobalTransaction() -{ - android_atomic_inc(&mTransactionCount); -} -void SurfaceFlinger::closeGlobalTransaction() -{ - if (android_atomic_dec(&mTransactionCount) == 1) { - signalEvent(); +void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state) { + Mutex::Autolock _l(mStateLock); - // if there is a transaction with a resize, wait for it to - // take effect before returning. - Mutex::Autolock _l(mStateLock); - while (mResizeTransationPending) { - status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); - if (CC_UNLIKELY(err != NO_ERROR)) { - // just in case something goes wrong in SF, return to the - // called after a few seconds. - LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); - mResizeTransationPending = false; - break; - } + uint32_t flags = 0; + const size_t count = state.size(); + for (size_t i=0 ; i<count ; i++) { + const ComposerState& s(state[i]); + sp<Client> client( static_cast<Client *>(s.client.get()) ); + flags |= setClientStateLocked(client, s.state); + } + if (flags) { + setTransactionFlags(flags); + } + + signalEvent(); + + // if there is a transaction with a resize, wait for it to + // take effect before returning. + while (mResizeTransationPending) { + status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); + if (CC_UNLIKELY(err != NO_ERROR)) { + // just in case something goes wrong in SF, return to the + // called after a few seconds. + LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); + mResizeTransationPending = false; + break; } } } @@ -1393,60 +1395,52 @@ status_t SurfaceFlinger::destroySurface(const wp<LayerBaseClient>& layer) return err; } -status_t SurfaceFlinger::setClientState( +uint32_t SurfaceFlinger::setClientStateLocked( const sp<Client>& client, - int32_t count, - const layer_state_t* states) + const layer_state_t& s) { - Mutex::Autolock _l(mStateLock); uint32_t flags = 0; - for (int i=0 ; i<count ; i++) { - const layer_state_t& s(states[i]); - sp<LayerBaseClient> layer(client->getLayerUser(s.surface)); - if (layer != 0) { - const uint32_t what = s.what; - if (what & ePositionChanged) { - if (layer->setPosition(s.x, s.y)) - flags |= eTraversalNeeded; - } - if (what & eLayerChanged) { - ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); - if (layer->setLayer(s.z)) { - mCurrentState.layersSortedByZ.removeAt(idx); - mCurrentState.layersSortedByZ.add(layer); - // we need traversal (state changed) - // AND transaction (list changed) - flags |= eTransactionNeeded|eTraversalNeeded; - } - } - if (what & eSizeChanged) { - if (layer->setSize(s.w, s.h)) { - flags |= eTraversalNeeded; - mResizeTransationPending = true; - } - } - if (what & eAlphaChanged) { - if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f))) - flags |= eTraversalNeeded; - } - if (what & eMatrixChanged) { - if (layer->setMatrix(s.matrix)) - flags |= eTraversalNeeded; - } - if (what & eTransparentRegionChanged) { - if (layer->setTransparentRegionHint(s.transparentRegion)) - flags |= eTraversalNeeded; + sp<LayerBaseClient> layer(client->getLayerUser(s.surface)); + if (layer != 0) { + const uint32_t what = s.what; + if (what & ePositionChanged) { + if (layer->setPosition(s.x, s.y)) + flags |= eTraversalNeeded; + } + if (what & eLayerChanged) { + ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); + if (layer->setLayer(s.z)) { + mCurrentState.layersSortedByZ.removeAt(idx); + mCurrentState.layersSortedByZ.add(layer); + // we need traversal (state changed) + // AND transaction (list changed) + flags |= eTransactionNeeded|eTraversalNeeded; } - if (what & eVisibilityChanged) { - if (layer->setFlags(s.flags, s.mask)) - flags |= eTraversalNeeded; + } + if (what & eSizeChanged) { + if (layer->setSize(s.w, s.h)) { + flags |= eTraversalNeeded; + mResizeTransationPending = true; } } + if (what & eAlphaChanged) { + if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f))) + flags |= eTraversalNeeded; + } + if (what & eMatrixChanged) { + if (layer->setMatrix(s.matrix)) + flags |= eTraversalNeeded; + } + if (what & eTransparentRegionChanged) { + if (layer->setTransparentRegionHint(s.transparentRegion)) + flags |= eTraversalNeeded; + } + if (what & eVisibilityChanged) { + if (layer->setFlags(s.flags, s.mask)) + flags |= eTraversalNeeded; + } } - if (flags) { - setTransactionFlags(flags); - } - return NO_ERROR; + return flags; } void SurfaceFlinger::screenReleased(int dpy) @@ -1588,8 +1582,7 @@ status_t SurfaceFlinger::onTransact( { switch (code) { case CREATE_CONNECTION: - case OPEN_GLOBAL_TRANSACTION: - case CLOSE_GLOBAL_TRANSACTION: + case SET_TRANSACTION_STATE: case SET_ORIENTATION: case FREEZE_DISPLAY: case UNFREEZE_DISPLAY: @@ -1806,10 +1799,10 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() const GLfloat h = hw_h - (hw_h * v); const GLfloat x = (hw_w - w) * 0.5f; const GLfloat y = (hw_h - h) * 0.5f; - vtx[0] = x; vtx[1] = y; - vtx[2] = x; vtx[3] = y + h; - vtx[4] = x + w; vtx[5] = y + h; - vtx[6] = x + w; vtx[7] = y; + vtx[0] = x; vtx[1] = y + h; + vtx[2] = x; vtx[3] = y; + vtx[4] = x + w; vtx[5] = y; + vtx[6] = x + w; vtx[7] = y + h; } }; @@ -1824,10 +1817,10 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() const GLfloat h = 1.0f; const GLfloat x = (hw_w - w) * 0.5f; const GLfloat y = (hw_h - h) * 0.5f; - vtx[0] = x; vtx[1] = y; - vtx[2] = x; vtx[3] = y + h; - vtx[4] = x + w; vtx[5] = y + h; - vtx[6] = x + w; vtx[7] = y; + vtx[0] = x; vtx[1] = y + h; + vtx[2] = x; vtx[3] = y; + vtx[4] = x + w; vtx[5] = y; + vtx[6] = x + w; vtx[7] = y + h; } }; @@ -2469,9 +2462,6 @@ sp<ISurface> Client::createSurface( status_t Client::destroySurface(SurfaceID sid) { return mFlinger->removeSurface(this, sid); } -status_t Client::setState(int32_t count, const layer_state_t* states) { - return mFlinger->setClientState(this, count, states); -} // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 45f80aedc24a..b49fa363e62f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -70,14 +70,12 @@ public: sp<LayerBaseClient> getLayerUser(int32_t i) const; private: - // ISurfaceComposerClient interface virtual sp<ISurface> createSurface( surface_data_t* params, const String8& name, DisplayID display, uint32_t w, uint32_t h,PixelFormat format, uint32_t flags); virtual status_t destroySurface(SurfaceID surfaceId); - virtual status_t setState(int32_t count, const layer_state_t* states); virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); @@ -168,8 +166,7 @@ public: virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc(); virtual sp<IMemoryHeap> getCblk() const; virtual void bootFinished(); - virtual void openGlobalTransaction(); - virtual void closeGlobalTransaction(); + virtual void setTransactionState(const Vector<ComposerState>& state); virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags); virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags); virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags); @@ -220,8 +217,7 @@ private: status_t removeSurface(const sp<Client>& client, SurfaceID sid); status_t destroySurface(const wp<LayerBaseClient>& layer); - status_t setClientState(const sp<Client>& client, - int32_t count, const layer_state_t* states); + uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s); class LayerVector : public SortedVector< sp<LayerBase> > { public: @@ -337,7 +333,6 @@ private: mutable Mutex mStateLock; State mCurrentState; volatile int32_t mTransactionFlags; - volatile int32_t mTransactionCount; Condition mTransactionCV; SortedVector< sp<LayerBase> > mLayerPurgatory; bool mResizeTransationPending; diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp index 18c54b3f8419..56b2a8f1cb03 100644 --- a/services/surfaceflinger/tests/resize/resize.cpp +++ b/services/surfaceflinger/tests/resize/resize.cpp @@ -43,9 +43,9 @@ int main(int argc, char** argv) PIXEL_FORMAT_RGB_565); - client->openTransaction(); + SurfaceComposerClient::openGlobalTransaction(); surface->setLayer(100000); - client->closeTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); Surface::SurfaceInfo info; surface->lock(&info); @@ -57,9 +57,9 @@ int main(int argc, char** argv) android_memset16((uint16_t*)info.bits, 0x07E0, bpr*info.h); surface->unlockAndPost(); - client->openTransaction(); + SurfaceComposerClient::openGlobalTransaction(); surface->setSize(320, 240); - client->closeTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); IPCThreadState::self()->joinThreadPool(); diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp index 5265f919536b..8e1c3fe322b4 100644 --- a/services/surfaceflinger/tests/surface/surface.cpp +++ b/services/surfaceflinger/tests/surface/surface.cpp @@ -39,9 +39,9 @@ int main(int argc, char** argv) sp<SurfaceControl> surfaceControl = client->createSurface( getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565); - client->openTransaction(); + SurfaceComposerClient::openGlobalTransaction(); surfaceControl->setLayer(100000); - client->closeTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); // pretend it went cross-process Parcel parcel; diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java index 8b752ee47468..ac7cb5afd53a 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java @@ -16,8 +16,8 @@ package com.android.server; -import static dalvik.system.BlockGuard.kernelToTag; -import static dalvik.system.BlockGuard.tagToKernel; +import static com.android.server.NetworkManagementSocketTagger.kernelToTag; +import static com.android.server.NetworkManagementSocketTagger.tagToKernel; import android.content.res.Resources; import android.net.NetworkStats; diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 324e89600294..b4ac987fc541 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -56,6 +56,7 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkTemplate; import android.os.Binder; +import android.os.INetworkManagementService; import android.os.IPowerManager; import android.test.AndroidTestCase; import android.test.mock.MockPackageManager; @@ -95,6 +96,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private IActivityManager mActivityManager; private IPowerManager mPowerManager; private INetworkStatsService mStatsService; + private INetworkManagementService mNetworkManagement; private INetworkPolicyListener mPolicyListener; private TrustedTime mTime; private IConnectivityManager mConnManager; @@ -150,13 +152,15 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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); @@ -175,6 +179,9 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce(); expectTime(System.currentTimeMillis()); + // default behavior is background data enabled + expect(mConnManager.getBackgroundDataSetting()).andReturn(true); + replay(); mService.systemReady(); verifyAndReset(); @@ -253,6 +260,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { public void testScreenChangesRules() throws Exception { Future<Void> future; + expectSetUidNetworkRules(UID_A, false); future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); @@ -260,6 +268,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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); @@ -268,6 +277,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { // now turn screen off and verify REJECT rule expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce(); + expectSetUidNetworkRules(UID_A, true); future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF)); @@ -276,6 +286,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { // and turn screen back on, verify ALLOW rule restored expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce(); + expectSetUidNetworkRules(UID_A, false); future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON)); @@ -286,6 +297,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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); @@ -293,6 +305,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifyAndReset(); // POLICY_NONE should RULE_ALLOW in foreground + expectSetUidNetworkRules(UID_A, false); future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mService.setUidPolicy(UID_A, POLICY_NONE); @@ -300,6 +313,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifyAndReset(); // POLICY_NONE should RULE_ALLOW in background + expectSetUidNetworkRules(UID_A, false); future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); @@ -311,6 +325,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { Future<Void> future; // POLICY_REJECT should RULE_ALLOW in background + expectSetUidNetworkRules(UID_A, true); future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); @@ -318,6 +333,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifyAndReset(); // POLICY_REJECT should RULE_ALLOW in foreground + expectSetUidNetworkRules(UID_A, false); future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); @@ -325,6 +341,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifyAndReset(); // POLICY_REJECT should RULE_REJECT in background + expectSetUidNetworkRules(UID_A, true); future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); @@ -336,6 +353,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { Future<Void> future; // POLICY_NONE should have RULE_ALLOW in background + expectSetUidNetworkRules(UID_A, false); future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); @@ -344,6 +362,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifyAndReset(); // adding POLICY_REJECT should cause RULE_REJECT + expectSetUidNetworkRules(UID_A, true); future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); @@ -351,6 +370,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifyAndReset(); // removing POLICY_REJECT should return us to RULE_ALLOW + expectSetUidNetworkRules(UID_A, false); future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); mService.setUidPolicy(UID_A, POLICY_NONE); @@ -431,8 +451,9 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10)) .andReturn(stats).atLeastOnce(); - // expect that quota remaining should be 1536 bytes - // TODO: write up NetworkManagementService mock + // TODO: consider making strongly ordered mock + expectRemoveInterfaceQuota(TEST_IFACE); + expectSetInterfaceQuota(TEST_IFACE, 1536L); expectClearNotifications(); future = expectMeteredIfacesChanged(TEST_IFACE); @@ -447,6 +468,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { Future<Void> future; // POLICY_REJECT should RULE_REJECT in background + expectSetUidNetworkRules(UID_A, true); future = expectRulesChanged(UID_A, RULE_REJECT_METERED); replay(); mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); @@ -454,6 +476,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifyAndReset(); // uninstall should clear RULE_REJECT + expectSetUidNetworkRules(UID_A, false); future = expectRulesChanged(UID_A, RULE_ALLOW_ALL); replay(); final Intent intent = new Intent(ACTION_UID_REMOVED); @@ -494,6 +517,22 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expectLastCall().anyTimes(); } + private void expectSetInterfaceQuota(String iface, long quota) throws Exception { + mNetworkManagement.setInterfaceQuota(iface, quota); + expectLastCall().atLeastOnce(); + } + + 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)); @@ -526,14 +565,14 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } 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 903f2b0b6f66..f2c28bb538c8 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -610,9 +610,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mAlarmManager.setInexactRepeating( eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class)); expectLastCall().atLeastOnce(); - - mNetManager.setBandwidthControlEnabled(true); - expectLastCall().atLeastOnce(); } private void expectNetworkState(NetworkState... state) throws Exception { @@ -633,7 +630,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory) throws Exception { - expect(mSettings.getEnabled()).andReturn(true).anyTimes(); expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes(); expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes(); expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes(); diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java index dea67f35bfde..ffabb7b6e72e 100644 --- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java +++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java @@ -94,9 +94,6 @@ public class PhoneNumberFormattingTextWatcher implements TextWatcher { */ public PhoneNumberFormattingTextWatcher(String countryCode) { if (countryCode == null) throw new IllegalArgumentException(); - // TODO: remove this once CountryDetector.detectCountry().getCountryIso() is fixed to always - // return uppercase. Tracked at b/4941319. - countryCode = countryCode.toUpperCase(Locale.ENGLISH); mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode); } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index fce7cdc82440..2f010e57b161 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -473,8 +473,8 @@ public class ServiceState implements Parcelable { + " EmergOnly=" + mIsEmergencyOnly); } - public void setStateOutOfService() { - mState = STATE_OUT_OF_SERVICE; + private void setNullState(int state) { + mState = state; mRoaming = false; mOperatorAlphaLong = null; mOperatorAlphaShort = null; @@ -491,23 +491,12 @@ public class ServiceState implements Parcelable { mIsEmergencyOnly = false; } - // TODO - can't this be combined with the above method? + public void setStateOutOfService() { + setNullState(STATE_OUT_OF_SERVICE); + } + public void setStateOff() { - mState = STATE_POWER_OFF; - mRoaming = false; - mOperatorAlphaLong = null; - mOperatorAlphaShort = null; - mOperatorNumeric = null; - mIsManualNetworkSelection = false; - mRadioTechnology = 0; - mCssIndicator = false; - mNetworkId = -1; - mSystemId = -1; - mCdmaRoamingIndicator = -1; - mCdmaDefaultRoamingIndicator = -1; - mCdmaEriIconIndex = -1; - mCdmaEriIconMode = -1; - mIsEmergencyOnly = false; + setNullState(STATE_POWER_OFF); } public void setState(int state) { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 184d665d0579..66120a171dd9 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -191,10 +191,6 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ public String getDeviceId() { - if (!isVoiceCapable()) { - return null; - } - try { return getSubscriberInfo().getDeviceId(); } catch (RemoteException ex) { diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index ab93e2a90ea9..68e0045eda45 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -526,16 +526,6 @@ public class CallerInfo { + countryIso); } - // Temp workaround: The current libphonenumber library requires - // the countryIso to be uppercase (e.g. "US") but the - // detector.detectCountry().getCountryIso() call currently returns - // "us". Passing "us" to util.parse() will just result in a - // NumberParseException. - // So force the countryIso to uppercase for now. - // TODO: remove this once getCountryIso() is fixed to always - // return uppercase. - countryIso = countryIso.toUpperCase(); - PhoneNumber pn = null; try { if (VDBG) Log.v(TAG, "parsing '" + number diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java index 910905aa4543..aa7568b108f8 100644 --- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java +++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java @@ -21,6 +21,7 @@ import android.net.LinkProperties; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; +import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.util.Log; @@ -55,8 +56,13 @@ public class DefaultPhoneNotifier implements PhoneNotifier { } public void notifyServiceState(Phone sender) { + ServiceState ss = sender.getServiceState(); + if (ss == null) { + ss = new ServiceState(); + ss.setStateOutOfService(); + } try { - mRegistry.notifyServiceState(sender.getServiceState()); + mRegistry.notifyServiceState(ss); } catch (RemoteException ex) { // system process is dead } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 5fc0bf9b51e2..a6b131a91106 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -556,6 +556,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (DBG) log("onDataConnectionAttached: start polling notify attached"); startNetStatPoll(); notifyDataConnection(Phone.REASON_DATA_ATTACHED); + } else { + // update APN availability so that APN can be enabled. + notifyDataAvailability(Phone.REASON_DATA_ATTACHED); } setupDataOnReadyApns(Phone.REASON_DATA_ATTACHED); @@ -785,13 +788,13 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext); apnContext.getDataConnection().tearDown(apnContext.getReason(), msg); apnContext.setState(State.DISCONNECTING); - } else { - // apn is connected but no reference to dcac. - // Should not be happen, but reset the state in case. - apnContext.setState(State.IDLE); - mPhone.notifyDataConnection(apnContext.getReason(), - apnContext.getApnType()); } + } else { + // apn is connected but no reference to dcac. + // Should not be happen, but reset the state in case. + apnContext.setState(State.IDLE); + mPhone.notifyDataConnection(apnContext.getReason(), + apnContext.getApnType()); } } } else { @@ -1528,13 +1531,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { createAllApnList(); if (mRadioAvailable) { if (DBG) log("onRecordsLoaded: notifying data availability"); - notifyDataAvailability(null); + notifyDataAvailability(Phone.REASON_SIM_LOADED); } setupDataOnReadyApns(Phone.REASON_SIM_LOADED); } @Override protected void onSetDependencyMet(String apnType, boolean met) { + // don't allow users to tweak hipri to work around default dependency not met + if (Phone.APN_TYPE_HIPRI.equals(apnType)) return; + ApnContext apnContext = mApnContexts.get(apnType); if (apnContext == null) { loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" + @@ -1542,6 +1548,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return; } applyNewState(apnContext, apnContext.isEnabled(), met); + if (Phone.APN_TYPE_DEFAULT.equals(apnType)) { + // tie actions on default to similar actions on HIPRI regarding dependencyMet + apnContext = mApnContexts.get(Phone.APN_TYPE_HIPRI); + if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met); + } } private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) { @@ -1627,11 +1638,17 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { @Override protected void onRoamingOff() { if (DBG) log("onRoamingOff"); + // Notify data availability so APN can be enabled. + notifyDataAvailability(Phone.REASON_ROAMING_OFF); + setupDataOnReadyApns(Phone.REASON_ROAMING_OFF); } @Override protected void onRoamingOn() { + // Notify data availability so APN can be enabled. + notifyDataAvailability(Phone.REASON_ROAMING_ON); + if (getDataOnRoamingEnabled()) { if (DBG) log("onRoamingOn: setup data on roaming"); setupDataOnReadyApns(Phone.REASON_ROAMING_ON); diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java index c4a6f53dc34a..6e9d0f9da62d 100644 --- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java @@ -502,16 +502,17 @@ public class PhoneNumberUtilsTest extends AndroidTestCase { @SmallTest public void testFormatNumberToE164() { - assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "us")); - assertNull(PhoneNumberUtils.formatNumberToE164("1234567", "us")); - assertEquals("+18004664114", PhoneNumberUtils.formatNumberToE164("800-GOOG-114", "us")); + // Note: ISO 3166-1 only allows upper case country codes. + assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "US")); + assertNull(PhoneNumberUtils.formatNumberToE164("1234567", "US")); + assertEquals("+18004664114", PhoneNumberUtils.formatNumberToE164("800-GOOG-114", "US")); } @SmallTest public void testFormatNumber() { - assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "us")); - assertEquals("123-4567", PhoneNumberUtils.formatNumber("1234567", "us")); - assertEquals("(800) 466-4114", PhoneNumberUtils.formatNumber("800-GOOG-114", "us")); + assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "US")); + assertEquals("123-4567", PhoneNumberUtils.formatNumber("1234567", "US")); + assertEquals("(800) 466-4114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US")); } diff --git a/tests/GridLayoutTest/res/layout/grid3.xml b/tests/GridLayoutTest/res/layout/grid3.xml index ba911c269e4e..536be7eefa03 100644 --- a/tests/GridLayoutTest/res/layout/grid3.xml +++ b/tests/GridLayoutTest/res/layout/grid3.xml @@ -66,8 +66,8 @@ <Space android:layout_row="4" android:layout_column="2" - android:layout_rowWeight="1" - android:layout_columnWeight="1" + android:layout_heightSpec="canStretch" + android:layout_widthSpec="canStretch" /> <Button diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java index e010a007529f..cba98c2b7538 100644 --- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java @@ -97,8 +97,8 @@ public class Activity2 extends Activity { Space v = new Space(context); { LayoutParams lp = new LayoutParams(row5, col3); - lp.rowWeight = 1; - lp.columnWeight = 1; + lp.widthSpec = CAN_STRETCH; + lp.heightSpec = CAN_STRETCH; vg.addView(v, lp); } } diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 7aa0617a702a..873414338fd3 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -31,6 +31,15 @@ android:hardwareAccelerated="true"> <activity + android:name="TimeDialogActivity" + android:label="_TimeDialog"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name="OpaqueActivity" android:label="_Opaque"> <intent-filter> @@ -494,8 +503,7 @@ <activity android:name="Animated3dActivity" - android:label="_Animated 3d" - android:theme="@android:style/Theme.Translucent.NoTitleBar"> + android:label="_Animated 3d"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java new file mode 100644 index 000000000000..9e3e950f0850 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.app.TimePickerDialog; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.widget.Button; +import android.widget.FrameLayout; + +@SuppressWarnings({"UnusedDeclaration"}) +public class TimeDialogActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FrameLayout layout = new FrameLayout(this); + Button b = new Button(this); + b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); + b.setText("Show dialog"); + b.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + new TimePickerDialog(TimeDialogActivity.this, null, 12, 12, true).show(); + } + }); + layout.addView(b); + + setContentView(layout); + } +} diff --git a/tests/RenderScriptTests/PerfTest/AndroidManifest.xml b/tests/RenderScriptTests/PerfTest/AndroidManifest.xml index aafd176838b9..cc6039685736 100644 --- a/tests/RenderScriptTests/PerfTest/AndroidManifest.xml +++ b/tests/RenderScriptTests/PerfTest/AndroidManifest.xml @@ -9,8 +9,7 @@ android:icon="@drawable/test_pattern"> <uses-library android:name="android.test.runner" /> <activity android:name="RsBench" - android:label="RsBenchmark" - android:theme="@android:style/Theme.Black.NoTitleBar"> + android:label="RsBenchmark"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml b/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml new file mode 100644 index 000000000000..823467758827 --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml @@ -0,0 +1,25 @@ +<?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. +*/ +--> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/benchmark_mode" + android:title="@string/benchmark_mode" /> + <item android:id="@+id/debug_mode" + android:title="@string/debug_mode" /> +</menu> diff --git a/tests/RenderScriptTests/PerfTest/res/values/strings.xml b/tests/RenderScriptTests/PerfTest/res/values/strings.xml new file mode 100644 index 000000000000..627ac212db8d --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/res/values/strings.xml @@ -0,0 +1,24 @@ +<?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. +*/ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <skip /> + <string name="benchmark_mode">Benchmark Mode</string> + <string name="debug_mode">Debug Mode</string> +</resources> diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java index d7393f8065cd..b336a4d03550 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java @@ -31,10 +31,14 @@ import android.provider.Settings.System; import android.util.Log; import android.view.Menu; import android.view.MenuItem; +import android.view.MenuInflater; import android.view.View; import android.view.Window; import android.widget.Button; import android.widget.ListView; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.widget.Toast; import java.lang.Runtime; @@ -77,4 +81,37 @@ public class RsBench extends Activity { super.onPause(); mView.pause(); } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.loader_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection + switch (item.getItemId()) { + case R.id.benchmark_mode: + mView.setBenchmarkMode(); + return true; + case R.id.debug_mode: + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Pick a Test"); + builder.setItems(mView.getTestNames(), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + Toast.makeText(getApplicationContext(), + "Switching to: " + mView.getTestNames()[item], + Toast.LENGTH_SHORT).show(); + mView.setDebugMode(item); + } + }); + builder.show(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java index c706286df685..c375be5e58bc 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java @@ -84,12 +84,6 @@ public class RsBenchRS { private Resources mRes; private RenderScriptGL mRS; - private Sampler mLinearClamp; - private Sampler mLinearWrap; - private Sampler mMipLinearWrap; - private Sampler mNearestClamp; - private Sampler mNearesWrap; - private ProgramStore mProgStoreBlendNoneDepth; private ProgramStore mProgStoreBlendNone; private ProgramStore mProgStoreBlendAlpha; @@ -115,10 +109,6 @@ public class RsBenchRS { private ScriptField_FragentShaderConstants3_s mFSConstPixel; - private ProgramRaster mCullBack; - private ProgramRaster mCullFront; - private ProgramRaster mCullNone; - private Allocation mTexTorus; private Allocation mTexOpaque; private Allocation mTexTransparent; @@ -143,6 +133,8 @@ public class RsBenchRS { private ScriptC_rsbench mScript; + private ScriptC_text_test mTextScript; + private ScriptC_torus_test mTorusScript; private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); @@ -310,6 +302,7 @@ public class RsBenchRS { mProgStoreBlendAdd = BLEND_ADD_DEPTH_NONE(mRS); mScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth); + mScript.set_gProgStoreBlendNone(mProgStoreBlendNone); mScript.set_gProgStoreBlendAlpha(mProgStoreBlendAlpha); mScript.set_gProgStoreBlendAdd(mProgStoreBlendAdd); @@ -330,22 +323,24 @@ public class RsBenchRS { texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); mProgFragmentTexture = texBuilder.create(); - mProgFragmentTexture.bindSampler(mLinearClamp, 0); + mProgFragmentTexture.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0); ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS); colBuilder.setVaryingColor(false); mProgFragmentColor = colBuilder.create(); mScript.set_gProgFragmentColor(mProgFragmentColor); + mScript.set_gProgFragmentTexture(mProgFragmentTexture); + // For Galaxy live wallpaper drawing ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS); builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, ProgramFragmentFixedFunction.Builder.Format.RGB, 0); ProgramFragment pfb = builder.create(); - pfb.bindSampler(mNearesWrap, 0); + pfb.bindSampler(Sampler.WRAP_NEAREST(mRS), 0); mScript.set_gPFBackground(pfb); builder = new ProgramFragmentFixedFunction.Builder(mRS); @@ -354,7 +349,7 @@ public class RsBenchRS { ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); builder.setVaryingColor(true); ProgramFragment pfs = builder.create(); - pfs.bindSampler(mMipLinearWrap, 0); + pfs.bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS), 0); mScript.set_gPFStars(pfs); } @@ -404,6 +399,7 @@ public class RsBenchRS { mScript.set_gProgVertex(mProgVertex); + // For galaxy live wallpaper mPvStarAlloc = new ScriptField_VpConsts(mRS, 1); mScript.bind_vpConstants(mPvStarAlloc); @@ -447,13 +443,11 @@ public class RsBenchRS { private void initCustomShaders() { mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1); mFSConst = new ScriptField_FragentShaderConstants_s(mRS, 1); - mScript.bind_gVSConstants(mVSConst); - mScript.bind_gFSConstants(mFSConst); + mVSConstPixel = new ScriptField_VertexShaderConstants3_s(mRS, 1); mFSConstPixel = new ScriptField_FragentShaderConstants3_s(mRS, 1); - mScript.bind_gVSConstPixel(mVSConstPixel); - mScript.bind_gFSConstPixel(mFSConstPixel); + // Initialize the shader builder ProgramVertex.Builder pvbCustom = new ProgramVertex.Builder(mRS); @@ -506,11 +500,7 @@ public class RsBenchRS { } mProgFragmentMultitex = pfbCustom.create(); - mScript.set_gProgVertexCustom(mProgVertexCustom); - mScript.set_gProgFragmentCustom(mProgFragmentCustom); - mScript.set_gProgVertexPixelLight(mProgVertexPixelLight); - mScript.set_gProgVertexPixelLightMove(mProgVertexPixelLightMove); - mScript.set_gProgFragmentPixelLight(mProgFragmentPixelLight); + mScript.set_gProgFragmentMultitex(mProgFragmentMultitex); } @@ -587,39 +577,22 @@ public class RsBenchRS { Log.e("rs", "could not load model"); } else { mTorus = (Mesh)entry.getObject(); - mScript.set_gTorusMesh(mTorus); } createParticlesMesh(); } private void initSamplers() { - Sampler.Builder bs = new Sampler.Builder(mRS); - bs.setMinification(Sampler.Value.LINEAR); - bs.setMagnification(Sampler.Value.LINEAR); - bs.setWrapS(Sampler.Value.WRAP); - bs.setWrapT(Sampler.Value.WRAP); - mLinearWrap = bs.create(); - - mLinearClamp = Sampler.CLAMP_LINEAR(mRS); - mNearestClamp = Sampler.CLAMP_NEAREST(mRS); - mMipLinearWrap = Sampler.WRAP_LINEAR_MIP_LINEAR(mRS); - mNearesWrap = Sampler.WRAP_NEAREST(mRS); - - mScript.set_gLinearClamp(mLinearClamp); - mScript.set_gLinearWrap(mLinearWrap); - mScript.set_gMipLinearWrap(mMipLinearWrap); - mScript.set_gNearestClamp(mNearestClamp); + mScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS)); + mScript.set_gLinearWrap(Sampler.WRAP_LINEAR(mRS)); + mScript.set_gMipLinearWrap(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS)); + mScript.set_gNearestClamp(Sampler.CLAMP_NEAREST(mRS)); } private void initProgramRaster() { - mCullBack = ProgramRaster.CULL_BACK(mRS); - mCullFront = ProgramRaster.CULL_FRONT(mRS); - mCullNone = ProgramRaster.CULL_NONE(mRS); - - mScript.set_gCullBack(mCullBack); - mScript.set_gCullFront(mCullFront); - mScript.set_gCullNone(mCullNone); + mScript.set_gCullBack(ProgramRaster.CULL_BACK(mRS)); + mScript.set_gCullFront(ProgramRaster.CULL_FRONT(mRS)); + mScript.set_gCullNone(ProgramRaster.CULL_NONE(mRS)); } private int strlen(byte[] array) { @@ -645,9 +618,47 @@ public class RsBenchRS { } } + public void setDebugMode(int num) { + mScript.invoke_setDebugMode(num); + } + + public void setBenchmarkMode() { + mScript.invoke_setBenchmarkMode(); + } + + void initTextScript() { + mTextScript = new ScriptC_text_test(mRS, mRes, R.raw.text_test); + mTextScript.set_gFontSans(mFontSans); + mTextScript.set_gFontSerif(mFontSerif); + } + + void initTorusScript() { + mTorusScript = new ScriptC_torus_test(mRS, mRes, R.raw.torus_test); + mTorusScript.set_gCullFront(ProgramRaster.CULL_FRONT(mRS)); + mTorusScript.set_gCullBack(ProgramRaster.CULL_BACK(mRS)); + mTorusScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS)); + mTorusScript.set_gTorusMesh(mTorus); + mTorusScript.set_gTexTorus(mTexTorus); + mTorusScript.set_gProgVertexCustom(mProgVertexCustom); + mTorusScript.set_gProgFragmentCustom(mProgFragmentCustom); + mTorusScript.set_gProgVertexPixelLight(mProgVertexPixelLight); + mTorusScript.set_gProgVertexPixelLightMove(mProgVertexPixelLightMove); + mTorusScript.set_gProgFragmentPixelLight(mProgFragmentPixelLight); + mTorusScript.bind_gVSConstPixel(mVSConstPixel); + mTorusScript.bind_gFSConstPixel(mFSConstPixel); + mTorusScript.bind_gVSConstants(mVSConst); + mTorusScript.bind_gFSConstants(mFSConst); + mTorusScript.set_gProgVertex(mProgVertex); + mTorusScript.set_gProgFragmentTexture(mProgFragmentTexture); + mTorusScript.set_gProgFragmentColor(mProgFragmentColor); + mTorusScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth); + } + private void initRS() { mScript = new ScriptC_rsbench(mRS, mRes, R.raw.rsbench); + + mRS.setMessageHandler(mRsMessage); mMaxModes = mScript.get_gMaxModes(); @@ -709,6 +720,13 @@ public class RsBenchRS { mSampleListViewAllocs.copyAll(); mScript.bind_gListViewText(mSampleListViewAllocs); + initTextScript(); + initTorusScript(); + + mScript.set_gFontScript(mTextScript); + mScript.set_gTorusScript(mTorusScript); + mScript.set_gDummyAlloc(Allocation.createSized(mRS, Element.I32(mRS), 1)); + mRS.bindRootScript(mScript); } } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java index 2882b931c589..61aa3e129395 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java @@ -105,4 +105,16 @@ public class RsBenchView extends RSSurfaceView { public boolean testIsFinished() { return mRender.testIsFinished(); } + + void setBenchmarkMode() { + mRender.setBenchmarkMode(); + } + + void setDebugMode(int num) { + mRender.setDebugMode(num); + } + + String[] getTestNames() { + return mRender.mTestNames; + } } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs index bb81862e4e30..eaafe1db3d40 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs @@ -18,6 +18,7 @@ #include "rs_graphics.rsh" #include "shader_def.rsh" +#include "subtest_def.rsh" /* Message sent from script to renderscript */ const int RS_MSG_TEST_DONE = 100; @@ -98,7 +99,6 @@ ListAllocs *gListViewText; rs_mesh g10by10Mesh; rs_mesh g100by100Mesh; rs_mesh gWbyHMesh; -rs_mesh gTorusMesh; rs_mesh gSingleMesh; rs_font gFontSans; @@ -115,25 +115,15 @@ rs_program_raster gCullBack; rs_program_raster gCullFront; rs_program_raster gCullNone; -// Custom vertex shader compunents -VertexShaderConstants *gVSConstants; -FragentShaderConstants *gFSConstants; -VertexShaderConstants3 *gVSConstPixel; -FragentShaderConstants3 *gFSConstPixel; // Export these out to easily set the inputs to shader VertexShaderInputs *gVSInputs; -// Custom shaders we use for lighting -rs_program_vertex gProgVertexCustom; -rs_program_fragment gProgFragmentCustom; -rs_program_vertex gProgVertexPixelLight; -rs_program_vertex gProgVertexPixelLightMove; -rs_program_fragment gProgFragmentPixelLight; + rs_program_fragment gProgFragmentMultitex; rs_allocation gRenderBufferColor; rs_allocation gRenderBufferDepth; -float gDt = 0; +static float gDt = 0; void init() { } @@ -141,16 +131,6 @@ void init() { static int gRenderSurfaceW; static int gRenderSurfaceH; -static const char *sampleText = "This is a sample of small text for performace"; -// Offsets for multiple layer of text -static int textOffsets[] = { 0, 0, -5, -5, 5, 5, -8, -8, 8, 8}; -static float textColors[] = {1.0f, 1.0f, 1.0f, 1.0f, - 0.5f, 0.7f, 0.5f, 1.0f, - 0.7f, 0.5f, 0.5f, 1.0f, - 0.5f, 0.5f, 0.7f, 1.0f, - 0.5f, 0.6f, 0.7f, 1.0f, -}; - /** * Methods to draw the galaxy live wall paper */ @@ -291,40 +271,16 @@ static void setupOffscreenTarget() { rsgBindDepthTarget(gRenderBufferDepth); } -static void displayFontSamples(int fillNum) { +rs_script gFontScript; +rs_script gTorusScript; +rs_allocation gDummyAlloc; - rs_font fonts[5]; - fonts[0] = gFontSans; - fonts[1] = gFontSerif; - fonts[2] = gFontSans; - fonts[3] = gFontSerif; - fonts[4] = gFontSans; - - uint width = gRenderSurfaceW; - uint height = gRenderSurfaceH; - int left = 0, right = 0, top = 0, bottom = 0; - rsgMeasureText(sampleText, &left, &right, &top, &bottom); - - int textHeight = top - bottom; - int textWidth = right - left; - int numVerticalLines = height / textHeight; - int yPos = top; - - int xOffset = 0, yOffset = 0; - for(int fillI = 0; fillI < fillNum; fillI ++) { - rsgBindFont(fonts[fillI]); - xOffset = textOffsets[fillI * 2]; - yOffset = textOffsets[fillI * 2 + 1]; - float *colPtr = textColors + fillI * 4; - rsgFontColor(colPtr[0], colPtr[1], colPtr[2], colPtr[3]); - for (int h = 0; h < 4; h ++) { - yPos = top + yOffset; - for (int v = 0; v < numVerticalLines; v ++) { - rsgDrawText(sampleText, xOffset + textWidth * h, yPos); - yPos += textHeight; - } - } - } +static void displayFontSamples(int fillNum) { + TestData testData; + testData.renderSurfaceW = gRenderSurfaceW; + testData.renderSurfaceH = gRenderSurfaceH; + testData.user = fillNum; + rsForEach(gFontScript, gDummyAlloc, gDummyAlloc, &testData); } static void bindProgramVertexOrtho() { @@ -555,212 +511,37 @@ static void displayLiveWallPaper(int wResolution, int hResolution) { drawMeshInPage(2.0f*gRenderSurfaceW, 0, wResolution, hResolution); } -static float gTorusRotation = 0; -static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) { - if (buffer == 0) { - rsgProgramVertexLoadModelMatrix(matrix); - } else { - rsgAllocationSyncAll(rsGetAllocation(buffer)); - } -} - -static void drawToruses(int numMeshes, rs_matrix4x4 *matrix, void *buffer) { - - if (numMeshes == 1) { - rsMatrixLoadTranslate(matrix, 0.0f, 0.0f, -7.5f); - rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); - updateModelMatrix(matrix, buffer); - rsgDrawMesh(gTorusMesh); - return; - } - - if (numMeshes == 2) { - rsMatrixLoadTranslate(matrix, -1.6f, 0.0f, -7.5f); - rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); - updateModelMatrix(matrix, buffer); - rsgDrawMesh(gTorusMesh); - - rsMatrixLoadTranslate(matrix, 1.6f, 0.0f, -7.5f); - rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); - updateModelMatrix(matrix, buffer); - rsgDrawMesh(gTorusMesh); - return; - } - - float startX = -5.0f; - float startY = -1.5f; - float startZ = -15.0f; - float dist = 3.2f; - - for (int h = 0; h < 4; h ++) { - for (int v = 0; v < 2; v ++) { - // Position our model on the screen - rsMatrixLoadTranslate(matrix, startX + dist * h, startY + dist * v, startZ); - rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); - updateModelMatrix(matrix, buffer); - rsgDrawMesh(gTorusMesh); - } - } -} - // Quick hack to get some geometry numbers static void displaySimpleGeoSamples(bool useTexture, int numMeshes) { - rsgBindProgramVertex(gProgVertex); - rsgBindProgramRaster(gCullBack); - // Setup the projection matrix with 30 degree field of view - rs_matrix4x4 proj; - float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH; - rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f); - rsgProgramVertexLoadProjectionMatrix(&proj); - - // Fragment shader with texture - rsgBindProgramStore(gProgStoreBlendNoneDepth); - if (useTexture) { - rsgBindProgramFragment(gProgFragmentTexture); - } else { - rsgBindProgramFragment(gProgFragmentColor); - rsgProgramFragmentConstantColor(gProgFragmentColor, 0.1, 0.7, 0.1, 1); - } - rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); - rsgBindTexture(gProgFragmentTexture, 0, gTexTorus); - - // Apply a rotation to our mesh - gTorusRotation += 50.0f * gDt; - if (gTorusRotation > 360.0f) { - gTorusRotation -= 360.0f; - } - - rs_matrix4x4 matrix; - drawToruses(numMeshes, &matrix, 0); -} - -float gLight0Rotation = 0; -float gLight1Rotation = 0; - -static void setupCustomShaderLights() { - float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f}; - float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f}; - float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f}; - float4 light0SpecCol = {0.9f, 0.6f, 0.6f, 1.0f}; - float4 light1DiffCol = {0.5f, 0.5f, 0.9f, 1.0f}; - float4 light1SpecCol = {0.5f, 0.5f, 0.9f, 1.0f}; - - gLight0Rotation += 50.0f * gDt; - if (gLight0Rotation > 360.0f) { - gLight0Rotation -= 360.0f; - } - gLight1Rotation -= 50.0f * gDt; - if (gLight1Rotation > 360.0f) { - gLight1Rotation -= 360.0f; - } - - rs_matrix4x4 l0Mat; - rsMatrixLoadRotate(&l0Mat, gLight0Rotation, 1.0f, 0.0f, 0.0f); - light0Pos = rsMatrixMultiply(&l0Mat, light0Pos); - rs_matrix4x4 l1Mat; - rsMatrixLoadRotate(&l1Mat, gLight1Rotation, 0.0f, 0.0f, 1.0f); - light1Pos = rsMatrixMultiply(&l1Mat, light1Pos); - - // Set light 0 properties - gVSConstants->light0_Posision = light0Pos; - gVSConstants->light0_Diffuse = 1.0f; - gVSConstants->light0_Specular = 0.5f; - gVSConstants->light0_CosinePower = 10.0f; - // Set light 1 properties - gVSConstants->light1_Posision = light1Pos; - gVSConstants->light1_Diffuse = 1.0f; - gVSConstants->light1_Specular = 0.7f; - gVSConstants->light1_CosinePower = 25.0f; - rsgAllocationSyncAll(rsGetAllocation(gVSConstants)); - - // Update fragment shader constants - // Set light 0 colors - gFSConstants->light0_DiffuseColor = light0DiffCol; - gFSConstants->light0_SpecularColor = light0SpecCol; - // Set light 1 colors - gFSConstants->light1_DiffuseColor = light1DiffCol; - gFSConstants->light1_SpecularColor = light1SpecCol; - rsgAllocationSyncAll(rsGetAllocation(gFSConstants)); - - // Set light 0 properties for per pixel lighting - gFSConstPixel->light0_Posision = light0Pos; - gFSConstPixel->light0_Diffuse = 1.0f; - gFSConstPixel->light0_Specular = 0.5f; - gFSConstPixel->light0_CosinePower = 10.0f; - gFSConstPixel->light0_DiffuseColor = light0DiffCol; - gFSConstPixel->light0_SpecularColor = light0SpecCol; - // Set light 1 properties - gFSConstPixel->light1_Posision = light1Pos; - gFSConstPixel->light1_Diffuse = 1.0f; - gFSConstPixel->light1_Specular = 0.7f; - gFSConstPixel->light1_CosinePower = 25.0f; - gFSConstPixel->light1_DiffuseColor = light1DiffCol; - gFSConstPixel->light1_SpecularColor = light1SpecCol; - rsgAllocationSyncAll(rsGetAllocation(gFSConstPixel)); + TestData testData; + testData.renderSurfaceW = gRenderSurfaceW; + testData.renderSurfaceH = gRenderSurfaceH; + testData.dt = gDt; + testData.user = 0; + testData.user1 = useTexture ? 1 : 0; + testData.user2 = numMeshes; + rsForEach(gTorusScript, gDummyAlloc, gDummyAlloc, &testData); } static void displayCustomShaderSamples(int numMeshes) { - - // Update vertex shader constants - // Load model matrix - // Apply a rotation to our mesh - gTorusRotation += 50.0f * gDt; - if (gTorusRotation > 360.0f) { - gTorusRotation -= 360.0f; - } - - // Setup the projection matrix - float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH; - rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f); - setupCustomShaderLights(); - - rsgBindProgramVertex(gProgVertexCustom); - - // Fragment shader with texture - rsgBindProgramStore(gProgStoreBlendNoneDepth); - rsgBindProgramFragment(gProgFragmentCustom); - rsgBindSampler(gProgFragmentCustom, 0, gLinearClamp); - rsgBindTexture(gProgFragmentCustom, 0, gTexTorus); - - // Use back face culling - rsgBindProgramRaster(gCullBack); - - drawToruses(numMeshes, &gVSConstants->model, gVSConstants); + TestData testData; + testData.renderSurfaceW = gRenderSurfaceW; + testData.renderSurfaceH = gRenderSurfaceH; + testData.dt = gDt; + testData.user = 1; + testData.user1 = numMeshes; + rsForEach(gTorusScript, gDummyAlloc, gDummyAlloc, &testData); } static void displayPixelLightSamples(int numMeshes, bool heavyVertex) { - - // Update vertex shader constants - // Load model matrix - // Apply a rotation to our mesh - gTorusRotation += 30.0f * gDt; - if (gTorusRotation > 360.0f) { - gTorusRotation -= 360.0f; - } - - gVSConstPixel->time = rsUptimeMillis()*0.005; - - // Setup the projection matrix - float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH; - rsMatrixLoadPerspective(&gVSConstPixel->proj, 30.0f, aspect, 0.1f, 100.0f); - setupCustomShaderLights(); - - if (heavyVertex) { - rsgBindProgramVertex(gProgVertexPixelLightMove); - } else { - rsgBindProgramVertex(gProgVertexPixelLight); - } - - // Fragment shader with texture - rsgBindProgramStore(gProgStoreBlendNoneDepth); - rsgBindProgramFragment(gProgFragmentPixelLight); - rsgBindSampler(gProgFragmentPixelLight, 0, gLinearClamp); - rsgBindTexture(gProgFragmentPixelLight, 0, gTexTorus); - - // Use back face culling - rsgBindProgramRaster(gCullBack); - - drawToruses(numMeshes, &gVSConstPixel->model, gVSConstPixel); + TestData testData; + testData.renderSurfaceW = gRenderSurfaceW; + testData.renderSurfaceH = gRenderSurfaceH; + testData.dt = gDt; + testData.user = 2; + testData.user1 = numMeshes; + testData.user2 = heavyVertex ? 1 : 0; + rsForEach(gTorusScript, gDummyAlloc, gDummyAlloc, &testData); } static void displayMultitextureSample(bool blend, int quadCount) { @@ -862,6 +643,20 @@ static const char *testNames[] = { "UI test with live wallpaper", }; +static bool gIsDebugMode = false; +void setDebugMode(int testNumber) { + gIsDebugMode = true; + benchMode = testNumber; + rsgClearAllRenderTargets(); +} + +void setBenchmarkMode() { + gIsDebugMode = false; + benchMode = 0; + runningLoops = 0; +} + + void getTestName(int testIndex) { int bufferLen = rsAllocationGetDimX(rsGetAllocation(gStringBuffer)); if (testIndex >= gMaxModes) { @@ -932,7 +727,7 @@ static void runTest(int index) { displaySingletexFill(true, 10); break; case 18: - displayMultitextureSample(true, 8); + displayMultitextureSample(true, 10); break; case 19: displayPixelLightSamples(1, false); @@ -992,14 +787,7 @@ static void drawOffscreenResult(int posX, int posY, int width, int height) { startX + width, startY, 0, 1, 1); } -int root(void) { - gRenderSurfaceW = rsgGetWidth(); - gRenderSurfaceH = rsgGetHeight(); - rsgClearColor(0.2f, 0.2f, 0.2f, 1.0f); - rsgClearDepth(1.0f); - if(!checkInit()) { - return 1; - } +static void benchmark() { gDt = 1.0f / 60.0f; @@ -1045,8 +833,6 @@ int root(void) { benchMode ++; - gTorusRotation = 0; - if (benchMode == gMaxModes) { rsSendToClientBlocking(RS_MSG_RESULTS_READY, gResultBuffer, gMaxModes*sizeof(float)); benchMode = 0; @@ -1058,5 +844,30 @@ int root(void) { sendMsgFlag = true; } } + +} + +static void debug() { + gDt = rsGetDt(); + + rsgFinish(); + runTest(benchMode); +} + +int root(void) { + gRenderSurfaceW = rsgGetWidth(); + gRenderSurfaceH = rsgGetHeight(); + rsgClearColor(0.2f, 0.2f, 0.2f, 1.0f); + rsgClearDepth(1.0f); + if(!checkInit()) { + return 1; + } + + if (gIsDebugMode) { + debug(); + } else { + benchmark(); + } + return 1; } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh new file mode 100644 index 000000000000..b635373b2afa --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh @@ -0,0 +1,28 @@ +// 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. + +#pragma version(1) + +#pragma rs java_package_name(com.android.perftest) + +typedef struct TestData_s { + int renderSurfaceW; + int renderSurfaceH; + float dt; + int user; + int user1; + int user2; + int user3; +} TestData; + diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs new file mode 100644 index 000000000000..0df6b3590bd5 --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs @@ -0,0 +1,82 @@ +// 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. + +#pragma version(1) + +#pragma rs java_package_name(com.android.perftest) + +#include "rs_graphics.rsh" +#include "subtest_def.rsh" + +rs_font gFontSans; +rs_font gFontSerif; + +void init() { +} + +static int gRenderSurfaceW = 1280; +static int gRenderSurfaceH = 720; + +static const char *sampleText = "This is a sample of small text for performace"; +// Offsets for multiple layer of text +static int textOffsets[] = { 0, 0, -5, -5, 5, 5, -8, -8, 8, 8}; +static float textColors[] = {1.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.7f, 0.5f, 1.0f, + 0.7f, 0.5f, 0.5f, 1.0f, + 0.5f, 0.5f, 0.7f, 1.0f, + 0.5f, 0.6f, 0.7f, 1.0f, +}; + +static void displayFontSamples(int fillNum) { + + rs_font fonts[5]; + fonts[0] = gFontSans; + fonts[1] = gFontSerif; + fonts[2] = gFontSans; + fonts[3] = gFontSerif; + fonts[4] = gFontSans; + + uint width = gRenderSurfaceW; + uint height = gRenderSurfaceH; + int left = 0, right = 0, top = 0, bottom = 0; + rsgMeasureText(sampleText, &left, &right, &top, &bottom); + + int textHeight = top - bottom; + int textWidth = right - left; + int numVerticalLines = height / textHeight; + int yPos = top; + + int xOffset = 0, yOffset = 0; + for(int fillI = 0; fillI < fillNum; fillI ++) { + rsgBindFont(fonts[fillI]); + xOffset = textOffsets[fillI * 2]; + yOffset = textOffsets[fillI * 2 + 1]; + float *colPtr = textColors + fillI * 4; + rsgFontColor(colPtr[0], colPtr[1], colPtr[2], colPtr[3]); + for (int h = 0; h < 4; h ++) { + yPos = top + yOffset; + for (int v = 0; v < numVerticalLines; v ++) { + rsgDrawText(sampleText, xOffset + textWidth * h, yPos); + yPos += textHeight; + } + } + } +} + +void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { + TestData *testData = (TestData*)usrData; + gRenderSurfaceW = testData->renderSurfaceW; + gRenderSurfaceH = testData->renderSurfaceH; + displayFontSamples(testData->user); +} diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs new file mode 100644 index 000000000000..26d56807f6f2 --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs @@ -0,0 +1,283 @@ +// 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. + +#pragma version(1) + +#pragma rs java_package_name(com.android.perftest) + +#include "rs_graphics.rsh" +#include "subtest_def.rsh" +#include "shader_def.rsh" + +rs_program_vertex gProgVertex; +rs_program_fragment gProgFragmentColor; +rs_program_fragment gProgFragmentTexture; + +rs_program_store gProgStoreBlendNoneDepth; +rs_mesh gTorusMesh; + +rs_program_raster gCullBack; +rs_program_raster gCullFront; + +// Custom vertex shader compunents +VertexShaderConstants *gVSConstants; +FragentShaderConstants *gFSConstants; +VertexShaderConstants3 *gVSConstPixel; +FragentShaderConstants3 *gFSConstPixel; + +// Custom shaders we use for lighting +rs_program_vertex gProgVertexCustom; +rs_program_fragment gProgFragmentCustom; + +rs_sampler gLinearClamp; +rs_allocation gTexTorus; + +rs_program_vertex gProgVertexPixelLight; +rs_program_vertex gProgVertexPixelLightMove; +rs_program_fragment gProgFragmentPixelLight; + +static float gDt = 0.0f; + +static int gRenderSurfaceW; +static int gRenderSurfaceH; + + +static float gTorusRotation = 0; +static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) { + if (buffer == 0) { + rsgProgramVertexLoadModelMatrix(matrix); + } else { + rsgAllocationSyncAll(rsGetAllocation(buffer)); + } +} + +static void drawToruses(int numMeshes, rs_matrix4x4 *matrix, void *buffer) { + + if (numMeshes == 1) { + rsMatrixLoadTranslate(matrix, 0.0f, 0.0f, -7.5f); + rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); + updateModelMatrix(matrix, buffer); + rsgDrawMesh(gTorusMesh); + return; + } + + if (numMeshes == 2) { + rsMatrixLoadTranslate(matrix, -1.6f, 0.0f, -7.5f); + rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); + updateModelMatrix(matrix, buffer); + rsgDrawMesh(gTorusMesh); + + rsMatrixLoadTranslate(matrix, 1.6f, 0.0f, -7.5f); + rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); + updateModelMatrix(matrix, buffer); + rsgDrawMesh(gTorusMesh); + return; + } + + float startX = -5.0f; + float startY = -1.5f; + float startZ = -15.0f; + float dist = 3.2f; + + for (int h = 0; h < 4; h ++) { + for (int v = 0; v < 2; v ++) { + // Position our model on the screen + rsMatrixLoadTranslate(matrix, startX + dist * h, startY + dist * v, startZ); + rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); + updateModelMatrix(matrix, buffer); + rsgDrawMesh(gTorusMesh); + } + } +} + + +// Quick hack to get some geometry numbers +static void displaySimpleGeoSamples(bool useTexture, int numMeshes) { + rsgBindProgramVertex(gProgVertex); + rsgBindProgramRaster(gCullBack); + // Setup the projection matrix with 30 degree field of view + rs_matrix4x4 proj; + float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH; + rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f); + rsgProgramVertexLoadProjectionMatrix(&proj); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNoneDepth); + if (useTexture) { + rsgBindProgramFragment(gProgFragmentTexture); + } else { + rsgBindProgramFragment(gProgFragmentColor); + rsgProgramFragmentConstantColor(gProgFragmentColor, 0.1, 0.7, 0.1, 1); + } + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + rsgBindTexture(gProgFragmentTexture, 0, gTexTorus); + + // Apply a rotation to our mesh + gTorusRotation += 50.0f * gDt; + if (gTorusRotation > 360.0f) { + gTorusRotation -= 360.0f; + } + + rs_matrix4x4 matrix; + drawToruses(numMeshes, &matrix, 0); +} + +float gLight0Rotation = 0; +float gLight1Rotation = 0; + +static void setupCustomShaderLights() { + float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f}; + float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f}; + float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f}; + float4 light0SpecCol = {0.9f, 0.6f, 0.6f, 1.0f}; + float4 light1DiffCol = {0.5f, 0.5f, 0.9f, 1.0f}; + float4 light1SpecCol = {0.5f, 0.5f, 0.9f, 1.0f}; + + gLight0Rotation += 50.0f * gDt; + if (gLight0Rotation > 360.0f) { + gLight0Rotation -= 360.0f; + } + gLight1Rotation -= 50.0f * gDt; + if (gLight1Rotation > 360.0f) { + gLight1Rotation -= 360.0f; + } + + rs_matrix4x4 l0Mat; + rsMatrixLoadRotate(&l0Mat, gLight0Rotation, 1.0f, 0.0f, 0.0f); + light0Pos = rsMatrixMultiply(&l0Mat, light0Pos); + rs_matrix4x4 l1Mat; + rsMatrixLoadRotate(&l1Mat, gLight1Rotation, 0.0f, 0.0f, 1.0f); + light1Pos = rsMatrixMultiply(&l1Mat, light1Pos); + + // Set light 0 properties + gVSConstants->light0_Posision = light0Pos; + gVSConstants->light0_Diffuse = 1.0f; + gVSConstants->light0_Specular = 0.5f; + gVSConstants->light0_CosinePower = 10.0f; + // Set light 1 properties + gVSConstants->light1_Posision = light1Pos; + gVSConstants->light1_Diffuse = 1.0f; + gVSConstants->light1_Specular = 0.7f; + gVSConstants->light1_CosinePower = 25.0f; + rsgAllocationSyncAll(rsGetAllocation(gVSConstants)); + + // Update fragment shader constants + // Set light 0 colors + gFSConstants->light0_DiffuseColor = light0DiffCol; + gFSConstants->light0_SpecularColor = light0SpecCol; + // Set light 1 colors + gFSConstants->light1_DiffuseColor = light1DiffCol; + gFSConstants->light1_SpecularColor = light1SpecCol; + rsgAllocationSyncAll(rsGetAllocation(gFSConstants)); + + // Set light 0 properties for per pixel lighting + gFSConstPixel->light0_Posision = light0Pos; + gFSConstPixel->light0_Diffuse = 1.0f; + gFSConstPixel->light0_Specular = 0.5f; + gFSConstPixel->light0_CosinePower = 10.0f; + gFSConstPixel->light0_DiffuseColor = light0DiffCol; + gFSConstPixel->light0_SpecularColor = light0SpecCol; + // Set light 1 properties + gFSConstPixel->light1_Posision = light1Pos; + gFSConstPixel->light1_Diffuse = 1.0f; + gFSConstPixel->light1_Specular = 0.7f; + gFSConstPixel->light1_CosinePower = 25.0f; + gFSConstPixel->light1_DiffuseColor = light1DiffCol; + gFSConstPixel->light1_SpecularColor = light1SpecCol; + rsgAllocationSyncAll(rsGetAllocation(gFSConstPixel)); +} + +static void displayCustomShaderSamples(int numMeshes) { + + // Update vertex shader constants + // Load model matrix + // Apply a rotation to our mesh + gTorusRotation += 50.0f * gDt; + if (gTorusRotation > 360.0f) { + gTorusRotation -= 360.0f; + } + + // Setup the projection matrix + float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH; + rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f); + setupCustomShaderLights(); + + rsgBindProgramVertex(gProgVertexCustom); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNoneDepth); + rsgBindProgramFragment(gProgFragmentCustom); + rsgBindSampler(gProgFragmentCustom, 0, gLinearClamp); + rsgBindTexture(gProgFragmentCustom, 0, gTexTorus); + + // Use back face culling + rsgBindProgramRaster(gCullBack); + + drawToruses(numMeshes, &gVSConstants->model, gVSConstants); +} + +static void displayPixelLightSamples(int numMeshes, bool heavyVertex) { + + // Update vertex shader constants + // Load model matrix + // Apply a rotation to our mesh + gTorusRotation += 30.0f * gDt; + if (gTorusRotation > 360.0f) { + gTorusRotation -= 360.0f; + } + + gVSConstPixel->time = rsUptimeMillis()*0.005; + + // Setup the projection matrix + float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH; + rsMatrixLoadPerspective(&gVSConstPixel->proj, 30.0f, aspect, 0.1f, 100.0f); + setupCustomShaderLights(); + + if (heavyVertex) { + rsgBindProgramVertex(gProgVertexPixelLightMove); + } else { + rsgBindProgramVertex(gProgVertexPixelLight); + } + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNoneDepth); + rsgBindProgramFragment(gProgFragmentPixelLight); + rsgBindSampler(gProgFragmentPixelLight, 0, gLinearClamp); + rsgBindTexture(gProgFragmentPixelLight, 0, gTexTorus); + + // Use back face culling + rsgBindProgramRaster(gCullBack); + + drawToruses(numMeshes, &gVSConstPixel->model, gVSConstPixel); +} + + +void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { + TestData *testData = (TestData*)usrData; + gRenderSurfaceW = testData->renderSurfaceW; + gRenderSurfaceH = testData->renderSurfaceH; + gDt = testData->dt; + + switch(testData->user) { + case 0: + displaySimpleGeoSamples(testData->user1 == 1 ? true : false, testData->user2); + break; + case 1: + displayCustomShaderSamples(testData->user1); + break; + case 2: + displayPixelLightSamples(testData->user1, testData->user2 == 1 ? true : false); + break; + } +} diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java index 3b0f54606233..47863bd46dfe 100644 --- a/voip/java/com/android/server/sip/SipService.java +++ b/voip/java/com/android/server/sip/SipService.java @@ -756,21 +756,20 @@ public final class SipService extends ISipService.Stub { } } - private class IntervalMeasurementProcess implements + private class IntervalMeasurementProcess implements Runnable, SipSessionGroup.KeepAliveProcessCallback { private static final String TAG = "SipKeepAliveInterval"; private static final int MAX_INTERVAL = 120; // in seconds - private static final int MIN_INTERVAL = 10; // in seconds + private static final int MIN_INTERVAL = 5; // in seconds private static final int PASS_THRESHOLD = 10; private static final int MAX_RETRY_COUNT = 5; + private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds private SipSessionGroupExt mGroup; private SipSessionGroup.SipSessionImpl mSession; - private boolean mRunning; - private int mMinInterval = 10; // in seconds + private int mMinInterval = DEFAULT_KEEPALIVE_INTERVAL; // in seconds private int mMaxInterval; private int mInterval; private int mPassCount = 0; - private int mErrorCount = 0; public IntervalMeasurementProcess(SipProfile localProfile, int maxInterval) { mMaxInterval = (maxInterval < 0) ? MAX_INTERVAL : maxInterval; @@ -788,8 +787,6 @@ public final class SipService extends ISipService.Stub { // TODO: remove this line once SipWakeupTimer can better handle // variety of timeout values mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor)); - mSession = (SipSessionGroup.SipSessionImpl) - mGroup.createSession(null); } catch (Exception e) { Log.w(TAG, "start interval measurement error: " + e); } @@ -797,6 +794,11 @@ public final class SipService extends ISipService.Stub { public void start() { synchronized (SipService.this) { + Log.d(TAG, "start measurement w interval=" + mInterval); + if (mSession == null) { + mSession = (SipSessionGroup.SipSessionImpl) + mGroup.createSession(null); + } try { mSession.startKeepAliveProcess(mInterval, this); } catch (SipException e) { @@ -807,14 +809,23 @@ public final class SipService extends ISipService.Stub { public void stop() { synchronized (SipService.this) { - mSession.stopKeepAliveProcess(); + if (mSession != null) { + mSession.stopKeepAliveProcess(); + mSession = null; + } + mTimer.cancel(this); } } private void restart() { synchronized (SipService.this) { + // Return immediately if the measurement process is stopped + if (mSession == null) return; + + Log.d(TAG, "restart measurement w interval=" + mInterval); try { mSession.stopKeepAliveProcess(); + mPassCount = 0; mSession.startKeepAliveProcess(mInterval, this); } catch (SipException e) { Log.e(TAG, "restart()", e); @@ -826,8 +837,6 @@ public final class SipService extends ISipService.Stub { @Override public void onResponse(boolean portChanged) { synchronized (SipService.this) { - mErrorCount = 0; - if (!portChanged) { if (++mPassCount != PASS_THRESHOLD) return; // update the interval, since the current interval is good to @@ -853,7 +862,6 @@ public final class SipService extends ISipService.Stub { } else { // calculate the new interval and continue. mInterval = (mMaxInterval + mMinInterval) / 2; - mPassCount = 0; if (DEBUG) { Log.d(TAG, "current interval: " + mKeepAliveInterval + ", test new interval: " + mInterval); @@ -866,22 +874,32 @@ public final class SipService extends ISipService.Stub { // SipSessionGroup.KeepAliveProcessCallback @Override public void onError(int errorCode, String description) { + Log.w(TAG, "interval measurement error: " + description); + restartLater(); + } + + // timeout handler + @Override + public void run() { + mTimer.cancel(this); + restart(); + } + + private void restartLater() { synchronized (SipService.this) { - Log.w(TAG, "interval measurement error: " + description); - if (++mErrorCount < MAX_RETRY_COUNT) { - Log.w(TAG, " retry count = " + mErrorCount); - mPassCount = 0; - restart(); - } else { - Log.w(TAG, " max retry count reached; measurement aborted"); - } + int interval = NAT_MEASUREMENT_RETRY_INTERVAL; + Log.d(TAG, "Retry measurement " + interval + "s later."); + mTimer.cancel(this); + mTimer.set(interval * 1000, this); } } } private class AutoRegistrationProcess extends SipSessionAdapter implements Runnable, SipSessionGroup.KeepAliveProcessCallback { + private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10; private String TAG = "SipAudoReg"; + private SipSessionGroup.SipSessionImpl mSession; private SipSessionGroup.SipSessionImpl mKeepAliveSession; private SipSessionListenerProxy mProxy = new SipSessionListenerProxy(); @@ -892,6 +910,8 @@ public final class SipService extends ISipService.Stub { private String mErrorMessage; private boolean mRunning = false; + private int mKeepAliveSuccessCount = 0; + private String getAction() { return toString(); } @@ -915,18 +935,56 @@ public final class SipService extends ISipService.Stub { } } + private void startKeepAliveProcess(int interval) { + Log.d(TAG, "start keepalive w interval=" + interval); + if (mKeepAliveSession == null) { + mKeepAliveSession = mSession.duplicate(); + } else { + mKeepAliveSession.stopKeepAliveProcess(); + } + try { + mKeepAliveSession.startKeepAliveProcess(interval, this); + } catch (SipException e) { + Log.e(TAG, "failed to start keepalive w interval=" + interval, + e); + } + } + + private void stopKeepAliveProcess() { + if (mKeepAliveSession != null) { + mKeepAliveSession.stopKeepAliveProcess(); + mKeepAliveSession = null; + } + mKeepAliveSuccessCount = 0; + } + // SipSessionGroup.KeepAliveProcessCallback @Override public void onResponse(boolean portChanged) { synchronized (SipService.this) { if (portChanged) { - restartPortMappingLifetimeMeasurement( - mSession.getLocalProfile(), getKeepAliveInterval()); + int interval = getKeepAliveInterval(); + if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) { + Log.i(TAG, "keepalive doesn't work with interval " + + interval + ", past success count=" + + mKeepAliveSuccessCount); + if (interval > DEFAULT_KEEPALIVE_INTERVAL) { + restartPortMappingLifetimeMeasurement( + mSession.getLocalProfile(), interval); + mKeepAliveSuccessCount = 0; + } + } else { + Log.i(TAG, "keep keepalive going with interval " + + interval + ", past success count=" + + mKeepAliveSuccessCount); + mKeepAliveSuccessCount /= 2; + } } else { // Start keep-alive interval measurement on the first // successfully kept-alive SipSessionGroup startPortMappingLifetimeMeasurement( mSession.getLocalProfile()); + mKeepAliveSuccessCount++; } if (!mRunning || !portChanged) return; @@ -960,10 +1018,7 @@ public final class SipService extends ISipService.Stub { } mTimer.cancel(this); - if (mKeepAliveSession != null) { - mKeepAliveSession.stopKeepAliveProcess(); - mKeepAliveSession = null; - } + stopKeepAliveProcess(); mRegistered = false; setListener(mProxy.getListener()); @@ -975,12 +1030,8 @@ public final class SipService extends ISipService.Stub { if (DEBUGV) { Log.v(TAG, "restart keepalive w interval=" + newInterval); } - mKeepAliveSession.stopKeepAliveProcess(); - try { - mKeepAliveSession.startKeepAliveProcess(newInterval, this); - } catch (SipException e) { - Log.e(TAG, "onKeepAliveIntervalChanged()", e); - } + mKeepAliveSuccessCount = 0; + startKeepAliveProcess(newInterval); } } @@ -1105,14 +1156,7 @@ public final class SipService extends ISipService.Stub { SipProfile localProfile = mSession.getLocalProfile(); if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp) || localProfile.getSendKeepAlive())) { - mKeepAliveSession = mSession.duplicate(); - Log.d(TAG, "start keepalive"); - try { - mKeepAliveSession.startKeepAliveProcess( - getKeepAliveInterval(), this); - } catch (SipException e) { - Log.e(TAG, "AutoRegistrationProcess", e); - } + startKeepAliveProcess(getKeepAliveInterval()); } } mMyWakeLock.release(session); diff --git a/voip/java/com/android/server/sip/SipWakeupTimer.java b/voip/java/com/android/server/sip/SipWakeupTimer.java index 76780c02b3df..00d47ac63ce6 100644 --- a/voip/java/com/android/server/sip/SipWakeupTimer.java +++ b/voip/java/com/android/server/sip/SipWakeupTimer.java @@ -83,7 +83,7 @@ class SipWakeupTimer extends BroadcastReceiver { mEventQueue = null; } - private synchronized boolean stopped() { + private boolean stopped() { if (mEventQueue == null) { Log.w(TAG, "Timer stopped"); return true; @@ -233,7 +233,7 @@ class SipWakeupTimer extends BroadcastReceiver { } @Override - public void onReceive(Context context, Intent intent) { + public synchronized void onReceive(Context context, Intent intent) { // This callback is already protected by AlarmManager's wake lock. String action = intent.getAction(); if (getAction().equals(action) @@ -261,7 +261,7 @@ class SipWakeupTimer extends BroadcastReceiver { } } - private synchronized void execute(long triggerTime) { + private void execute(long triggerTime) { if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = " + showTime(triggerTime) + ": " + mEventQueue.size()); if (stopped() || mEventQueue.isEmpty()) return; |