Merge "DPMS: Replace ArrayList<>(1) with SingletonList"
diff --git a/api/current.txt b/api/current.txt
index d54b7fd..8f93361 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -33245,6 +33245,7 @@
method public void onStopListening();
method public void onTileAdded();
method public void onTileRemoved();
+ method public final void showDialog(android.app.Dialog);
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 2ea5ebb..7a97280 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -35394,6 +35394,7 @@
method public void onStopListening();
method public void onTileAdded();
method public void onTileRemoved();
+ method public final void showDialog(android.app.Dialog);
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 904347d..ddb5b06 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -33247,6 +33247,7 @@
method public void onStopListening();
method public void onTileAdded();
method public void onTileRemoved();
+ method public final void showDialog(android.app.Dialog);
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
}
diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl
index 087eb61..7e70501 100644
--- a/core/java/android/service/quicksettings/IQSService.aidl
+++ b/core/java/android/service/quicksettings/IQSService.aidl
@@ -23,4 +23,5 @@
*/
interface IQSService {
void updateQsTile(in Tile tile);
+ void onShowDialog(in Tile tile);
}
diff --git a/core/java/android/service/quicksettings/IQSTileService.aidl b/core/java/android/service/quicksettings/IQSTileService.aidl
index 6b46bee5..63a4c5e 100644
--- a/core/java/android/service/quicksettings/IQSTileService.aidl
+++ b/core/java/android/service/quicksettings/IQSTileService.aidl
@@ -27,5 +27,5 @@
void onTileRemoved();
void onStartListening();
void onStopListening();
- void onClick();
+ void onClick(IBinder wtoken);
}
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index c8ae171..a53fc59 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -137,6 +137,18 @@
}
}
+ /**
+ * @hide
+ * Notifies the IQSService that this tile is showing a dialog.
+ */
+ void onShowDialog() {
+ try {
+ mService.onShowDialog(this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't onShowDialog");
+ }
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongInterface(mService);
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index eba4c6f..fd2d5b0 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -15,6 +15,7 @@
*/
package android.service.quicksettings;
+import android.app.Dialog;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
@@ -22,6 +23,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.view.WindowManager;
/**
* A QSTileService provides the user a tile that can be added to Quick Settings.
@@ -55,7 +57,7 @@
* android:icon="@drawable/my_default_icon_label"
* android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
* <intent-filter>
- * <action android:name="android.intent.action.QS_TILE" />
+ * <action android:name="android.service.quicksettings.action.QS_TILE" />
* </intent-filter>
* </service>}
* </pre>
@@ -73,6 +75,7 @@
private boolean mListening = false;
private Tile mTile;
+ private IBinder mToken;
/**
* Called when the user adds this tile to Quick Settings.
@@ -116,6 +119,20 @@
}
/**
+ * Used to show a dialog.
+ *
+ * This will collapse the Quick Settings panel and show the dialog.
+ *
+ * @param dialog Dialog to show.
+ */
+ public final void showDialog(Dialog dialog) {
+ dialog.getWindow().getAttributes().token = mToken;
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_QS_DIALOG);
+ dialog.show();
+ getQsTile().onShowDialog();
+ }
+
+ /**
* Gets the {@link Tile} for this service.
* <p/>
* This tile may be used to get or set the current state for this
@@ -155,8 +172,8 @@
}
@Override
- public void onClick() throws RemoteException {
- mHandler.sendEmptyMessage(H.MSG_TILE_CLICKED);
+ public void onClick(IBinder wtoken) throws RemoteException {
+ mHandler.obtainMessage(H.MSG_TILE_CLICKED, wtoken).sendToTarget();
}
};
}
@@ -185,19 +202,20 @@
case MSG_TILE_REMOVED:
TileService.this.onTileAdded();
break;
- case MSG_START_LISTENING:
+ case MSG_STOP_LISTENING:
if (mListening) {
mListening = false;
TileService.this.onStopListening();
}
break;
- case MSG_STOP_LISTENING:
+ case MSG_START_LISTENING:
if (!mListening) {
mListening = true;
TileService.this.onStartListening();
}
break;
case MSG_TILE_CLICKED:
+ mToken = (IBinder) msg.obj;
TileService.this.onClick();
break;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1521f2e..d6bc27c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -238,6 +238,7 @@
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION, to = "TYPE_VOICE_INTERACTION"),
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "TYPE_VOICE_INTERACTION_STARTING"),
@ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER, to = "TYPE_DOCK_DIVIDER"),
+ @ViewDebug.IntToString(from = TYPE_QS_DIALOG, to = "TYPE_QS_DIALOG"),
})
public int type;
@@ -584,6 +585,13 @@
public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
/**
+ * Window type: like {@link #TYPE_APPLICATION_ATTACHED_DIALOG}, but used
+ * by Quick Settings Tiles.
+ * @hide
+ */
+ public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
+
+ /**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 0473016f..8a90c4c 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -199,7 +199,6 @@
external/sqlite/android \
external/expat/lib \
external/tremor/Tremor \
- external/jpeg \
external/harfbuzz_ng/src \
libcore/include \
$(call include-path-for, audio-utils) \
@@ -238,7 +237,7 @@
libicuuc \
libicui18n \
libmedia \
- libjpeg \
+ libjpeg-turbo \
libusbhost \
libharfbuzz_ng \
libz \
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 3cdf640..f10f4bd 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -16,17 +16,20 @@
#define LOG_TAG "BitmapRegionDecoder"
-#include "AutoDecodeCancel.h"
#include "BitmapFactory.h"
#include "CreateJavaOutputStreamAdaptor.h"
-#include "SkBitmap.h"
-#include "SkData.h"
#include "GraphicsJNI.h"
-#include "SkImageEncoder.h"
+#include "Utils.h"
+
+#include "SkBitmap.h"
+#include "SkBitmapRegionDecoder.h"
+#include "SkCodec.h"
+#include "SkData.h"
+#include "SkEncodedFormat.h"
#include "SkUtils.h"
#include "SkPixelRef.h"
#include "SkStream.h"
-#include "Utils.h"
+
#include "android_nio_utils.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
@@ -39,60 +42,54 @@
using namespace android;
-class BitmapRegionDecoder {
-public:
- BitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
- fDecoder = decoder;
- fWidth = width;
- fHeight = height;
- }
- ~BitmapRegionDecoder() {
- delete fDecoder;
+// This is very similar to, and based on, getMimeTypeString() in BitmapFactory.
+jstring encodedFormatToString(JNIEnv* env, SkEncodedFormat format) {
+ const char* mimeType;
+ switch (format) {
+ case SkEncodedFormat::kBMP_SkEncodedFormat:
+ mimeType = "image/bmp";
+ break;
+ case SkEncodedFormat::kGIF_SkEncodedFormat:
+ mimeType = "image/gif";
+ break;
+ case SkEncodedFormat::kICO_SkEncodedFormat:
+ mimeType = "image/x-ico";
+ break;
+ case SkEncodedFormat::kJPEG_SkEncodedFormat:
+ mimeType = "image/jpeg";
+ break;
+ case SkEncodedFormat::kPNG_SkEncodedFormat:
+ mimeType = "image/png";
+ break;
+ case SkEncodedFormat::kWEBP_SkEncodedFormat:
+ mimeType = "image/webp";
+ break;
+ case SkEncodedFormat::kWBMP_SkEncodedFormat:
+ mimeType = "image/vnd.wap.wbmp";
+ break;
+ default:
+ mimeType = nullptr;
+ break;
}
- bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
- SkColorType pref, int sampleSize) {
- fDecoder->setSampleSize(sampleSize);
- return fDecoder->decodeSubset(bitmap, rect, pref);
+ jstring jstr = nullptr;
+ if (mimeType != nullptr) {
+ jstr = env->NewStringUTF(mimeType);
}
-
- SkImageDecoder* getDecoder() const { return fDecoder; }
- int getWidth() const { return fWidth; }
- int getHeight() const { return fHeight; }
-
-private:
- SkImageDecoder* fDecoder;
- int fWidth;
- int fHeight;
-};
+ return jstr;
+}
// Takes ownership of the SkStreamRewindable. For consistency, deletes stream even
// when returning null.
static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) {
- SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
- int width, height;
- if (NULL == decoder) {
- delete stream;
+ SkAutoTDelete<SkBitmapRegionDecoder> brd(
+ SkBitmapRegionDecoder::Create(stream, SkBitmapRegionDecoder::kAndroidCodec_Strategy));
+ if (NULL == brd) {
doThrowIOE(env, "Image format not supported");
- return nullObjectReturn("SkImageDecoder::Factory returned null");
+ return nullObjectReturn("CreateBitmapRegionDecoder returned null");
}
- JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env);
- decoder->setAllocator(javaAllocator);
- javaAllocator->unref();
-
- // This call passes ownership of stream to the decoder, or deletes on failure.
- if (!decoder->buildTileIndex(stream, &width, &height)) {
- char msg[100];
- snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
- decoder->getFormatName());
- doThrowIOE(env, msg);
- delete decoder;
- return nullObjectReturn("decoder->buildTileIndex returned false");
- }
-
- BitmapRegionDecoder *bm = new BitmapRegionDecoder(decoder, width, height);
- return GraphicsJNI::createBitmapRegionDecoder(env, bm);
+ return GraphicsJNI::createBitmapRegionDecoder(env, brd.detach());
}
static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
@@ -160,102 +157,106 @@
/*
* nine patch not supported
- *
* purgeable not supported
* reportSizeToVM not supported
*/
-static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle,
- jint start_x, jint start_y, jint width, jint height, jobject options) {
- BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
- jobject tileBitmap = NULL;
- SkImageDecoder *decoder = brd->getDecoder();
- int sampleSize = 1;
- SkColorType prefColorType = kUnknown_SkColorType;
- bool doDither = true;
- bool preferQualityOverSpeed = false;
- bool requireUnpremultiplied = false;
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX,
+ jint inputY, jint inputWidth, jint inputHeight, jobject options) {
+ // Set default options.
+ int sampleSize = 1;
+ SkColorType colorType = kN32_SkColorType;
+ bool requireUnpremul = false;
+ jobject javaBitmap = NULL;
+
+ // Update the default options with any options supplied by the client.
if (NULL != options) {
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
- // initialize these, in case we fail later on
+ jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+ colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
+ if (kAlpha_8_SkColorType == colorType) {
+ colorType = kGray_8_SkColorType;
+ }
+ requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
+ javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
+ // The Java options of ditherMode and preferQualityOverSpeed are deprecated. We will
+ // ignore the values of these fields.
+
+ // Initialize these fields to indicate a failure. If the decode succeeds, we
+ // will update them 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);
- prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
- doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
- preferQualityOverSpeed = env->GetBooleanField(options,
- gOptions_preferQualityOverSpeedFieldID);
- // Get the bitmap for re-use if it exists.
- tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
- requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
}
- decoder->setDitherImage(doDither);
- decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
- decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
- 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");;
+ // Recycle a bitmap if possible.
+ android::Bitmap* recycledBitmap = nullptr;
+ size_t recycledBytes = 0;
+ if (javaBitmap) {
+ recycledBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
+ if (recycledBitmap->peekAtPixelRef()->isImmutable()) {
+ ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
+ }
+ recycledBytes = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
}
- SkIRect region;
- region.fLeft = start_x;
- region.fTop = start_y;
- region.fRight = start_x + width;
- region.fBottom = start_y + height;
+ // Set up the pixel allocator
+ SkBRDAllocator* allocator = nullptr;
+ RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
+ JavaPixelAllocator javaAlloc(env);
+ if (javaBitmap) {
+ allocator = &recycleAlloc;
+ // We are required to match the color type of the recycled bitmap.
+ colorType = recycledBitmap->info().colorType();
+ } else {
+ allocator = &javaAlloc;
+ }
+
+ // Decode the region.
+ SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
+ SkBitmapRegionDecoder* brd =
+ reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
SkBitmap bitmap;
-
- if (tileBitmap != NULL) {
- // Re-use bitmap.
- GraphicsJNI::getSkBitmap(env, tileBitmap, &bitmap);
+ if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, colorType, requireUnpremul)) {
+ return nullObjectReturn("Failed to decode region.");
}
- if (!brd->decodeRegion(&bitmap, region, prefColorType, sampleSize)) {
- return nullObjectReturn("decoder->decodeRegion returned false");
- }
-
- // update options (if any)
+ // If the client provided options, indicate that the decode was successful.
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()));
+ encodedFormatToString(env, brd->getEncodedFormat()));
}
- if (tileBitmap != NULL) {
- bitmap.notifyPixelsChanged();
- return tileBitmap;
+ // If we may have reused a bitmap, we need to indicate that the pixels have changed.
+ if (javaBitmap) {
+ recycleAlloc.copyIfNecessary();
+ return javaBitmap;
}
- JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
-
int bitmapCreateFlags = 0;
- if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
- return GraphicsJNI::createBitmap(env, allocator->getStorageObjAndReset(),
- bitmapCreateFlags);
+ if (!requireUnpremul) {
+ bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
+ }
+ return GraphicsJNI::createBitmap(env, javaAlloc.getStorageObjAndReset(), bitmapCreateFlags);
}
static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
- BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
- return static_cast<jint>(brd->getHeight());
+ SkBitmapRegionDecoder* brd =
+ reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ return static_cast<jint>(brd->height());
}
static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
- BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
- return static_cast<jint>(brd->getWidth());
+ SkBitmapRegionDecoder* brd =
+ reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+ return static_cast<jint>(brd->width());
}
static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
- BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle);
+ SkBitmapRegionDecoder* brd =
+ reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
delete brd;
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index ed44019..3d5091a 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -439,7 +439,7 @@
return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID);
}
-jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap)
+jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
{
SkASSERT(bitmap != NULL);
@@ -677,6 +677,91 @@
////////////////////////////////////////////////////////////////////////////////
+RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator(
+ android::Bitmap* recycledBitmap, size_t recycledBytes)
+ : mRecycledBitmap(recycledBitmap)
+ , mRecycledBytes(recycledBytes)
+ , mSkiaBitmap(nullptr)
+ , mNeedsCopy(false)
+{}
+
+RecyclingClippingPixelAllocator::~RecyclingClippingPixelAllocator() {}
+
+bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
+ // Ensure that the caller did not pass in a NULL bitmap to the constructor or this
+ // function.
+ LOG_ALWAYS_FATAL_IF(!mRecycledBitmap);
+ LOG_ALWAYS_FATAL_IF(!bitmap);
+ mSkiaBitmap = bitmap;
+
+ // This behaves differently than the RecyclingPixelAllocator. For backwards
+ // compatibility, the original color type of the recycled bitmap must be maintained.
+ if (mRecycledBitmap->info().colorType() != bitmap->colorType()) {
+ return false;
+ }
+
+ // The Skia bitmap specifies the width and height needed by the decoder.
+ // mRecycledBitmap specifies the width and height of the bitmap that we
+ // want to reuse. Neither can be changed. We will try to find a way
+ // to reuse the memory.
+ const int maxWidth = SkTMax(bitmap->width(), mRecycledBitmap->info().width());
+ const int maxHeight = SkTMax(bitmap->height(), mRecycledBitmap->info().height());
+ const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight);
+ const size_t rowBytes = maxInfo.minRowBytes();
+ const size_t bytesNeeded = maxInfo.getSafeSize(rowBytes);
+ if (bytesNeeded <= mRecycledBytes) {
+ // Here we take advantage of reconfigure() to reset the rowBytes and ctable
+ // of mRecycledBitmap. It is very important that we pass in
+ // mRecycledBitmap->info() for the SkImageInfo. According to the
+ // specification for BitmapRegionDecoder, we are not allowed to change
+ // the SkImageInfo.
+ mRecycledBitmap->reconfigure(mRecycledBitmap->info(), rowBytes, ctable);
+
+ // This call will give the bitmap the same pixelRef as mRecycledBitmap.
+ bitmap->setPixelRef(mRecycledBitmap->refPixelRef())->unref();
+
+ // Make sure that the recycled bitmap has the correct alpha type.
+ mRecycledBitmap->setAlphaType(bitmap->alphaType());
+
+ bitmap->lockPixels();
+ mNeedsCopy = false;
+
+ // TODO: If the dimensions of the SkBitmap are smaller than those of
+ // mRecycledBitmap, should we zero the memory in mRecycledBitmap?
+ return true;
+ }
+
+ // In the event that mRecycledBitmap is not large enough, allocate new memory
+ // on the heap.
+ SkBitmap::HeapAllocator heapAllocator;
+
+ // We will need to copy from heap memory to mRecycledBitmap's memory after the
+ // decode is complete.
+ mNeedsCopy = true;
+
+ return heapAllocator.allocPixelRef(bitmap, ctable);
+}
+
+void RecyclingClippingPixelAllocator::copyIfNecessary() {
+ if (mNeedsCopy) {
+ SkPixelRef* recycledPixels = mRecycledBitmap->refPixelRef();
+ void* dst = recycledPixels->pixels();
+ size_t dstRowBytes = mRecycledBitmap->rowBytes();
+ size_t bytesToCopy = SkTMin(mRecycledBitmap->info().minRowBytes(),
+ mSkiaBitmap->info().minRowBytes());
+ for (int y = 0; y < mRecycledBitmap->info().height(); y++) {
+ memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy);
+ dst = SkTAddOffset<void>(dst, dstRowBytes);
+ }
+ recycledPixels->notifyPixelsChanged();
+ recycledPixels->unref();
+ }
+ mRecycledBitmap = nullptr;
+ mSkiaBitmap = nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
AshmemPixelAllocator::AshmemPixelAllocator(JNIEnv *env) {
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK,
"env->GetJavaVM failed");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 90f8291..e99a3ff 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -3,6 +3,8 @@
#include "Bitmap.h"
#include "SkBitmap.h"
+#include "SkBRDAllocator.h"
+#include "SkCodec.h"
#include "SkDevice.h"
#include "SkPixelRef.h"
#include "SkMallocPixelRef.h"
@@ -12,7 +14,7 @@
#include <Canvas.h>
#include <jni.h>
-class BitmapRegionDecoder;
+class SkBitmapRegionDecoder;
class SkCanvas;
namespace android {
@@ -90,7 +92,7 @@
static jobject createRegion(JNIEnv* env, SkRegion* region);
- static jobject createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap);
+ static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable);
@@ -123,7 +125,7 @@
* ensure that the allocated buffer is properly accounted for with a
* reference in the heap (or a JNI global reference).
*/
-class JavaPixelAllocator : public SkBitmap::Allocator {
+class JavaPixelAllocator : public SkBRDAllocator {
public:
JavaPixelAllocator(JNIEnv* env);
~JavaPixelAllocator();
@@ -139,11 +141,78 @@
return result;
};
+ /**
+ * Indicates that this allocator allocates zero initialized
+ * memory.
+ */
+ SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; }
+
private:
JavaVM* mJavaVM;
android::Bitmap* mStorage = nullptr;
};
+/**
+ * Allocator to handle reusing bitmaps for BitmapRegionDecoder.
+ *
+ * The BitmapRegionDecoder documentation states that, if it is
+ * provided, the recycled bitmap will always be reused, clipping
+ * the decoded output to fit in the recycled bitmap if necessary.
+ * This allocator implements that behavior.
+ *
+ * Skia's SkBitmapRegionDecoder expects the memory that
+ * is allocated to be large enough to decode the entire region
+ * that is requested. It will decode directly into the memory
+ * that is provided.
+ *
+ * FIXME: BUG:25465958
+ * If the recycled bitmap is not large enough for the decode
+ * requested, meaning that a clip is required, we will allocate
+ * enough memory for Skia to perform the decode, and then copy
+ * from the decoded output into the recycled bitmap.
+ *
+ * If the recycled bitmap is large enough for the decode requested,
+ * we will provide that memory for Skia to decode directly into.
+ *
+ * This allocator should only be used for a single allocation.
+ * After we reuse the recycledBitmap once, it is dangerous to
+ * reuse it again, given that it still may be in use from our
+ * first allocation.
+ */
+class RecyclingClippingPixelAllocator : public SkBRDAllocator {
+public:
+
+ RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap,
+ size_t recycledBytes);
+
+ ~RecyclingClippingPixelAllocator();
+
+ virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override;
+
+ /**
+ * Must be called!
+ *
+ * In the event that the recycled bitmap is not large enough for
+ * the allocation requested, we will allocate memory on the heap
+ * instead. As a final step, once we are done using this memory,
+ * we will copy the contents of the heap memory into the recycled
+ * bitmap's memory, clipping as necessary.
+ */
+ void copyIfNecessary();
+
+ /**
+ * Indicates that this allocator does not allocate zero initialized
+ * memory.
+ */
+ SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kNo_ZeroInitialized; }
+
+private:
+ android::Bitmap* mRecycledBitmap;
+ const size_t mRecycledBytes;
+ SkBitmap* mSkiaBitmap;
+ bool mNeedsCopy;
+};
+
class AshmemPixelAllocator : public SkBitmap::Allocator {
public:
AshmemPixelAllocator(JNIEnv* env);
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index c9a5f8f..e6fe447 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -116,7 +116,7 @@
final HandlerThread mThread;
final BackgroundHandler mBackgroundHandler;
- final MainHandler mMainHandler = new MainHandler();
+ final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper());
private ApplicationsState(Application app) {
mContext = app;
@@ -687,6 +687,10 @@
static final int MSG_LAUNCHER_INFO_CHANGED = 7;
static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
+ public MainHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
rebuildActiveSessions();
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 581c810..102e47a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -77,10 +77,7 @@
mDrawerLayout = null;
return;
}
- if (sDashboardCategories == null) {
- sTileCache = new HashMap<>();
- sDashboardCategories = TileUtils.getCategories(this, sTileCache);
- }
+ getDashboardCategories();
setActionBar(toolbar);
mDrawerAdapter = new SettingsDrawerAdapter(this);
ListView listView = (ListView) findViewById(R.id.left_drawer);
@@ -109,19 +106,23 @@
protected void onResume() {
super.onResume();
- final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- filter.addDataScheme("package");
- registerReceiver(mPackageReceiver, filter);
+ if (mDrawerLayout != null) {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addDataScheme("package");
+ registerReceiver(mPackageReceiver, filter);
- new CategoriesUpdater().execute();
+ new CategoriesUpdater().execute();
+ }
}
@Override
protected void onPause() {
- unregisterReceiver(mPackageReceiver);
+ if (mDrawerLayout != null) {
+ unregisterReceiver(mPackageReceiver);
+ }
super.onPause();
}
@@ -178,6 +179,10 @@
}
public List<DashboardCategory> getDashboardCategories() {
+ if (sDashboardCategories == null) {
+ sTileCache = new HashMap<>();
+ sDashboardCategories = TileUtils.getCategories(this, sTileCache);
+ }
return sDashboardCategories;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
index 55f4736..a5e1fd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
@@ -66,9 +66,9 @@
}
@Override
- public void onClick() {
+ public void onClick(IBinder token) {
try {
- mService.onClick();
+ mService.onClick(token);
} catch (Exception e) {
Log.d(TAG, "Caught exception from QSTileService", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
index d26e8d6..04006eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
@@ -24,12 +24,16 @@
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
+import android.os.Binder;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
import android.util.Log;
-
+import android.view.IWindowManager;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTileServiceWrapper;
@@ -38,19 +42,26 @@
public class CustomTile extends QSTile<QSTile.State> {
public static final String PREFIX = "custom(";
+ private static final boolean DEBUG = false;
+
// We don't want to thrash binding and unbinding if the user opens and closes the panel a lot.
// So instead we have a period of waiting.
private static final long UNBIND_DELAY = 30000;
private final ComponentName mComponent;
private final Tile mTile;
+ private final IWindowManager mWindowManager;
+ private final IBinder mToken = new Binder();
private QSTileServiceWrapper mService;
private boolean mListening;
private boolean mBound;
+ private boolean mIsTokenGranted;
+ private boolean mIsShowingDialog;
private CustomTile(QSTileHost host, String action) {
super(host);
+ mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
mTile = new Tile(mComponent, host);
try {
@@ -72,12 +83,15 @@
}
public void updateState(Tile tile) {
- Log.d("TileService", "Setting state " + tile.getLabel());
mTile.setIcon(tile.getIcon());
mTile.setLabel(tile.getLabel());
mTile.setContentDescription(tile.getContentDescription());
}
+ public void onDialogShown() {
+ mIsShowingDialog = true;
+ }
+
@Override
public void setListening(boolean listening) {
if (mListening == listening) return;
@@ -95,14 +109,30 @@
if (mService!= null) {
mService.onStopListening();
}
+ if (mIsTokenGranted && !mIsShowingDialog) {
+ try {
+ if (DEBUG) Log.d(TAG, "Removing token");
+ mWindowManager.removeWindowToken(mToken);
+ } catch (RemoteException e) {
+ }
+ mIsTokenGranted = false;
+ }
+ mIsShowingDialog = false;
mHandler.postDelayed(mUnbind, UNBIND_DELAY);
}
}
-
+
@Override
protected void handleDestroy() {
super.handleDestroy();
mHandler.removeCallbacks(mUnbind);
+ if (mIsTokenGranted) {
+ try {
+ if (DEBUG) Log.d(TAG, "Removing token");
+ mWindowManager.removeWindowToken(mToken);
+ } catch (RemoteException e) {
+ }
+ }
mUnbind.run();
}
@@ -119,7 +149,13 @@
@Override
protected void handleClick() {
if (mService != null) {
- mService.onClick();
+ try {
+ if (DEBUG) Log.d(TAG, "Adding token");
+ mWindowManager.addWindowToken(mToken, WindowManager.LayoutParams.TYPE_QS_DIALOG);
+ mIsTokenGranted = true;
+ } catch (RemoteException e) {
+ }
+ mService.onClick(mToken);
} else {
Log.e(TAG, "Click with no service " + getTileSpec());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 57c2648..f7ff8aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -293,12 +293,21 @@
verifyCaller(tile.getComponentName().getPackageName());
CustomTile customTile = getTileForComponent(tile.getComponentName());
if (customTile != null) {
- Log.d("TileService", "Got tile update for " + tile.getComponentName());
customTile.updateState(tile);
customTile.refreshState();
}
}
+ @Override
+ public void onShowDialog(Tile tile) throws RemoteException {
+ verifyCaller(tile.getComponentName().getPackageName());
+ CustomTile customTile = getTileForComponent(tile.getComponentName());
+ if (customTile != null) {
+ customTile.onDialogShown();
+ collapsePanels();
+ }
+ }
+
private void verifyCaller(String packageName) {
try {
int uid = mContext.getPackageManager().getPackageUid(packageName,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 639753a..9a7d153 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -108,9 +108,6 @@
import android.view.KeyCharacterMap.FallbackAction;
import android.view.KeyEvent;
import android.view.MotionEvent;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.policy.PhoneWindow;
import android.view.Surface;
import android.view.View;
import android.view.ViewConfiguration;
@@ -124,6 +121,8 @@
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.policy.PhoneWindow;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.widget.PointerLocationView;
@@ -1915,6 +1914,7 @@
case TYPE_PRIVATE_PRESENTATION:
case TYPE_VOICE_INTERACTION:
case TYPE_ACCESSIBILITY_OVERLAY:
+ case TYPE_QS_DIALOG:
// The window manager will check these.
break;
case TYPE_PHONE:
@@ -2110,6 +2110,8 @@
return 2;
case TYPE_DOCK_DIVIDER:
return 2;
+ case TYPE_QS_DIALOG:
+ return 2;
case TYPE_PHONE:
return 3;
case TYPE_SEARCH_BAR:
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index c246609..a4b4276 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -351,6 +351,7 @@
case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_QS_DIALOG:
case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Rect magnifiedRegionBounds = mTempRect2;
mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 707450c..f153873 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -43,6 +43,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -1865,6 +1866,11 @@
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
+ if (type == TYPE_QS_DIALOG) {
+ Slog.w(TAG, "Attempted to add QS dialog window with unknown token "
+ + attrs.token + ". Aborting.");
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+ }
if (type == TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG, "Attempted to add Accessibility overlay window with unknown token "
+ attrs.token + ". Aborting.");