diff options
61 files changed, 1923 insertions, 320 deletions
@@ -375,6 +375,8 @@ sample_dir := development/samples # (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..cafe0f9 100644 --- a/core/java/android/net/DownloadManager.java +++ b/core/java/android/net/DownloadManager.java @@ -51,14 +51,14 @@ public class DownloadManager { public final static String COLUMN_ID = "id"; /** - * The client-supplied title for this download. This will be displayed in system notifications, - * if enabled. + * The client-supplied title for this download. This will be displayed in system notifications. + * Defaults to the empty string. */ public final static String COLUMN_TITLE = "title"; /** * The client-supplied description of this download. This will be displayed in system - * notifications, if enabled. + * notifications. Defaults to the empty string. */ public final static String COLUMN_DESCRIPTION = "description"; @@ -68,22 +68,24 @@ public class DownloadManager { public final static String COLUMN_URI = "uri"; /** - * Internet Media Type of the downloaded file. This will be filled in based on the server's - * response once the download has started. + * Internet Media Type of the downloaded file. If no value is provided upon creation, this will + * initially be null and will be filled in based on the server's response once the download has + * started. * * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a> */ public final static String COLUMN_MEDIA_TYPE = "media_type"; /** - * Total size of the download in bytes. This will be filled in once the download starts. + * Total size of the download in bytes. This will initially be -1 and will be filled in once + * the download starts. */ public final static String COLUMN_TOTAL_SIZE_BYTES = "total_size"; /** * Uri where downloaded file will be stored. If a destination is supplied by client, that URI - * will be used here. Otherwise, the value will be filled in with a generated URI once the - * download has started. + * will be used here. Otherwise, the value will initially be null and will be filled in with a + * generated URI once the download has started. */ public final static String COLUMN_LOCAL_URI = "local_uri"; @@ -185,6 +187,12 @@ public class DownloadManager { 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"; @@ -682,8 +690,13 @@ public class DownloadManager { if (column.equals(COLUMN_MEDIA_TYPE)) { return getUnderlyingString(Downloads.COLUMN_MIME_TYPE); } + assert column.equals(COLUMN_LOCAL_URI); - return Uri.fromFile(new File(getUnderlyingString(Downloads._DATA))).toString(); + String localUri = getUnderlyingString(Downloads._DATA); + if (localUri == null) { + return null; + } + return Uri.fromFile(new File(localUri)).toString(); } private long translateLong(String column) { @@ -715,7 +728,8 @@ public class DownloadManager { 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 +754,9 @@ public class DownloadManager { 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 @@ public class VCardBuilder { 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 @@ public class VCardBuilder { // 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 (first) { - first = false; - } else { - mBuilder.append(VCARD_PARAM_SEPARATOR); + 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); } - 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 @@ public class VCardEntryConstructor implements VCardInterpreter { 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 @@ public class VCardUtils { 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 final class Downloads { 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/java/android/view/Display.java b/core/java/android/view/Display.java index fabe5c8..34d7935 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -27,7 +27,8 @@ public class Display /** - * Use the WindowManager interface to create a Display object. + * Use {@link android.view.WindowManager#getDefaultDisplay() + * WindowManager.getDefaultDisplay()} to create a Display object. * Display gives you access to some information about a particular display * connected to the device. */ 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 @@ LOCAL_SRC_FILES:= \ 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 @@ LOCAL_SRC_FILES:= \ 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_Binder(JNIEnv* env); 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 @@ static const RegJNIRec gRegJNI[] = { 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 @@ static jfieldID gFileDescriptor_descriptor; #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 @@ static inline int32_t validOrNeg1(bool isValid, int32_t value) { 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 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, 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 void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) { } } +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 @@ static JNINativeMethod gMethods[] = { }, { "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 @@ private: }; SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, - jbyteArray storage) { + jbyteArray storage, int markSize) { static bool gInited; if (!gInited) { @@ -153,6 +154,8 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, 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 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, "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 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, 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 @@ void doThrowOOME(JNIEnv* env, const char* msg) { 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 jmethodID gBitmap_allocBufferMethodID; 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 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, } 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 @@ bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { //////////////////////////////////////////////////////////////////////////////// +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 @@ int register_android_graphics_Graphics(JNIEnv* env) 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 @@ public: 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 @@ private: 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 doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument 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 @@ static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject claz 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 @@ static JNINativeMethod gWifiMethods[] = { { "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 @@ private: 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 @@ private: // 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 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne 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 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne 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 @@ status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChan 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 @@ status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignor 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 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da 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 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da 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 @@ public class VCardImporterTests extends VCardTestsBase { .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 @@ public class VCardUtilsTests extends TestCase { 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.FileDescriptor; 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 @@ public class BitmapFactory { 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 @@ public class BitmapFactory { 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/EventHub.h b/include/ui/EventHub.h index dab35b3..3132941 100644 --- a/include/ui/EventHub.h +++ b/include/ui/EventHub.h @@ -266,6 +266,12 @@ private: #ifdef EV_SW int32_t mSwitches[SW_MAX + 1]; #endif + + static const int INPUT_BUFFER_SIZE = 64; + struct input_event mInputBufferData[INPUT_BUFFER_SIZE]; + int32_t mInputBufferIndex; + int32_t mInputBufferCount; + int32_t mInputDeviceIndex; }; }; // namespace android 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 @@ private: // 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/EventHub.cpp b/libs/ui/EventHub.cpp index 124f7b3..33393fe 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -100,6 +100,7 @@ EventHub::EventHub(void) , mDevicesById(0), mNumDevicesById(0) , mOpeningDevices(0), mClosingDevices(0) , mDevices(0), mFDs(0), mFDCount(0), mOpened(false) + , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); #ifdef EV_SW @@ -343,13 +344,6 @@ bool EventHub::getEvent(RawEvent* outEvent) outEvent->value = 0; outEvent->when = 0; - status_t err; - - int i; - int res; - int pollres; - struct input_event iev; - // Note that we only allow one caller to getEvent(), so don't need // to do locking here... only when adding/removing devices. @@ -358,9 +352,8 @@ bool EventHub::getEvent(RawEvent* outEvent) mOpened = true; } - while(1) { - - // First, report any devices that had last been added/removed. + for (;;) { + // Report any devices that had last been added/removed. if (mClosingDevices != NULL) { device_t* device = mClosingDevices; LOGV("Reporting device closed: id=0x%x, name=%s\n", @@ -390,77 +383,96 @@ bool EventHub::getEvent(RawEvent* outEvent) return true; } - release_wake_lock(WAKE_LOCK_ID); - - pollres = poll(mFDs, mFDCount, -1); + // Grab the next input event. + for (;;) { + // Consume buffered input events, if any. + if (mInputBufferIndex < mInputBufferCount) { + const struct input_event& iev = mInputBufferData[mInputBufferIndex++]; + const device_t* device = mDevices[mInputDeviceIndex]; + + LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(), + (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = iev.type; + outEvent->scanCode = iev.code; + if (iev.type == EV_KEY) { + status_t err = device->layoutMap->map(iev.code, + & outEvent->keyCode, & outEvent->flags); + LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", + iev.code, outEvent->keyCode, outEvent->flags, err); + if (err != 0) { + outEvent->keyCode = AKEYCODE_UNKNOWN; + outEvent->flags = 0; + } + } else { + outEvent->keyCode = iev.code; + } + outEvent->value = iev.value; - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + // Use an event timestamp in the same timebase as + // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis() + // as expected by the rest of the system. + outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); + return true; + } - if (pollres <= 0) { - if (errno != EINTR) { - LOGW("select failed (errno=%d)\n", errno); - usleep(100000); + // Finish reading all events from devices identified in previous poll(). + // This code assumes that mInputDeviceIndex is initially 0 and that the + // revents member of pollfd is initialized to 0 when the device is first added. + // Since mFDs[0] is used for inotify, we process regular events starting at index 1. + mInputDeviceIndex += 1; + if (mInputDeviceIndex >= mFDCount) { + mInputDeviceIndex = 0; + break; } - continue; - } - //printf("poll %d, returned %d\n", mFDCount, pollres); - - // mFDs[0] is used for inotify, so process regular events starting at mFDs[1] - for(i = 1; i < mFDCount; i++) { - if(mFDs[i].revents) { - LOGV("revents for %d = 0x%08x", i, mFDs[i].revents); - if(mFDs[i].revents & POLLIN) { - res = read(mFDs[i].fd, &iev, sizeof(iev)); - if (res == sizeof(iev)) { - device_t* device = mDevices[i]; - LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", - device->path.string(), - (int) iev.time.tv_sec, (int) iev.time.tv_usec, - iev.type, iev.code, iev.value); - if (device->id == mFirstKeyboardId) { - outEvent->deviceId = 0; - } else { - outEvent->deviceId = device->id; - } - outEvent->type = iev.type; - outEvent->scanCode = iev.code; - if (iev.type == EV_KEY) { - err = device->layoutMap->map(iev.code, - & outEvent->keyCode, & outEvent->flags); - LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", - iev.code, outEvent->keyCode, outEvent->flags, err); - if (err != 0) { - outEvent->keyCode = AKEYCODE_UNKNOWN; - outEvent->flags = 0; - } - } else { - outEvent->keyCode = iev.code; - } - outEvent->value = iev.value; - - // Use an event timestamp in the same timebase as - // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis() - // as expected by the rest of the system. - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - return true; - } else { - if (res<0) { - LOGW("could not get event (errno=%d)", errno); - } else { - LOGE("could not get event (wrong size: %d)", res); - } - continue; + const struct pollfd &pfd = mFDs[mInputDeviceIndex]; + if (pfd.revents & POLLIN) { + int32_t readSize = read(pfd.fd, mInputBufferData, + sizeof(struct input_event) * INPUT_BUFFER_SIZE); + if (readSize < 0) { + if (errno != EAGAIN && errno != EINTR) { + LOGW("could not get event (errno=%d)", errno); } + } else if ((readSize % sizeof(struct input_event)) != 0) { + LOGE("could not get event (wrong size: %d)", readSize); + } else { + mInputBufferCount = readSize / sizeof(struct input_event); + mInputBufferIndex = 0; } } } - + // read_notify() will modify mFDs and mFDCount, so this must be done after // processing all other events. if(mFDs[0].revents & POLLIN) { read_notify(mFDs[0].fd); } + + // Poll for events. Mind the wake lock dance! + // We hold a wake lock at all times except during poll(). This works due to some + // subtle choreography. When a device driver has pending (unread) events, it acquires + // a kernel wake lock. However, once the last pending event has been read, the device + // driver will release the kernel wake lock. To prevent the system from going to sleep + // when this happens, the EventHub holds onto its own user wake lock while the client + // is processing events. Thus the system can only sleep if there are no events + // pending or currently being processed. + release_wake_lock(WAKE_LOCK_ID); + + int pollResult = poll(mFDs, mFDCount, -1); + + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + + if (pollResult <= 0) { + if (errno != EINTR) { + LOGW("select failed (errno=%d)\n", errno); + usleep(100000); + } + } } } @@ -478,6 +490,7 @@ bool EventHub::openPlatformInput(void) mFDs = (pollfd *)calloc(1, sizeof(mFDs[0])); mDevices = (device_t **)calloc(1, sizeof(mDevices[0])); mFDs[0].events = POLLIN; + mFDs[0].revents = 0; mDevices[0] = NULL; #ifdef HAVE_INOTIFY mFDs[0].fd = inotify_init(); @@ -584,6 +597,12 @@ int EventHub::open_device(const char *deviceName) idstr[0] = '\0'; } + if (fcntl(fd, F_SETFL, O_NONBLOCK)) { + LOGE("Error %d making device file descriptor non-blocking.", errno); + close(fd); + return -1; + } + int devid = 0; while (devid < mNumDevicesById) { if (mDevicesById[devid].device == NULL) { @@ -640,6 +659,7 @@ int EventHub::open_device(const char *deviceName) mFDs[mFDCount].fd = fd; mFDs[mFDCount].events = POLLIN; + mFDs[mFDCount].revents = 0; // Figure out the kinds of events the device reports. 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 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi 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 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan 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 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan 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 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh 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 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh 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/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 6af3a7f..12a1e6e 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1273,6 +1273,14 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( hexdump(csd, csd_size); #endif + if (csd_size == 0) { + // There's no further information, i.e. no codec specific data + // Let's assume that the information provided in the mpeg4 headers + // is accurate and hope for the best. + + return OK; + } + if (csd_size < 2) { return ERROR_MALFORMED; } 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 @@ public class WifiService extends IWifiManager.Stub { 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 @@ public class WifiService extends IWifiManager.Stub { 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 @@ public class WifiService extends IWifiManager.Stub { 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 @@ public class WifiService extends IWifiManager.Stub { 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 @@ public class WifiService extends IWifiManager.Stub { 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 @@ public class WifiService extends IWifiManager.Stub { } 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 @@ public class WifiService extends IWifiManager.Stub { 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 class WifiService extends IWifiManager.Stub { 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 @@ public class WifiService extends IWifiManager.Stub { ++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 @@ public class WifiService extends IWifiManager.Stub { ++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 @@ public class GpsLocationProvider implements LocationProviderInterface { // 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 @@ public class GpsLocationProvider implements LocationProviderInterface { 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 @@ public class GpsLocationProvider implements LocationProviderInterface { 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 @@ public class GpsLocationProvider implements LocationProviderInterface { 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 @@ private: 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 @@ status_t NativeInputManager::registerInputChannel(JNIEnv* env, { 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 @@ status_t NativeInputManager::registerInputChannel(JNIEnv* env, goto DeleteWeakRef; } - mInputChannelObjWeakByReceiveFd.add(inputChannel->getReceivePipeFd(), - inputChannelObjWeak); + mInputChannelObjWeakTable.add(inputChannel.get(), inputChannelObjWeak); } status = mInputManager->registerInputChannel(inputChannel); @@ -534,7 +532,7 @@ status_t NativeInputManager::registerInputChannel(JNIEnv* env, // Failed! { AutoMutex _l(mInputChannelRegistryLock); - mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd()); + mInputChannelObjWeakTable.removeItem(inputChannel.get()); } DeleteWeakRef: @@ -548,16 +546,15 @@ status_t NativeInputManager::unregisterInputChannel(JNIEnv* env, { 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 @@ jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env, { 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/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp index b80dbc5..146c177 100644 --- a/services/jni/com_android_server_PowerManagerService.cpp +++ b/services/jni/com_android_server_PowerManagerService.cpp @@ -22,6 +22,7 @@ #include "jni.h" #include <limits.h> #include <android_runtime/AndroidRuntime.h> +#include <utils/Timers.h> #include "com_android_server_PowerManagerService.h" namespace android { @@ -43,6 +44,11 @@ static Mutex gPowerManagerLock; static bool gScreenOn; static bool gScreenBright; +static nsecs_t gLastEventTime[POWER_MANAGER_LAST_EVENT + 1]; + +// Throttling interval for user activity calls. +static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 500 * 1000000L; // 500ms + // ---------------------------------------------------------------------------- static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { @@ -67,6 +73,21 @@ bool android_server_PowerManagerService_isScreenBright() { void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) { if (gPowerManagerServiceObj) { + // Throttle calls into user activity by event type. + // We're a little conservative about argument checking here in case the caller + // passes in bad data which could corrupt system state. + if (eventType >= 0 && eventType <= POWER_MANAGER_LAST_EVENT) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (eventTime > now) { + eventTime = now; + } + + if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) { + return; + } + gLastEventTime[eventType] = eventTime; + } + JNIEnv* env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity, @@ -136,6 +157,12 @@ int register_android_server_PowerManagerService(JNIEnv* env) { GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz, "userActivity", "(JZIZ)V"); + // Initialize + for (int i = 0; i < POWER_MANAGER_LAST_EVENT; i++) { + gLastEventTime[i] = LLONG_MIN; + } + gScreenOn = true; + gScreenBright = true; return 0; } diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h index 9b05f38..7c329b2 100644 --- a/services/jni/com_android_server_PowerManagerService.h +++ b/services/jni/com_android_server_PowerManagerService.h @@ -30,6 +30,8 @@ enum { POWER_MANAGER_LONG_TOUCH_EVENT = 3, POWER_MANAGER_TOUCH_UP_EVENT = 4, POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events. + + POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code. }; extern bool android_server_PowerManagerService_isScreenOn(); diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java index 819cfbe..8e08592 100644 --- a/telephony/java/com/android/internal/telephony/CallManager.java +++ b/telephony/java/com/android/internal/telephony/CallManager.java @@ -16,16 +16,17 @@ package com.android.internal.telephony; +import com.android.internal.telephony.sip.SipPhone; + import android.content.Context; +import android.media.AudioManager; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.os.RegistrantList; -import android.util.Log; - import android.telephony.PhoneStateListener; import android.telephony.ServiceState; - +import android.util.Log; import java.util.ArrayList; import java.util.Collections; @@ -52,7 +53,7 @@ import java.util.List; */ public final class CallManager { - private static final String LOG_TAG ="GSM"; + private static final String LOG_TAG ="Phone"; private static final boolean LOCAL_DEBUG = true; private static final int EVENT_DISCONNECT = 100; @@ -303,6 +304,32 @@ public final class CallManager { } } + public void setAudioMode() { + Context context = getContext(); + if (context == null) return; + AudioManager audioManager = (AudioManager) + context.getSystemService(Context.AUDIO_SERVICE); + + int mode = AudioManager.MODE_NORMAL; + switch (getState()) { + case RINGING: + mode = AudioManager.MODE_RINGTONE; + break; + case OFFHOOK: + Phone fgPhone = getFgPhone(); + if (!(fgPhone instanceof SipPhone)) { + mode = AudioManager.MODE_IN_CALL; + } + break; + } + audioManager.setMode(mode); + } + + private Context getContext() { + Phone defaultPhone = getDefaultPhone(); + return ((defaultPhone == null) ? null : defaultPhone.getContext()); + } + private void registerForPhoneStates(Phone phone) { phone.registerForPreciseCallStateChanged(mHandler, EVENT_PRECISE_CALL_STATE_CHANGED, null); phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null); @@ -453,7 +480,7 @@ public final class CallManager { heldPhone = heldCall.getPhone(); } - return (heldPhone == activePhone); + return heldPhone.getClass().equals(activePhone.getClass()); } /** @@ -466,10 +493,14 @@ public final class CallManager { * In these cases, this operation may not be performed. */ public void conference(Call heldCall) throws CallStateException { - if (canConference(heldCall)) + Phone fgPhone = getFgPhone(); + if (fgPhone instanceof SipPhone) { + ((SipPhone) fgPhone).conference(heldCall); + } else if (canConference(heldCall)) { + fgPhone.conference(); + } else { throw(new CallStateException("Can't conference foreground and selected background call")); - - heldCall.getPhone().conference(); + } } /** @@ -1288,7 +1319,10 @@ public final class CallManager { mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj); break; case EVENT_INCOMING_RING: - mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj); + // The event may come from RIL who's not aware of an ongoing fg call + if (!hasActiveFgCall()) { + mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj); + } break; case EVENT_RINGBACK_TONE: mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj); diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java index a94518f..a052db0 100755 --- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.net.Uri; import android.net.rtp.AudioGroup; +import android.net.rtp.AudioStream; import android.net.sip.SipAudioCall; import android.net.sip.SipManager; import android.net.sip.SipProfile; @@ -221,7 +222,21 @@ public class SipPhone extends SipPhoneBase { } public void conference() throws CallStateException { - // TODO + if ((foregroundCall.getState() != SipCall.State.ACTIVE) + || (foregroundCall.getState() != SipCall.State.ACTIVE)) { + throw new CallStateException("wrong state to merge calls: fg=" + + foregroundCall.getState() + ", bg=" + + backgroundCall.getState()); + } + foregroundCall.merge(backgroundCall); + } + + public void conference(Call that) throws CallStateException { + if (!(that instanceof SipCall)) { + throw new CallStateException("expect " + SipCall.class + + ", cannot merge with " + that.getClass()); + } + foregroundCall.merge((SipCall) that); } public boolean canTransfer() { @@ -467,6 +482,18 @@ public class SipPhone extends SipPhoneBase { return (audioGroup.getMode() == AudioGroup.MODE_MUTED); } + void merge(SipCall that) throws CallStateException { + AudioGroup myGroup = getAudioGroup(); + for (Connection c : that.connections) { + SipConnection conn = (SipConnection) c; + conn.mergeTo(myGroup); + connections.add(conn); + conn.changeOwner(this); + } + that.connections.clear(); + that.setState(Call.State.IDLE); + } + void sendDtmf(char c) { AudioGroup audioGroup = getAudioGroup(); if (audioGroup == null) return; @@ -541,6 +568,7 @@ public class SipPhone extends SipPhoneBase { private class SipConnection extends SipConnectionBase { private SipCall mOwner; private SipAudioCall mSipAudioCall; + private AudioGroup mOriginalGroup; private Call.State mState = Call.State.IDLE; private SipProfile mPeer; private boolean mIncoming = false; @@ -660,6 +688,16 @@ public class SipPhone extends SipPhoneBase { } } + void mergeTo(AudioGroup group) throws CallStateException { + AudioStream stream = mSipAudioCall.getAudioStream(); + if (stream == null) { + throw new CallStateException("wrong state to merge: " + + mSipAudioCall.getState()); + } + if (mOriginalGroup == null) mOriginalGroup = getAudioGroup(); + stream.join(group); + } + @Override protected void setState(Call.State state) { if (state == mState) return; @@ -694,6 +732,8 @@ public class SipPhone extends SipPhoneBase { @Override public void hangup() throws CallStateException { + // TODO: need to pull AudioStream out of the AudioGroup in case + // this conn was part of a conf call Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": " + ": on phone " + getPhone()); try { 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 class WifiManager { 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 @@ public class WifiManager { /** * 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 class WifiNative { 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 @@ public class WifiStateTracker extends NetworkStateTracker { 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 @@ public class WifiStateTracker extends NetworkStateTracker { } } + /** + * 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 @@ public class WifiStateTracker extends NetworkStateTracker { 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 @@ public class WifiStateTracker extends NetworkStateTracker { * @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) { + 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; + } + if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; + return; } - return WifiNative.setPowerModeCommand(mode); + + WifiNative.setPowerModeCommand(mode); } /** |
