Merge "CallManager: do not deliver ring event if fg call is live." into gingerbread
diff --git a/Android.mk b/Android.mk
index b1f43f8..9f92637 100644
--- a/Android.mk
+++ b/Android.mk
@@ -375,6 +375,8 @@
# (see development/build/sdk.atree)
web_docs_sample_code_flags := \
-hdf android.hasSamples 1 \
+ -samplecode $(sample_dir)/AccessibilityService \
+ resources/samples/AccessibilityService "Accessibility Service" \
-samplecode $(sample_dir)/ApiDemos \
resources/samples/ApiDemos "API Demos" \
-samplecode $(sample_dir)/BackupRestore \
diff --git a/api/current.xml b/api/current.xml
index 392ea95..f494775 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -96284,6 +96284,17 @@
visibility="public"
>
</field>
+<field name="ERROR_CANNOT_RESUME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1008"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ERROR_DEVICE_NOT_FOUND"
type="int"
transient="false"
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index e69c324..447e642 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -185,6 +185,12 @@
public final static int ERROR_DEVICE_NOT_FOUND = 1007;
/**
+ * Value of {@link #COLUMN_ERROR_CODE} when some possibly transient error occurred but we can't
+ * resume the download.
+ */
+ public final static int ERROR_CANNOT_RESUME = 1008;
+
+ /**
* Broadcast intent action sent by the download manager when a download completes.
*/
public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
@@ -715,7 +721,8 @@
if (translateStatus(status) != STATUS_FAILED) {
return 0; // arbitrary value when status is not an error
}
- if ((400 <= status && status < 490) || (500 <= status && status < 600)) {
+ if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
+ || (500 <= status && status < 600)) {
// HTTP status code
return status;
}
@@ -740,6 +747,9 @@
case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
return ERROR_DEVICE_NOT_FOUND;
+ case Downloads.Impl.STATUS_CANNOT_RESUME:
+ return ERROR_CANNOT_RESUME;
+
default:
return ERROR_UNKNOWN;
}
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java
index 1da6d7a..d634672 100644
--- a/core/java/android/pim/vcard/VCardBuilder.java
+++ b/core/java/android/pim/vcard/VCardBuilder.java
@@ -1463,6 +1463,9 @@
parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
} else if (VCardUtils.isMobilePhoneLabel(label)) {
parameterList.add(VCardConstants.PARAM_TYPE_CELL);
+ } else if (mIsV30) {
+ // This label is appropriately encoded in appendTypeParameters.
+ parameterList.add(label);
} else {
final String upperLabel = label.toUpperCase();
if (VCardUtils.isValidInV21ButUnknownToContactsPhoteType(upperLabel)) {
@@ -1741,21 +1744,30 @@
// which would be recommended way in vcard 3.0 though not valid in vCard 2.1.
boolean first = true;
for (final String typeValue : types) {
- // Note: vCard 3.0 specifies the different type of acceptable type Strings, but
- // we don't emit that kind of vCard 3.0 specific type since there should be
- // high probabilyty in which external importers cannot understand them.
- //
- // e.g. TYPE="\u578B\u306B\u3087" (vCard 3.0 allows non-Ascii characters if they
- // are quoted.)
- if (!VCardUtils.isV21Word(typeValue)) {
- continue;
+ if (VCardConfig.isV30(mVCardType)) {
+ // Note: vCard 3.0 specifies the different type of acceptable type Strings, but
+ // we don't emit that kind of vCard 3.0 specific type since there should be
+ // high probabilyty in which external importers cannot understand them.
+ //
+ // e.g. TYPE="\u578B\u306B\u3087" (vCard 3.0 allows non-Ascii characters if they
+ // are quoted.)
+ if (first) {
+ first = false;
+ } else {
+ mBuilder.append(VCARD_PARAM_SEPARATOR);
+ }
+ appendTypeParameter(VCardUtils.toStringAvailableAsV30ParameValue(typeValue));
+ } else { // vCard 2.1
+ if (!VCardUtils.isV21Word(typeValue)) {
+ continue;
+ }
+ if (first) {
+ first = false;
+ } else {
+ mBuilder.append(VCARD_PARAM_SEPARATOR);
+ }
+ appendTypeParameter(typeValue);
}
- if (first) {
- first = false;
- } else {
- mBuilder.append(VCARD_PARAM_SEPARATOR);
- }
- appendTypeParameter(typeValue);
}
}
diff --git a/core/java/android/pim/vcard/VCardEntryConstructor.java b/core/java/android/pim/vcard/VCardEntryConstructor.java
index 290ca2b..ae4ec29 100644
--- a/core/java/android/pim/vcard/VCardEntryConstructor.java
+++ b/core/java/android/pim/vcard/VCardEntryConstructor.java
@@ -157,11 +157,15 @@
mParamType = type;
}
+ @Override
public void propertyParamValue(String value) {
if (mParamType == null) {
// From vCard 2.1 specification. vCard 3.0 formally does not allow this case.
mParamType = "TYPE";
}
+ if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
+ value = encodeString(value, mCharsetForDecodedBytes);
+ }
mCurrentProperty.addParameter(mParamType, value);
mParamType = null;
}
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
index 11b112b..f972799 100644
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -16,10 +16,10 @@
package android.pim.vcard;
import android.content.ContentProviderOperation;
-import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.Data;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
@@ -477,6 +477,43 @@
return true;
}
+ /**
+ * <P>
+ * Returns String available as parameter value in vCard 3.0.
+ * </P>
+ * <P>
+ * RFC 2426 requires vCard composer to quote parameter values when it contains
+ * semi-colon, for example (See RFC 2426 for more information).
+ * This method checks whether the given String can be used without quotes.
+ * </P>
+ * <P>
+ * Note: We remove DQUOTE silently for now.
+ * </P>
+ */
+ public static String toStringAvailableAsV30ParameValue(String value) {
+ if (TextUtils.isEmpty(value)) {
+ value = "";
+ }
+ final int asciiFirst = 0x20;
+ final int asciiLast = 0x7E; // included
+ final StringBuilder builder = new StringBuilder();
+ final int length = value.length();
+ boolean needQuote = false;
+ for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
+ final int codePoint = value.codePointAt(i);
+ if (codePoint < asciiFirst || codePoint == '"') {
+ // CTL characters and DQUOTE are never accepted. Remove them.
+ continue;
+ }
+ builder.appendCodePoint(codePoint);
+ if (codePoint == ':' || codePoint == ',' || codePoint == ' ') {
+ needQuote = true;
+ }
+ }
+ final String result = builder.toString();
+ return ((needQuote || result.isEmpty()) ? ('"' + result + '"') : result);
+ }
+
public static String toHalfWidthString(final String orgString) {
if (TextUtils.isEmpty(orgString)) {
return null;
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index c9b5512..6bf0d5b 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -1060,6 +1060,16 @@
public static final int STATUS_PRECONDITION_FAILED = 412;
/**
+ * The lowest-valued error status that is not an actual HTTP status code.
+ */
+ public static final int MIN_ARTIFICIAL_ERROR_STATUS = 489;
+
+ /**
+ * Some possibly transient error occurred, but we can't resume the download.
+ */
+ public static final int STATUS_CANNOT_RESUME = 489;
+
+ /**
* This download was canceled
*/
public static final int STATUS_CANCELED = 490;
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index efdc399..42135c5 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -78,6 +78,7 @@
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
+ android/graphics/AutoDecodeCancel.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \
android/graphics/Camera.cpp \
@@ -101,6 +102,7 @@
android_graphics_PixelFormat.cpp \
android/graphics/Picture.cpp \
android/graphics/PorterDuff.cpp \
+ android/graphics/LargeBitmap.cpp \
android/graphics/Rasterizer.cpp \
android/graphics/Region.cpp \
android/graphics/Shader.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 62ca2ef..407d2e7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -53,6 +53,7 @@
extern int register_android_os_Process(JNIEnv* env);
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
+extern int register_android_graphics_LargeBitmap(JNIEnv*);
extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_Interpolator(JNIEnv* env);
@@ -1264,6 +1265,7 @@
REG_JNI(register_android_graphics_Bitmap),
REG_JNI(register_android_graphics_BitmapFactory),
+ REG_JNI(register_android_graphics_LargeBitmap),
REG_JNI(register_android_graphics_Camera),
REG_JNI(register_android_graphics_Canvas),
REG_JNI(register_android_graphics_ColorFilter),
diff --git a/core/jni/android/graphics/AutoDecodeCancel.cpp b/core/jni/android/graphics/AutoDecodeCancel.cpp
new file mode 100644
index 0000000..f0739ea
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.cpp
@@ -0,0 +1,100 @@
+#include "AutoDecodeCancel.h"
+
+static SkMutex gAutoDecoderCancelMutex;
+static AutoDecoderCancel* gAutoDecoderCancel;
+#ifdef SK_DEBUG
+static int gAutoDecoderCancelCount;
+#endif
+
+AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
+ SkImageDecoder* decoder) {
+ fJOptions = joptions;
+ fDecoder = decoder;
+
+ if (NULL != joptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ // Add us as the head of the list
+ fPrev = NULL;
+ fNext = gAutoDecoderCancel;
+ if (gAutoDecoderCancel) {
+ gAutoDecoderCancel->fPrev = this;
+ }
+ gAutoDecoderCancel = this;
+
+ SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
+ Validate();
+ }
+}
+
+AutoDecoderCancel::~AutoDecoderCancel() {
+ if (NULL != fJOptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ // take us out of the dllist
+ AutoDecoderCancel* prev = fPrev;
+ AutoDecoderCancel* next = fNext;
+
+ if (prev) {
+ SkASSERT(prev->fNext == this);
+ prev->fNext = next;
+ } else {
+ SkASSERT(gAutoDecoderCancel == this);
+ gAutoDecoderCancel = next;
+ }
+ if (next) {
+ SkASSERT(next->fPrev == this);
+ next->fPrev = prev;
+ }
+
+ SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
+ Validate();
+ }
+}
+
+bool AutoDecoderCancel::RequestCancel(jobject joptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ Validate();
+
+ AutoDecoderCancel* pair = gAutoDecoderCancel;
+ while (pair != NULL) {
+ if (pair->fJOptions == joptions) {
+ pair->fDecoder->cancelDecode();
+ return true;
+ }
+ pair = pair->fNext;
+ }
+ return false;
+}
+
+#ifdef SK_DEBUG
+// can only call this inside a lock on gAutoDecoderCancelMutex
+void AutoDecoderCancel::Validate() {
+ const int gCount = gAutoDecoderCancelCount;
+
+ if (gCount == 0) {
+ SkASSERT(gAutoDecoderCancel == NULL);
+ } else {
+ SkASSERT(gCount > 0);
+
+ AutoDecoderCancel* curr = gAutoDecoderCancel;
+ SkASSERT(curr);
+ SkASSERT(curr->fPrev == NULL);
+
+ int count = 0;
+ while (curr) {
+ count += 1;
+ SkASSERT(count <= gCount);
+ if (curr->fPrev) {
+ SkASSERT(curr->fPrev->fNext == curr);
+ }
+ if (curr->fNext) {
+ SkASSERT(curr->fNext->fPrev == curr);
+ }
+ curr = curr->fNext;
+ }
+ SkASSERT(count == gCount);
+ }
+}
+#endif
diff --git a/core/jni/android/graphics/AutoDecodeCancel.h b/core/jni/android/graphics/AutoDecodeCancel.h
new file mode 100644
index 0000000..37b86f9
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.h
@@ -0,0 +1,27 @@
+#ifndef AutoDecodeCancel_DEFINED
+#define AutoDecodeCancel_DEFINED
+
+#include <jni.h>
+#include "SkImageDecoder.h"
+
+class AutoDecoderCancel {
+public:
+ AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
+ ~AutoDecoderCancel();
+
+ static bool RequestCancel(jobject options);
+
+private:
+ AutoDecoderCancel* fNext;
+ AutoDecoderCancel* fPrev;
+ jobject fJOptions; // java options object
+ SkImageDecoder* fDecoder;
+
+#ifdef SK_DEBUG
+ static void Validate();
+#else
+ static void Validate() {}
+#endif
+};
+
+#endif
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index b41bad0..21b2e3b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -1,14 +1,15 @@
#define LOG_TAG "BitmapFactory"
+#include "BitmapFactory.h"
#include "SkImageDecoder.h"
#include "SkImageRef_ashmem.h"
#include "SkImageRef_GlobalPool.h"
#include "SkPixelRef.h"
#include "SkStream.h"
-#include "GraphicsJNI.h"
#include "SkTemplates.h"
#include "SkUtils.h"
#include "CreateJavaOutputStreamAdaptor.h"
+#include "AutoDecodeCancel.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/Asset.h>
@@ -16,18 +17,18 @@
#include <netinet/in.h>
#include <sys/mman.h>
-static jclass gOptions_class;
-static jfieldID gOptions_justBoundsFieldID;
-static jfieldID gOptions_sampleSizeFieldID;
-static jfieldID gOptions_configFieldID;
-static jfieldID gOptions_ditherFieldID;
-static jfieldID gOptions_purgeableFieldID;
-static jfieldID gOptions_shareableFieldID;
-static jfieldID gOptions_nativeAllocFieldID;
-static jfieldID gOptions_widthFieldID;
-static jfieldID gOptions_heightFieldID;
-static jfieldID gOptions_mimeFieldID;
-static jfieldID gOptions_mCancelID;
+jclass gOptions_class;
+jfieldID gOptions_justBoundsFieldID;
+jfieldID gOptions_sampleSizeFieldID;
+jfieldID gOptions_configFieldID;
+jfieldID gOptions_ditherFieldID;
+jfieldID gOptions_purgeableFieldID;
+jfieldID gOptions_shareableFieldID;
+jfieldID gOptions_nativeAllocFieldID;
+jfieldID gOptions_widthFieldID;
+jfieldID gOptions_heightFieldID;
+jfieldID gOptions_mimeFieldID;
+jfieldID gOptions_mCancelID;
static jclass gFileDescriptor_class;
static jfieldID gFileDescriptor_descriptor;
@@ -38,129 +39,6 @@
#define TRACE_BITMAP(code)
#endif
-///////////////////////////////////////////////////////////////////////////////
-
-class AutoDecoderCancel {
-public:
- AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
- ~AutoDecoderCancel();
-
- static bool RequestCancel(jobject options);
-
-private:
- AutoDecoderCancel* fNext;
- AutoDecoderCancel* fPrev;
- jobject fJOptions; // java options object
- SkImageDecoder* fDecoder;
-
-#ifdef SK_DEBUG
- static void Validate();
-#else
- static void Validate() {}
-#endif
-};
-
-static SkMutex gAutoDecoderCancelMutex;
-static AutoDecoderCancel* gAutoDecoderCancel;
-#ifdef SK_DEBUG
- static int gAutoDecoderCancelCount;
-#endif
-
-AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
- SkImageDecoder* decoder) {
- fJOptions = joptions;
- fDecoder = decoder;
-
- if (NULL != joptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- // Add us as the head of the list
- fPrev = NULL;
- fNext = gAutoDecoderCancel;
- if (gAutoDecoderCancel) {
- gAutoDecoderCancel->fPrev = this;
- }
- gAutoDecoderCancel = this;
-
- SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
- Validate();
- }
-}
-
-AutoDecoderCancel::~AutoDecoderCancel() {
- if (NULL != fJOptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- // take us out of the dllist
- AutoDecoderCancel* prev = fPrev;
- AutoDecoderCancel* next = fNext;
-
- if (prev) {
- SkASSERT(prev->fNext == this);
- prev->fNext = next;
- } else {
- SkASSERT(gAutoDecoderCancel == this);
- gAutoDecoderCancel = next;
- }
- if (next) {
- SkASSERT(next->fPrev == this);
- next->fPrev = prev;
- }
-
- SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
- Validate();
- }
-}
-
-bool AutoDecoderCancel::RequestCancel(jobject joptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- Validate();
-
- AutoDecoderCancel* pair = gAutoDecoderCancel;
- while (pair != NULL) {
- if (pair->fJOptions == joptions) {
- pair->fDecoder->cancelDecode();
- return true;
- }
- pair = pair->fNext;
- }
- return false;
-}
-
-#ifdef SK_DEBUG
-// can only call this inside a lock on gAutoDecoderCancelMutex
-void AutoDecoderCancel::Validate() {
- const int gCount = gAutoDecoderCancelCount;
-
- if (gCount == 0) {
- SkASSERT(gAutoDecoderCancel == NULL);
- } else {
- SkASSERT(gCount > 0);
-
- AutoDecoderCancel* curr = gAutoDecoderCancel;
- SkASSERT(curr);
- SkASSERT(curr->fPrev == NULL);
-
- int count = 0;
- while (curr) {
- count += 1;
- SkASSERT(count <= gCount);
- if (curr->fPrev) {
- SkASSERT(curr->fPrev->fNext == curr);
- }
- if (curr->fNext) {
- SkASSERT(curr->fNext->fPrev == curr);
- }
- curr = curr->fNext;
- }
- SkASSERT(count == gCount);
- }
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
using namespace android;
class NinePatchPeeker : public SkImageDecoder::Peeker {
@@ -279,7 +157,7 @@
return ((int32_t)isValid - 1) | value;
}
-static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
static const struct {
SkImageDecoder::Format fFormat;
const char* fMimeType;
@@ -477,7 +355,7 @@
jobject padding,
jobject options) { // BitmapFactory$Options
jobject bitmap = NULL;
- SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
+ SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
if (stream) {
// for now we don't allow purgeable with java inputstreams
@@ -682,6 +560,107 @@
}
}
+static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
+ SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+ int width, height;
+ if (NULL == decoder) {
+ doThrowIOE(env, "Image format not supported");
+ return nullObjectReturn("SkImageDecoder::Factory returned null");
+ }
+
+ JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
+ decoder->setAllocator(javaAllocator);
+ JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
+ decoder->setReporter(javaMemoryReporter);
+ javaAllocator->unref();
+ javaMemoryReporter->unref();
+
+ if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
+ char msg[1024];
+ snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
+ doThrowIOE(env, msg);
+ return nullObjectReturn("decoder->buildTileIndex returned false");
+ }
+
+ SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
+
+ return GraphicsJNI::createLargeBitmap(env, bm);
+}
+
+static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+ int offset, int length, jboolean isShareable) {
+ AutoJavaByteArray ar(env, byteArray);
+ SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
+ SkAutoUnref aur(stream);
+ if (isShareable) {
+ aur.detach();
+ }
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
+ jobject fileDescriptor, jboolean isShareable) {
+ NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+ jint descriptor = env->GetIntField(fileDescriptor,
+ gFileDescriptor_descriptor);
+ bool weOwnTheFD = false;
+
+ if (isShareable) {
+ int newFD = ::dup(descriptor);
+ if (-1 != newFD) {
+ weOwnTheFD = true;
+ descriptor = newFD;
+ }
+ }
+
+ SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
+ SkAutoUnref aur(stream);
+ if (!stream->isValid()) {
+ return NULL;
+ }
+
+ if (isShareable) {
+ aur.detach();
+ }
+
+ /* Restore our offset when we leave, so we can be called more than once
+ with the same descriptor. This is only required if we didn't dup the
+ file descriptor, but it is OK to do it all the time.
+ */
+ AutoFDSeek as(descriptor);
+
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
+ jobject is, // InputStream
+ jbyteArray storage, // byte[]
+ jboolean isShareable) {
+ jobject largeBitmap = NULL;
+ SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
+
+ if (stream) {
+ // for now we don't allow shareable with java inputstreams
+ largeBitmap = doBuildTileIndex(env, stream, false);
+ stream->unref();
+ }
+ return largeBitmap;
+}
+
+static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
+ jint native_asset, // Asset
+ jboolean isShareable) {
+ SkStream* stream;
+ Asset* asset = reinterpret_cast<Asset*>(native_asset);
+ stream = new AssetStreamAdaptor(asset);
+ SkAutoUnref aur(stream);
+ if (isShareable) {
+ aur.detach();
+ }
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
///////////////////////////////////////////////////////////////////////////////
static JNINativeMethod gMethods[] = {
@@ -711,6 +690,26 @@
},
{ "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
+
+ { "nativeCreateLargeBitmap",
+ "([BIIZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromByteArray
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromStream
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromFileDescriptor
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(IZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromAsset
+ },
};
static JNINativeMethod gOptionsMethods[] = {
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
new file mode 100644
index 0000000..f868434
--- /dev/null
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -0,0 +1,21 @@
+#ifndef BitmapFactory_DEFINE
+#define BitmapFactory_DEFINE
+
+#include "GraphicsJNI.h"
+
+extern jclass gOptions_class;
+extern jfieldID gOptions_justBoundsFieldID;
+extern jfieldID gOptions_sampleSizeFieldID;
+extern jfieldID gOptions_configFieldID;
+extern jfieldID gOptions_ditherFieldID;
+extern jfieldID gOptions_purgeableFieldID;
+extern jfieldID gOptions_shareableFieldID;
+extern jfieldID gOptions_nativeAllocFieldID;
+extern jfieldID gOptions_widthFieldID;
+extern jfieldID gOptions_heightFieldID;
+extern jfieldID gOptions_mimeFieldID;
+extern jfieldID gOptions_mCancelID;
+
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format);
+
+#endif
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 007757f..137acc6 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -5,6 +5,7 @@
static jclass gInputStream_Clazz;
static jmethodID gInputStream_resetMethodID;
+static jmethodID gInputStream_markMethodID;
static jmethodID gInputStream_availableMethodID;
static jmethodID gInputStream_readMethodID;
static jmethodID gInputStream_skipMethodID;
@@ -143,7 +144,7 @@
};
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
- jbyteArray storage) {
+ jbyteArray storage, int markSize) {
static bool gInited;
if (!gInited) {
@@ -153,6 +154,8 @@
gInputStream_resetMethodID = env->GetMethodID(gInputStream_Clazz,
"reset", "()V");
+ gInputStream_markMethodID = env->GetMethodID(gInputStream_Clazz,
+ "mark", "(I)V");
gInputStream_availableMethodID = env->GetMethodID(gInputStream_Clazz,
"available", "()I");
gInputStream_readMethodID = env->GetMethodID(gInputStream_Clazz,
@@ -161,6 +164,7 @@
"skip", "(J)J");
RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
+ RETURN_NULL_IF_NULL(gInputStream_markMethodID);
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
RETURN_NULL_IF_NULL(gInputStream_skipMethodID);
@@ -168,6 +172,10 @@
gInited = true;
}
+ if (markSize) {
+ env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);
+ }
+
return new JavaInputStreamAdaptor(env, stream, storage);
}
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
index cf21dde..c34c96a 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
@@ -6,7 +6,7 @@
#include "SkStream.h"
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
- jbyteArray storage);
+ jbyteArray storage, int markSize = 0);
SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
jbyteArray storage);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5659ba2..204bb74 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -46,6 +46,10 @@
doThrow(env, "java/lang/OutOfMemoryError", msg);
}
+void doThrowIOE(JNIEnv* env, const char* msg) {
+ doThrow(env, "java/lang/IOException", msg);
+}
+
bool GraphicsJNI::hasException(JNIEnv *env) {
if (env->ExceptionCheck() != 0) {
LOGE("*** Uncaught exception returned from Java call!\n");
@@ -165,6 +169,9 @@
static jclass gBitmapConfig_class;
static jfieldID gBitmapConfig_nativeInstanceID;
+static jclass gLargeBitmap_class;
+static jmethodID gLargeBitmap_constructorMethodID;
+
static jclass gCanvas_class;
static jfieldID gCanvas_nativeInstanceID;
@@ -370,6 +377,23 @@
}
return obj;
}
+jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap)
+{
+ SkASSERT(bitmap != NULL);
+
+ jobject obj = env->AllocObject(gLargeBitmap_class);
+ if (hasException(env)) {
+ obj = NULL;
+ return obj;
+ }
+ if (obj) {
+ env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap);
+ if (hasException(env)) {
+ obj = NULL;
+ }
+ }
+ return obj;
+}
jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
{
@@ -502,6 +526,35 @@
////////////////////////////////////////////////////////////////////////////////
+JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
+ : fEnv(env), fTotalSize(0) {}
+
+JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
+ jlong jtotalSize = fTotalSize;
+ fEnv->CallVoidMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalFreeMethodID,
+ jtotalSize);
+}
+
+bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
+ jlong jsize = memorySize; // the VM wants longs for the size
+ bool r = fEnv->CallBooleanMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalAllocationMethodID,
+ jsize);
+ if (GraphicsJNI::hasException(fEnv)) {
+ return false;
+ }
+ if (!r) {
+ LOGE("VM won't let us allocate %zd bytes\n", memorySize);
+ doThrowOOME(fEnv, "bitmap size exceeds VM budget");
+ return false;
+ }
+ fTotalSize += memorySize;
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
static jclass make_globalref(JNIEnv* env, const char classname[])
{
jclass c = env->FindClass(classname);
@@ -547,6 +600,9 @@
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
"(IZ[BI)V");
+ gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap");
+ gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V");
+
gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
"nativeInt", "I");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index fe24b05..8d6528b 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -4,6 +4,8 @@
#include "SkPoint.h"
#include "SkRect.h"
#include "SkBitmap.h"
+#include "../images/SkLargeBitmap.h"
+#include "../images/SkImageDecoder.h"
#include <jni.h>
class SkCanvas;
@@ -54,6 +56,8 @@
static jobject createRegion(JNIEnv* env, SkRegion* region);
+ static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap);
+
/** Set a pixelref for the bitmap (needs setConfig to already be called)
Returns true on success. If it returns false, then it failed, and the
appropriate exception will have been raised.
@@ -80,6 +84,18 @@
bool fReportSizeToVM;
};
+class JavaMemoryUsageReporter : public SkVMMemoryReporter {
+public:
+ JavaMemoryUsageReporter(JNIEnv* env);
+ virtual ~JavaMemoryUsageReporter();
+ // overrides
+ virtual bool reportMemory(size_t memorySize);
+
+private:
+ JNIEnv* fEnv;
+ size_t fTotalSize;
+};
+
enum JNIAccess {
kRO_JNIAccess,
kRW_JNIAccess
@@ -156,6 +172,7 @@
void doThrowRE(JNIEnv* env, const char* msg = NULL); // Runtime
void doThrowISE(JNIEnv* env, const char* msg = NULL); // Illegal State
void doThrowOOME(JNIEnv* env, const char* msg = NULL); // Out of memory
+void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception
#define NPE_CHECK_RETURN_ZERO(env, object) \
do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0)
diff --git a/core/jni/android/graphics/LargeBitmap.cpp b/core/jni/android/graphics/LargeBitmap.cpp
new file mode 100644
index 0000000..4cf5dfa
--- /dev/null
+++ b/core/jni/android/graphics/LargeBitmap.cpp
@@ -0,0 +1,138 @@
+#define LOG_TAG "LargeBitmap"
+
+#include "SkBitmap.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "GraphicsJNI.h"
+#include "SkDither.h"
+#include "SkUnPreMultiply.h"
+#include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkPixelRef.h"
+#include "BitmapFactory.h"
+#include "AutoDecodeCancel.h"
+#include "SkLargeBitmap.h"
+
+#include <binder/Parcel.h>
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <jni.h>
+
+#if 0
+ #define TRACE_BITMAP(code) code
+#else
+ #define TRACE_BITMAP(code)
+#endif
+
+static jobject nullObjectReturn(const char msg[]) {
+ if (msg) {
+ SkDebugf("--- %s\n", msg);
+ }
+ return NULL;
+}
+
+/*
+ * nine patch not supported
+ *
+ * purgeable not supported
+ * reportSizeToVM not supported
+ */
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm,
+ int start_x, int start_y, int width, int height, jobject options) {
+ SkImageDecoder *decoder = bm->getDecoder();
+ int sampleSize = 1;
+ SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+ bool doDither = true;
+
+ if (NULL != options) {
+ sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+ // initialize these, in case we fail later on
+ env->SetIntField(options, gOptions_widthFieldID, -1);
+ env->SetIntField(options, gOptions_heightFieldID, -1);
+ env->SetObjectField(options, gOptions_mimeFieldID, 0);
+
+ jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+ prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+ doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
+ }
+
+ decoder->setDitherImage(doDither);
+ SkBitmap* bitmap = new SkBitmap;
+ SkAutoTDelete<SkBitmap> adb(bitmap);
+ AutoDecoderCancel adc(options, decoder);
+
+ // To fix the race condition in case "requestCancelDecode"
+ // happens earlier than AutoDecoderCancel object is added
+ // to the gAutoDecoderCancelMutex linked list.
+ if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
+ return nullObjectReturn("gOptions_mCancelID");;
+ }
+
+ SkIRect region;
+ region.fLeft = start_x;
+ region.fTop = start_y;
+ region.fRight = start_x + width;
+ region.fBottom = start_y + height;
+
+ if (!bm->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+ return nullObjectReturn("decoder->decodeRegion returned false");
+ }
+
+ // update options (if any)
+ if (NULL != options) {
+ env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
+ env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
+ // TODO: set the mimeType field with the data from the codec.
+ // but how to reuse a set of strings, rather than allocating new one
+ // each time?
+ env->SetObjectField(options, gOptions_mimeFieldID,
+ getMimeTypeString(env, decoder->getFormat()));
+ }
+
+ // detach bitmap from its autotdeleter, since we want to own it now
+ adb.detach();
+
+ SkPixelRef* pr;
+ pr = bitmap->pixelRef();
+ // promise we will never change our pixels (great for sharing and pictures)
+ pr->setImmutable();
+ // now create the java bitmap
+ return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
+}
+
+static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ return bm->getHeight();
+}
+
+static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ return bm->getWidth();
+}
+
+static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ delete bm;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gLargeBitmapMethods[] = {
+ { "nativeDecodeRegion",
+ "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+ (void*)nativeDecodeRegion},
+ { "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
+ { "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
+ { "nativeClean", "(I)V", (void*)nativeClean},
+};
+
+#define kClassPathName "android/graphics/LargeBitmap"
+
+int register_android_graphics_LargeBitmap(JNIEnv* env);
+int register_android_graphics_LargeBitmap(JNIEnv* env)
+{
+ return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+ gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods));
+}
+
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 7392442..903283e 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -493,6 +493,15 @@
return doBooleanCommand("BLACKLIST clear", "OK");
}
+static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject clazz, jboolean enabled)
+{
+ char cmdstr[25];
+
+ snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETSUSPEND %d", enabled ? 0 : 1);
+ return doBooleanCommand(cmdstr, "OK");
+}
+
+
static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info)
{
jint ipaddr, gateway, mask, dns1, dns2, server, lease;
@@ -571,6 +580,7 @@
{ "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand },
{ "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand },
{ "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand },
+ { "setSuspendOptimizationsCommand", "(Z)Z", (void*) android_net_wifi_setSuspendOptimizationsCommand},
{ "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest },
{ "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError },
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 556d367..42f35d1 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -76,10 +76,14 @@
STATUS_ZOMBIE
};
- Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop);
+ Connection(uint16_t id,
+ const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop);
inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
+ // A unique id for this connection.
+ uint16_t id;
+
Status status;
sp<InputChannel> inputChannel;
@@ -91,29 +95,34 @@
// The sequence number of the current event being dispatched.
// This is used as part of the finished token as a way to determine whether the finished
// token is still valid before sending a finished signal back to the publisher.
- uint32_t messageSeqNum;
+ uint16_t messageSeqNum;
// True if a message has been received from the publisher but not yet finished.
bool messageInProgress;
};
Mutex mLock;
+ uint16_t mNextConnectionId;
KeyedVector<int32_t, sp<Connection> > mConnectionsByReceiveFd;
+ ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
+
static void handleInputChannelDisposed(JNIEnv* env,
jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data);
static bool handleReceiveCallback(int receiveFd, int events, void* data);
- static jlong generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum);
+ static jlong generateFinishedToken(int32_t receiveFd,
+ uint16_t connectionId, uint16_t messageSeqNum);
static void parseFinishedToken(jlong finishedToken,
- int32_t* outReceiveFd, uint32_t* outMessageIndex);
+ int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex);
};
// ----------------------------------------------------------------------------
-NativeInputQueue::NativeInputQueue() {
+NativeInputQueue::NativeInputQueue() :
+ mNextConnectionId(0) {
}
NativeInputQueue::~NativeInputQueue() {
@@ -134,18 +143,17 @@
sp<PollLoop> pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueueObj);
- int receiveFd;
{ // acquire lock
AutoMutex _l(mLock);
- receiveFd = inputChannel->getReceivePipeFd();
- if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 0) {
+ if (getConnectionIndex(inputChannel) >= 0) {
LOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
- sp<Connection> connection = new Connection(inputChannel, pollLoop);
+ uint16_t connectionId = mNextConnectionId++;
+ sp<Connection> connection = new Connection(connectionId, inputChannel, pollLoop);
status_t result = connection->inputConsumer.initialize();
if (result) {
LOGW("Failed to initialize input consumer for input channel '%s', status=%d",
@@ -155,13 +163,14 @@
connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);
+ int32_t receiveFd = inputChannel->getReceivePipeFd();
mConnectionsByReceiveFd.add(receiveFd, connection);
+
+ pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
} // release lock
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, this);
-
- pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
return OK;
}
@@ -177,38 +186,56 @@
LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
#endif
- int32_t receiveFd;
- sp<Connection> connection;
{ // acquire lock
AutoMutex _l(mLock);
- receiveFd = inputChannel->getReceivePipeFd();
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
+ ssize_t connectionIndex = getConnectionIndex(inputChannel);
if (connectionIndex < 0) {
LOGW("Attempted to unregister already unregistered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
- connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
connection->status = Connection::STATUS_ZOMBIE;
+ connection->pollLoop->removeCallback(inputChannel->getReceivePipeFd());
+
env->DeleteGlobalRef(connection->inputHandlerObjGlobal);
connection->inputHandlerObjGlobal = NULL;
+
+ if (connection->messageInProgress) {
+ LOGI("Sending finished signal for input channel '%s' since it is being unregistered "
+ "while an input message is still in progress.",
+ connection->getInputChannelName());
+ connection->messageInProgress = false;
+ connection->inputConsumer.sendFinishedSignal(); // ignoring result
+ }
} // release lock
android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
-
- connection->pollLoop->removeCallback(receiveFd);
return OK;
}
+ssize_t NativeInputQueue::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connection->inputChannel.get() == inputChannel.get()) {
+ return connectionIndex;
+ }
+ }
+
+ return -1;
+}
+
status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish) {
int32_t receiveFd;
- uint32_t messageSeqNum;
- parseFinishedToken(finishedToken, &receiveFd, &messageSeqNum);
+ uint16_t connectionId;
+ uint16_t messageSeqNum;
+ parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum);
{ // acquire lock
AutoMutex _l(mLock);
@@ -216,16 +243,25 @@
ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
if (connectionIndex < 0) {
if (! ignoreSpuriousFinish) {
- LOGW("Attempted to finish input on channel that is no longer registered.");
+ LOGI("Ignoring finish signal on channel that is no longer registered.");
}
return DEAD_OBJECT;
}
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connectionId != connection->id) {
+ if (! ignoreSpuriousFinish) {
+ LOGI("Ignoring finish signal on channel that is no longer registered.");
+ }
+ return DEAD_OBJECT;
+ }
+
if (messageSeqNum != connection->messageSeqNum || ! connection->messageInProgress) {
if (! ignoreSpuriousFinish) {
- LOGW("Attempted to finish input twice on channel '%s'.",
- connection->getInputChannelName());
+ LOGW("Attempted to finish input twice on channel '%s'. "
+ "finished messageSeqNum=%d, current messageSeqNum=%d, messageInProgress=%d",
+ connection->getInputChannelName(),
+ messageSeqNum, connection->messageSeqNum, connection->messageInProgress);
}
return INVALID_OPERATION;
}
@@ -312,7 +348,7 @@
connection->messageInProgress = true;
connection->messageSeqNum += 1;
- finishedToken = generateFinishedToken(receiveFd, connection->messageSeqNum);
+ finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
} // release lock
@@ -384,20 +420,23 @@
return true;
}
-jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum) {
- return (jlong(receiveFd) << 32) | jlong(messageSeqNum);
+jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId,
+ uint16_t messageSeqNum) {
+ return (jlong(receiveFd) << 32) | (jlong(connectionId) << 16) | jlong(messageSeqNum);
}
void NativeInputQueue::parseFinishedToken(jlong finishedToken,
- int32_t* outReceiveFd, uint32_t* outMessageIndex) {
+ int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex) {
*outReceiveFd = int32_t(finishedToken >> 32);
- *outMessageIndex = uint32_t(finishedToken & 0xffffffff);
+ *outConnectionId = uint16_t(finishedToken >> 16);
+ *outMessageIndex = uint16_t(finishedToken);
}
// ----------------------------------------------------------------------------
-NativeInputQueue::Connection::Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) :
- status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel),
+NativeInputQueue::Connection::Connection(uint16_t id,
+ const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) :
+ id(id), status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel),
pollLoop(pollLoop), inputHandlerObjGlobal(NULL),
messageSeqNum(0), messageInProgress(false) {
}
diff --git a/core/tests/coretests/res/raw/v30_multibyte_param.vcf b/core/tests/coretests/res/raw/v30_multibyte_param.vcf
new file mode 100644
index 0000000..cd200e5
--- /dev/null
+++ b/core/tests/coretests/res/raw/v30_multibyte_param.vcf
@@ -0,0 +1,5 @@
+BEGIN:VCARD
+VERSION:3.0
+N:F;G;M;;
+TEL;TYPE="费":1
+END:VCARD
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
index 21f2254..e0e1f87 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
@@ -1008,4 +1008,26 @@
.put(Phone.TYPE, Phone.TYPE_PAGER)
.put(Phone.NUMBER, "6101231234@pagersample.com");
}
+
+ public void testMultiBytePropV30_Parse() {
+ mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNodeWithOrder("VERSION", "3.0")
+ .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
+ .addExpectedNodeWithOrder("TEL", "1", new TypeSet("\u8D39"));
+ }
+
+ public void testMultiBytePropV30() {
+ mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
+ final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "F")
+ .put(StructuredName.MIDDLE_NAME, "M")
+ .put(StructuredName.GIVEN_NAME, "G")
+ .put(StructuredName.DISPLAY_NAME, "G M F");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "\u8D39")
+ .put(Phone.NUMBER, "1");
+ }
}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
index 59299f9..e805bee 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
@@ -82,4 +82,34 @@
assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
}
}
+
+ public void testToStringAvailableAsV30ParamValue() {
+ // Smoke tests.
+ assertEquals("HOME", VCardUtils.toStringAvailableAsV30ParameValue("HOME"));
+ assertEquals("TEL", VCardUtils.toStringAvailableAsV30ParameValue("TEL"));
+ assertEquals("PAGER", VCardUtils.toStringAvailableAsV30ParameValue("PAGER"));
+
+ assertEquals("\"\"", VCardUtils.toStringAvailableAsV30ParameValue(""));
+
+ // non-Ascii must be allowed
+ assertEquals("\u4E8B\u52D9\u6240",
+ VCardUtils.toStringAvailableAsV30ParameValue("\u4E8B\u52D9\u6240"));
+ // Reported as bug report.
+ assertEquals("\u8D39", VCardUtils.toStringAvailableAsV30ParameValue("\u8D39"));
+ assertEquals("\"comma,separated\"",
+ VCardUtils.toStringAvailableAsV30ParameValue("comma,separated"));
+ assertEquals("\"colon:aware\"",
+ VCardUtils.toStringAvailableAsV30ParameValue("colon:aware"));
+ // CTL characters.
+ assertEquals("CTLExample",
+ VCardUtils.toStringAvailableAsV30ParameValue("CTL\u0001Example"));
+ // DQUOTE must be removed.
+ assertEquals("quoted",
+ VCardUtils.toStringAvailableAsV30ParameValue("\"quoted\""));
+ // DQUOTE must be removed basically, but we should detect a space, which
+ // require us to use DQUOTE again.
+ // Right-side has one more illegal dquote to test quote-handle code thoroughly.
+ assertEquals("\"Already quoted\"",
+ VCardUtils.toStringAvailableAsV30ParameValue("\"Already quoted\"\""));
+ }
}
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 2313f4c..8982388 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -27,6 +27,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.FileNotFoundException;
/**
* Creates Bitmap objects from various sources, including files, streams,
@@ -581,6 +582,139 @@
nativeSetDefaultConfig(config.nativeInt);
}
+ /**
+ * Create a LargeBitmap from the specified byte array.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param data byte array of compressed image data.
+ * @param offset offset into data for where the decoder should begin
+ * parsing.
+ * @param length the number of bytes, beginning at offset, to parse
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(byte[] data,
+ int offset, int length, boolean isShareable) throws IOException {
+ if ((offset | length) < 0 || data.length < offset + length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ return nativeCreateLargeBitmap(data, offset, length, isShareable);
+ }
+
+ /**
+ * Create a LargeBitmap from the file descriptor.
+ * The position within the descriptor will not be changed when
+ * this returns, so the descriptor can be used again as is.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param fd The file descriptor containing the data to decode
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(
+ FileDescriptor fd, boolean isShareable) throws IOException {
+ if (MemoryFile.isMemoryFile(fd)) {
+ int mappedlength = MemoryFile.getSize(fd);
+ MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+ InputStream is = file.getInputStream();
+ return createLargeBitmap(is, isShareable);
+ }
+ return nativeCreateLargeBitmap(fd, isShareable);
+ }
+
+ /**
+ * Create a LargeBitmap from an input stream.
+ * The stream's position will be where ever it was after the encoded data
+ * was read.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param is The input stream that holds the raw data to be decoded into a
+ * LargeBitmap.
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(InputStream is,
+ boolean isShareable) throws IOException {
+ // we need mark/reset to work properly in JNI
+
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is, 16 * 1024);
+ }
+
+ if (is instanceof AssetManager.AssetInputStream) {
+ return nativeCreateLargeBitmap(
+ ((AssetManager.AssetInputStream) is).getAssetInt(),
+ isShareable);
+ } else {
+ // pass some temp storage down to the native code. 1024 is made up,
+ // but should be large enough to avoid too many small calls back
+ // into is.read(...).
+ byte [] tempStorage = null;
+ tempStorage = new byte[16 * 1024];
+ return nativeCreateLargeBitmap(is, tempStorage, isShareable);
+ }
+ }
+
+ /**
+ * Create a LargeBitmap from a file path.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param pathName complete path name for the file to be decoded.
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(String pathName,
+ boolean isShareable) throws FileNotFoundException, IOException {
+ LargeBitmap bm = null;
+ InputStream stream = null;
+
+ try {
+ stream = new FileInputStream(pathName);
+ bm = createLargeBitmap(stream, isShareable);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // do nothing here
+ }
+ }
+ }
+ return bm;
+ }
+
private static native void nativeSetDefaultConfig(int nativeConfig);
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
@@ -590,5 +724,14 @@
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
int length, Options opts);
private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
+
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ byte[] data, int offset, int length, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ FileDescriptor fd, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ InputStream is, byte[] storage, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ int asset, boolean isShareable);
}
diff --git a/graphics/java/android/graphics/LargeBitmap.java b/graphics/java/android/graphics/LargeBitmap.java
new file mode 100644
index 0000000..6656b17
--- /dev/null
+++ b/graphics/java/android/graphics/LargeBitmap.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
+
+import java.io.OutputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * LargeBitmap can be used to decode a rectangle region from an image.
+ * LargeBimap is particularly useful when an original image is large and
+ * you only need parts of the image.
+ *
+ * To create a LargeBitmap, call BitmapFactory.createLargeBitmap().
+ * Given a LargeBitmap, users can call decodeRegion() repeatedly
+ * to get a decoded Bitmap of the specified region.
+ * @hide
+ */
+public final class LargeBitmap {
+ private int mNativeLargeBitmap;
+ private boolean mRecycled;
+
+ /* Private constructor that must received an already allocated native
+ large bitmap int (pointer).
+
+ This can be called from JNI code.
+ */
+ private LargeBitmap(int lbm) {
+ mNativeLargeBitmap = lbm;
+ mRecycled = false;
+ }
+
+ /**
+ * Decodes a rectangle region in the image specified by rect.
+ *
+ * @param rect The rectangle that specified the region to be decode.
+ * @param opts null-ok; Options that control downsampling.
+ * inPurgeable is not supported.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded.
+ */
+ public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+ checkRecycled("decodeRegion called on recycled large bitmap");
+ if (rect.left < 0 || rect.top < 0 || rect.right > getWidth() || rect.bottom > getHeight())
+ throw new IllegalArgumentException("rectangle is not inside the image");
+ return nativeDecodeRegion(mNativeLargeBitmap, rect.left, rect.top,
+ rect.right - rect.left, rect.bottom - rect.top, options);
+ }
+
+ /** Returns the original image's width */
+ public int getWidth() {
+ checkRecycled("getWidth called on recycled large bitmap");
+ return nativeGetWidth(mNativeLargeBitmap);
+ }
+
+ /** Returns the original image's height */
+ public int getHeight() {
+ checkRecycled("getHeight called on recycled large bitmap");
+ return nativeGetHeight(mNativeLargeBitmap);
+ }
+
+ /**
+ * Frees up the memory associated with this large bitmap, and mark the
+ * large bitmap as "dead", meaning it will throw an exception if decodeRegion(),
+ * getWidth() or getHeight() is called.
+ * This operation cannot be reversed, so it should only be called if you are
+ * sure there are no further uses for the large bitmap. This is an advanced call,
+ * and normally need not be called, since the normal GC process will free up this
+ * memory when there are no more references to this bitmap.
+ */
+ public void recycle() {
+ if (!mRecycled) {
+ nativeClean(mNativeLargeBitmap);
+ mRecycled = true;
+ }
+ }
+
+ /**
+ * Returns true if this large bitmap has been recycled.
+ * If so, then it is an error to try use its method.
+ *
+ * @return true if the large bitmap has been recycled
+ */
+ public final boolean isRecycled() {
+ return mRecycled;
+ }
+
+ /**
+ * Called by methods that want to throw an exception if the bitmap
+ * has already been recycled.
+ */
+ private void checkRecycled(String errorMessage) {
+ if (mRecycled) {
+ throw new IllegalStateException(errorMessage);
+ }
+ }
+
+ protected void finalize() {
+ recycle();
+ }
+
+ private static native Bitmap nativeDecodeRegion(int lbm,
+ int start_x, int start_y, int width, int height,
+ BitmapFactory.Options options);
+ private static native int nativeGetWidth(int lbm);
+ private static native int nativeGetHeight(int lbm);
+ private static native void nativeClean(int lbm);
+}
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index d3495fe..2505cb0 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -554,6 +554,8 @@
// All registered connections mapped by receive pipe file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
+ ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
+
// Active connections are connections that have a non-empty outbound queue.
// We don't use a ref-counted pointer here because we explicitly abort connections
// during unregistration which causes the connection's outbound queue to be cleared
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index b53f140..13030b5 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -433,8 +433,7 @@
for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(
- inputTarget.inputChannel->getReceivePipeFd());
+ ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
@@ -1367,12 +1366,10 @@
LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
#endif
- int receiveFd;
{ // acquire lock
AutoMutex _l(mLock);
- receiveFd = inputChannel->getReceivePipeFd();
- if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 0) {
+ if (getConnectionIndex(inputChannel) >= 0) {
LOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
@@ -1386,12 +1383,13 @@
return status;
}
+ int32_t receiveFd = inputChannel->getReceivePipeFd();
mConnectionsByReceiveFd.add(receiveFd, connection);
+ mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
+
runCommandsLockedInterruptible();
} // release lock
-
- mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
return OK;
}
@@ -1400,12 +1398,10 @@
LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
#endif
- int32_t receiveFd;
{ // acquire lock
AutoMutex _l(mLock);
- receiveFd = inputChannel->getReceivePipeFd();
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
+ ssize_t connectionIndex = getConnectionIndex(inputChannel);
if (connectionIndex < 0) {
LOGW("Attempted to unregister already unregistered input channel '%s'",
inputChannel->getName().string());
@@ -1417,20 +1413,32 @@
connection->status = Connection::STATUS_ZOMBIE;
+ mPollLoop->removeCallback(inputChannel->getReceivePipeFd());
+
nsecs_t currentTime = now();
abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
runCommandsLockedInterruptible();
} // release lock
- mPollLoop->removeCallback(receiveFd);
-
// Wake the poll loop because removing the connection may have changed the current
// synchronization state.
mPollLoop->wake();
return OK;
}
+ssize_t InputDispatcher::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connection->inputChannel.get() == inputChannel.get()) {
+ return connectionIndex;
+ }
+ }
+
+ return -1;
+}
+
void InputDispatcher::activateConnectionLocked(Connection* connection) {
for (size_t i = 0; i < mActiveConnections.size(); i++) {
if (mActiveConnections.itemAt(i) == connection) {
diff --git a/packages/DefaultContainerService/res/values-cs/strings.xml b/packages/DefaultContainerService/res/values-cs/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-cs/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-da/strings.xml b/packages/DefaultContainerService/res/values-da/strings.xml
new file mode 100644
index 0000000..5243028
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-da/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Hjælp til pakkeadgang"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-de/strings.xml b/packages/DefaultContainerService/res/values-de/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-de/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-el/strings.xml b/packages/DefaultContainerService/res/values-el/strings.xml
new file mode 100644
index 0000000..a4d8144
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-el/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Βοηθός πρόσβασης πακέτου"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-es-rUS/strings.xml b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..670c2c5
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Asist. p/acceder al paq."</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-es/strings.xml b/packages/DefaultContainerService/res/values-es/strings.xml
new file mode 100644
index 0000000..022c461
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-es/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Ayudante acceso a paquete"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-fr/strings.xml b/packages/DefaultContainerService/res/values-fr/strings.xml
new file mode 100644
index 0000000..5c458bc
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-fr/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Aide accès au package"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-it/strings.xml b/packages/DefaultContainerService/res/values-it/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-it/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ja/strings.xml b/packages/DefaultContainerService/res/values-ja/strings.xml
new file mode 100644
index 0000000..2f57e4e
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ja/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"パッケージアクセス支援ツール"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ko/strings.xml b/packages/DefaultContainerService/res/values-ko/strings.xml
new file mode 100644
index 0000000..0304972
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ko/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"패키지 액세스 도움말"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-nb/strings.xml b/packages/DefaultContainerService/res/values-nb/strings.xml
new file mode 100644
index 0000000..637f54d
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-nb/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Hjelpeprogram for pakketilgang"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-nl/strings.xml b/packages/DefaultContainerService/res/values-nl/strings.xml
new file mode 100644
index 0000000..9ece040
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-nl/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Helper voor pakkettoegang"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pl/strings.xml b/packages/DefaultContainerService/res/values-pl/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pl/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..5c03669
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Ajuda p/ aceder pacotes"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pt/strings.xml b/packages/DefaultContainerService/res/values-pt/strings.xml
new file mode 100644
index 0000000..5fbd949
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pt/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Assistente de pacote"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ru/strings.xml b/packages/DefaultContainerService/res/values-ru/strings.xml
new file mode 100644
index 0000000..ccb0c53
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ru/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Мастер доступа к пакетам"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-sv/strings.xml b/packages/DefaultContainerService/res/values-sv/strings.xml
new file mode 100644
index 0000000..097a709
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-sv/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Hjälp med paketåtkomst"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-tr/strings.xml b/packages/DefaultContainerService/res/values-tr/strings.xml
new file mode 100644
index 0000000..12ea674
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-tr/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Paket Erişim Yardımcısı"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rCN/strings.xml b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..65928b1
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"软件包访问帮助程序"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rTW/strings.xml b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..9a43509
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"套件存取輔助程式"</string>
+</resources>
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 509c789..af4d7e4 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -116,6 +116,8 @@
private final LockList mLocks = new LockList();
// some wifi lock statistics
+ private int mFullHighPerfLocksAcquired;
+ private int mFullHighPerfLocksReleased;
private int mFullLocksAcquired;
private int mFullLocksReleased;
private int mScanLocksAcquired;
@@ -1782,8 +1784,8 @@
msg.sendToTarget();
}
- private void sendStartMessage(boolean scanOnlyMode) {
- Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
+ private void sendStartMessage(int lockMode) {
+ Message.obtain(mWifiHandler, MESSAGE_START_WIFI, lockMode, 0).sendToTarget();
}
private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
@@ -1801,12 +1803,15 @@
boolean wifiEnabled = getPersistedWifiEnabled();
boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
boolean lockHeld = mLocks.hasLocks();
- int strongestLockMode;
+ int strongestLockMode = WifiManager.WIFI_MODE_FULL;
boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
- if (mDeviceIdle && lockHeld) {
+
+ if (lockHeld) {
strongestLockMode = mLocks.getStrongestLockMode();
- } else {
+ }
+ /* If device is not idle, lockmode cannot be scan only */
+ if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
strongestLockMode = WifiManager.WIFI_MODE_FULL;
}
@@ -1827,7 +1832,7 @@
sWakeLock.acquire();
sendEnableMessage(true, false, mLastEnableUid);
sWakeLock.acquire();
- sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
+ sendStartMessage(strongestLockMode);
} else if (!mWifiStateTracker.isDriverStopped()) {
int wakeLockTimeout =
Settings.Secure.getInt(
@@ -1905,8 +1910,10 @@
break;
case MESSAGE_START_WIFI:
- mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
+ mWifiStateTracker.setScanOnlyMode(msg.arg1 == WifiManager.WIFI_MODE_SCAN_ONLY);
mWifiStateTracker.restart();
+ mWifiStateTracker.setHighPerfMode(msg.arg1 ==
+ WifiManager.WIFI_MODE_FULL_HIGH_PERF);
sWakeLock.release();
break;
@@ -1986,8 +1993,10 @@
}
pw.println();
pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
+ mFullHighPerfLocksAcquired + " full high perf, " +
mScanLocksAcquired + " scan");
pw.println("Locks released: " + mFullLocksReleased + " full, " +
+ mFullHighPerfLocksReleased + " full high perf, " +
mScanLocksReleased + " scan");
pw.println();
pw.println("Locks held:");
@@ -2042,11 +2051,15 @@
if (mList.isEmpty()) {
return WifiManager.WIFI_MODE_FULL;
}
- for (WifiLock l : mList) {
- if (l.mMode == WifiManager.WIFI_MODE_FULL) {
- return WifiManager.WIFI_MODE_FULL;
- }
+
+ if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
+ return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
}
+
+ if (mFullLocksAcquired > mFullLocksReleased) {
+ return WifiManager.WIFI_MODE_FULL;
+ }
+
return WifiManager.WIFI_MODE_SCAN_ONLY;
}
@@ -2085,7 +2098,11 @@
public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
- if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
+ if (lockMode != WifiManager.WIFI_MODE_FULL &&
+ lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
+ lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
+ Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
+ if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
return false;
}
WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
@@ -2107,6 +2124,12 @@
++mFullLocksAcquired;
mBatteryStats.noteFullWifiLockAcquired(uid);
break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ ++mFullHighPerfLocksAcquired;
+ /* Treat high power as a full lock for battery stats */
+ mBatteryStats.noteFullWifiLockAcquired(uid);
+ break;
+
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksAcquired;
mBatteryStats.noteScanWifiLockAcquired(uid);
@@ -2146,6 +2169,10 @@
++mFullLocksReleased;
mBatteryStats.noteFullWifiLockReleased(uid);
break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ ++mFullHighPerfLocksReleased;
+ mBatteryStats.noteFullWifiLockReleased(uid);
+ break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksReleased;
mBatteryStats.noteScanWifiLockReleased(uid);
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index ea6aa94..c1165c7 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -197,6 +197,9 @@
// capabilities of the GPS engine
private int mEngineCapabilities;
+ // true if XTRA is supported
+ private boolean mSupportsXtra;
+
// for calculating time to first fix
private long mFixRequestTime = 0;
// time to first fix for most recent session
@@ -635,6 +638,7 @@
mEnabled = native_init();
if (mEnabled) {
+ mSupportsXtra = native_supports_xtra();
if (mSuplServerHost != null) {
native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
}
@@ -839,7 +843,7 @@
sendMessage(INJECT_NTP_TIME, 0, null);
result = true;
} else if ("force_xtra_injection".equals(command)) {
- if (native_supports_xtra()) {
+ if (mSupportsXtra) {
xtraDownloadRequest();
result = true;
}
@@ -1372,7 +1376,7 @@
handleInjectNtpTime();
break;
case DOWNLOAD_XTRA_DATA:
- if (native_supports_xtra()) {
+ if (mSupportsXtra) {
handleDownloadXtraData();
}
break;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index ebe71ab..ba58b43 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -319,9 +319,9 @@
bool isScreenOn();
bool isScreenBright();
- // Weak references to all currently registered input channels by receive fd.
+ // Weak references to all currently registered input channels by connection pointer.
Mutex mInputChannelRegistryLock;
- KeyedVector<int, jweak> mInputChannelObjWeakByReceiveFd;
+ KeyedVector<InputChannel*, jweak> mInputChannelObjWeakTable;
jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
@@ -509,8 +509,7 @@
{
AutoMutex _l(mInputChannelRegistryLock);
- ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
- inputChannel->getReceivePipeFd());
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
if (index >= 0) {
LOGE("Input channel object '%s' has already been registered",
inputChannel->getName().string());
@@ -518,8 +517,7 @@
goto DeleteWeakRef;
}
- mInputChannelObjWeakByReceiveFd.add(inputChannel->getReceivePipeFd(),
- inputChannelObjWeak);
+ mInputChannelObjWeakTable.add(inputChannel.get(), inputChannelObjWeak);
}
status = mInputManager->registerInputChannel(inputChannel);
@@ -534,7 +532,7 @@
// Failed!
{
AutoMutex _l(mInputChannelRegistryLock);
- mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
+ mInputChannelObjWeakTable.removeItem(inputChannel.get());
}
DeleteWeakRef:
@@ -548,16 +546,15 @@
{
AutoMutex _l(mInputChannelRegistryLock);
- ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
- inputChannel->getReceivePipeFd());
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
if (index < 0) {
LOGE("Input channel object '%s' is not currently registered",
inputChannel->getName().string());
return INVALID_OPERATION;
}
- inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
- mInputChannelObjWeakByReceiveFd.removeItemsAt(index);
+ inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index);
+ mInputChannelObjWeakTable.removeItemsAt(index);
}
env->DeleteWeakGlobalRef(inputChannelObjWeak);
@@ -572,13 +569,12 @@
{
AutoMutex _l(mInputChannelRegistryLock);
- ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
- inputChannel->getReceivePipeFd());
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
if (index < 0) {
return NULL;
}
- jweak inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
+ jweak inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index);
return env->NewLocalRef(inputChannelObjWeak);
}
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 4a22b68..9d21521 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -307,6 +307,16 @@
public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
/**
+ * In this Wi-Fi lock mode, Wi-Fi will behave as in the mode
+ * {@link #WIFI_MODE_FULL} but it operates at high performance
+ * at the expense of power. This mode should be used
+ * only when the wifi connection needs to have minimum loss and low
+ * latency as it can impact the battery life.
+ * @hide
+ */
+ public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
+
+ /**
* In this Wi-Fi lock mode, Wi-Fi will be kept active,
* and will behave normally, i.e., it will attempt to automatically
* establish a connection to a remembered access point that is
@@ -993,8 +1003,9 @@
/**
* Creates a new WifiLock.
*
- * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and
- * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
+ * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
+ * and {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
+ *
* @param tag a tag for the WifiLock to identify it in debugging messages. This string is
* never shown to the user under normal conditions, but should be descriptive
* enough to identify your application and the specific WifiLock within it, if it
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 7a3282c..25f05c0 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -149,6 +149,8 @@
public native static String getDhcpError();
+ public native static boolean setSuspendOptimizationsCommand(boolean enabled);
+
/**
* Wait for the supplicant to send an event, returning the event string.
* @return the event string sent by the supplicant.
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index f57df62..0cc1f46 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -276,6 +276,9 @@
private boolean mIsScanModeActive;
private boolean mEnableRssiPolling;
+ private boolean mIsHighPerfEnabled;
+ private int mPowerModeRefCount = 0;
+ private int mOptimizationsDisabledRefCount = 0;
/**
* One of {@link WifiManager#WIFI_STATE_DISABLED},
@@ -659,6 +662,67 @@
}
}
+ /**
+ * Set suspend mode optimizations. These include:
+ * - packet filtering
+ * - turn off roaming
+ * - DTIM settings
+ *
+ * Uses reference counting to keep the suspend optimizations disabled
+ * as long as one entity wants optimizations disabled.
+ *
+ * For example, WifiLock can keep suspend optimizations disabled
+ * or the user setting (wifi never sleeps) can keep suspend optimizations
+ * disabled. As long as one entity wants it disabled, it should stay
+ * that way
+ *
+ * @param enabled true if optimizations need enabled, false otherwise
+ */
+ public synchronized void setSuspendModeOptimizations(boolean enabled) {
+
+ /* It is good to plumb suspend optimization enable
+ * or disable even if ref count indicates already done
+ * since we could have a case of previous failure.
+ */
+ if (!enabled) {
+ mOptimizationsDisabledRefCount++;
+ } else {
+ mOptimizationsDisabledRefCount--;
+ if (mOptimizationsDisabledRefCount > 0) {
+ return;
+ } else {
+ /* Keep refcount from becoming negative */
+ mOptimizationsDisabledRefCount = 0;
+ }
+ }
+
+ if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
+ return;
+ }
+
+ WifiNative.setSuspendOptimizationsCommand(enabled);
+ }
+
+
+ /**
+ * Set high performance mode of operation. This would mean
+ * use active power mode and disable suspend optimizations
+ * @param enabled true if enabled, false otherwise
+ */
+ public synchronized void setHighPerfMode(boolean enabled) {
+ if (mIsHighPerfEnabled != enabled) {
+ if (enabled) {
+ setPowerMode(DRIVER_POWER_MODE_ACTIVE);
+ setSuspendModeOptimizations(false);
+ } else {
+ setPowerMode(DRIVER_POWER_MODE_AUTO);
+ setSuspendModeOptimizations(true);
+ }
+ mIsHighPerfEnabled = enabled;
+ Log.d(TAG,"high performance mode: " + enabled);
+ }
+ }
+
private void checkIsBluetoothPlaying() {
boolean isBluetoothPlaying = false;
@@ -744,6 +808,9 @@
dhcpThread.start();
mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
mIsScanModeActive = true;
+ mIsHighPerfEnabled = false;
+ mOptimizationsDisabledRefCount = 0;
+ mPowerModeRefCount = 0;
mTornDownByConnMgr = false;
mLastBssid = null;
mLastSsid = null;
@@ -1947,13 +2014,41 @@
* @param mode
* DRIVER_POWER_MODE_AUTO
* DRIVER_POWER_MODE_ACTIVE
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ *
+ * Uses reference counting to keep power mode active
+ * as long as one entity wants power mode to be active.
+ *
+ * For example, WifiLock high perf mode can keep power mode active
+ * or a DHCP session can keep it active. As long as one entity wants
+ * it enabled, it should stay that way
+ *
*/
- public synchronized boolean setPowerMode(int mode) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
+ private synchronized void setPowerMode(int mode) {
+
+ /* It is good to plumb power mode change
+ * even if ref count indicates already done
+ * since we could have a case of previous failure.
+ */
+ switch(mode) {
+ case DRIVER_POWER_MODE_ACTIVE:
+ mPowerModeRefCount++;
+ break;
+ case DRIVER_POWER_MODE_AUTO:
+ mPowerModeRefCount--;
+ if (mPowerModeRefCount > 0) {
+ return;
+ } else {
+ /* Keep refcount from becoming negative */
+ mPowerModeRefCount = 0;
+ }
+ break;
}
- return WifiNative.setPowerModeCommand(mode);
+
+ if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
+ return;
+ }
+
+ WifiNative.setPowerModeCommand(mode);
}
/**