diff options
23 files changed, 984 insertions, 161 deletions
diff --git a/Android.mk b/Android.mk index b1f43f8e6f2a..9f9263754358 100644 --- a/Android.mk +++ b/Android.mk @@ -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 392ea95c2276..f49477571b16 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 e69c3241350b..447e64208ce2 100644 --- a/core/java/android/net/DownloadManager.java +++ b/core/java/android/net/DownloadManager.java @@ -185,6 +185,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"; @@ -715,7 +721,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 +747,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/provider/Downloads.java b/core/java/android/provider/Downloads.java index c9b55121aa84..6bf0d5b7ad8c 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/jni/Android.mk b/core/jni/Android.mk index efdc3994ff77..42135c528e13 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 62ca2efe3b8b..407d2e7e63a6 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 000000000000..f0739ea84911 --- /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 000000000000..37b86f92f990 --- /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 b41bad0d66e8..21b2e3bde902 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 000000000000..f868434620f1 --- /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 007757f4361f..137acc612cbf 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 cf21dde12301..c34c96ae54f1 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 5659ba2f353f..204bb74b03f1 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 fe24b055577e..8d6528bde70f 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 000000000000..4cf5dfa49da0 --- /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 739244248281..903283e4dbd7 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/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 2313f4cb30a4..89823882220f 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 000000000000..6656b1752e91 --- /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/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 509c789e4080..af4d7e4d9724 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 ea6aa94dcbe9..c1165c7bc7f3 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/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 4a22b6885203..3b41dc13602d 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}, + * {@link #WIFI_MODE_SCAN_ONLY} and {@link #WIFI_MODE_FULL_HIGH_PERF} 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 7a3282c241d4..25f05c01f964 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 f57df62b19e1..0cc1f464c974 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); } /** |