Merge "Fix screen pinning in seascape" into oc-dev
diff --git a/api/current.txt b/api/current.txt
index cfcdb7d..f47a478 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12821,7 +12821,6 @@
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent);
- method public static android.graphics.ColorSpace.Renderer createRenderer();
method public float[] fromXyz(float, float, float);
method public abstract float[] fromXyz(float[]);
method public static android.graphics.ColorSpace get(android.graphics.ColorSpace.Named);
@@ -12905,16 +12904,6 @@
enum_constant public static final android.graphics.ColorSpace.RenderIntent SATURATION;
}
- public static class ColorSpace.Renderer {
- method public android.graphics.ColorSpace.Renderer add(android.graphics.ColorSpace, int);
- method public android.graphics.ColorSpace.Renderer add(android.graphics.ColorSpace, float, float, float, int);
- method public android.graphics.ColorSpace.Renderer clip(boolean);
- method public android.graphics.Bitmap render();
- method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean);
- method public android.graphics.ColorSpace.Renderer size(int);
- method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean);
- }
-
public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
ctor public ColorSpace.Rgb(java.lang.String, float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator);
ctor public ColorSpace.Rgb(java.lang.String, float[], float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator, float, float);
diff --git a/api/system-current.txt b/api/system-current.txt
index df8d49a..1a40d6a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -13584,7 +13584,6 @@
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent);
- method public static android.graphics.ColorSpace.Renderer createRenderer();
method public float[] fromXyz(float, float, float);
method public abstract float[] fromXyz(float[]);
method public static android.graphics.ColorSpace get(android.graphics.ColorSpace.Named);
@@ -13668,16 +13667,6 @@
enum_constant public static final android.graphics.ColorSpace.RenderIntent SATURATION;
}
- public static class ColorSpace.Renderer {
- method public android.graphics.ColorSpace.Renderer add(android.graphics.ColorSpace, int);
- method public android.graphics.ColorSpace.Renderer add(android.graphics.ColorSpace, float, float, float, int);
- method public android.graphics.ColorSpace.Renderer clip(boolean);
- method public android.graphics.Bitmap render();
- method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean);
- method public android.graphics.ColorSpace.Renderer size(int);
- method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean);
- }
-
public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
ctor public ColorSpace.Rgb(java.lang.String, float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator);
ctor public ColorSpace.Rgb(java.lang.String, float[], float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator, float, float);
diff --git a/api/test-current.txt b/api/test-current.txt
index a8060d8..42e3bd6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -12871,7 +12871,6 @@
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent);
- method public static android.graphics.ColorSpace.Renderer createRenderer();
method public float[] fromXyz(float, float, float);
method public abstract float[] fromXyz(float[]);
method public static android.graphics.ColorSpace get(android.graphics.ColorSpace.Named);
@@ -12955,16 +12954,6 @@
enum_constant public static final android.graphics.ColorSpace.RenderIntent SATURATION;
}
- public static class ColorSpace.Renderer {
- method public android.graphics.ColorSpace.Renderer add(android.graphics.ColorSpace, int);
- method public android.graphics.ColorSpace.Renderer add(android.graphics.ColorSpace, float, float, float, int);
- method public android.graphics.ColorSpace.Renderer clip(boolean);
- method public android.graphics.Bitmap render();
- method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean);
- method public android.graphics.ColorSpace.Renderer size(int);
- method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean);
- }
-
public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
ctor public ColorSpace.Rgb(java.lang.String, float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator);
ctor public ColorSpace.Rgb(java.lang.String, float[], float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator, float, float);
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 75de4da..0821674 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3628,7 +3628,7 @@
<string name="data_usage_3g_limit_title">2G-3G data limit reached</string>
<!-- Notification title when 4G data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
<string name="data_usage_4g_limit_title">4G data limit reached</string>
- <!-- Notification title when mobile data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
+ <!-- Notification title when mobile data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=50] -->
<string name="data_usage_mobile_limit_title">Mobile data limit reached</string>
<!-- Notification title when Wi-Fi data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
<string name="data_usage_wifi_limit_title">Wi-Fi data limit reached</string>
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index e61d467..4fc63ea 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -123,39 +123,11 @@
* and {@link #connect(ColorSpace, ColorSpace)}, are also guaranteed to be
* thread-safe.</p>
*
- * <h3>Visualization and debugging</h3>
- *
- * <p>To visualize and debug color spaces, you can call {@link #createRenderer()}.
- * The {@link Renderer} created by calling this method can be used to compare
- * color spaces and locate specific colors on a CIE 1931 or CIE 1976 UCS
- * chromaticity diagram.</p>
- *
- * <p>The following code snippet shows how to render a bitmap that compares
- * the color gamuts and white points of {@link Named#DCI_P3} and
- * {@link Named#PRO_PHOTO_RGB}:</p>
- *
- * <pre class="prettyprint">
- * Bitmap bitmap = ColorSpace.createRenderer()
- * .size(768)
- * .clip(true)
- * .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
- * .add(ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB), 0xff097ae9)
- * .render();
- * </pre>
- * <p>
- * <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_renderer.png" />
- * <figcaption style="text-align: center;">DCI-P3 vs ProPhoto RGB</figcaption>
- * </p>
- *
- * <p>Please refer to the documentation of the {@link Renderer} class for more
- * information about its options and capabilities.</p>
- *
* @see #get(Named)
* @see Named
* @see Model
* @see Connector
* @see Adaptation
- * @see Renderer
*/
@AnyThread
@SuppressWarnings("StaticInitializerReferencesSubClass")
@@ -1417,6 +1389,8 @@
* @return A new non-null {@link Renderer} instance
*
* @see Renderer
+ *
+ * @hide
*/
@NonNull
public static Renderer createRenderer() {
@@ -3712,6 +3686,8 @@
* See {@link #add(ColorSpace, float, float, float, int)} for more information.</p>
*
* @see ColorSpace#createRenderer()
+ *
+ * @hide
*/
public static class Renderer {
private static final int NATIVE_SIZE = 1440;
@@ -4054,7 +4030,7 @@
*/
@NonNull
@Size(6)
- private static float[] getPrimaries(@NonNull Rgb rgb,
+ private static void getPrimaries(@NonNull Rgb rgb,
@NonNull @Size(6) float[] primaries, boolean asUcs) {
// TODO: We should find a better way to handle these cases
if (rgb.equals(ColorSpace.get(Named.EXTENDED_SRGB)) ||
@@ -4069,7 +4045,6 @@
rgb.getPrimaries(primaries);
}
if (asUcs) xyYToUv(primaries);
- return primaries;
}
/**
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index ed71849..06555c1 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -17,6 +17,8 @@
package android.media;
import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -72,6 +74,12 @@
private static final int ACQUIRE_MAX_IMAGES = 2;
/**
+ * Invalid consumer buffer usage flag. This usage flag will be ignored
+ * by the {@code ImageReader} instance is constructed with this value.
+ */
+ private static final long BUFFER_USAGE_UNKNOWN = 0;
+
+ /**
* <p>
* Create a new reader for images of the desired size and format.
* </p>
@@ -121,13 +129,104 @@
* @see Image
*/
public static ImageReader newInstance(int width, int height, int format, int maxImages) {
- return new ImageReader(width, height, format, maxImages);
+ return new ImageReader(width, height, format, maxImages, BUFFER_USAGE_UNKNOWN);
+ }
+
+ /**
+ * <p>
+ * Create a new reader for images of the desired size, format and consumer usage flag.
+ * </p>
+ * <p>
+ * The {@code maxImages} parameter determines the maximum number of {@link Image} objects that
+ * can be be acquired from the {@code ImageReader} simultaneously. Requesting more buffers will
+ * use up more memory, so it is important to use only the minimum number necessary for the use
+ * case.
+ * </p>
+ * <p>
+ * The valid sizes and formats depend on the source of the image data.
+ * </p>
+ * <p>
+ * The format and usage flag combination describes how the buffer will be used by
+ * consumer end-points. For example, if the application intends to send the images to
+ * {@link android.media.MediaCodec} or {@link android.media.MediaRecorder} for hardware video
+ * encoding, the format and usage flag combination needs to be
+ * {@link ImageFormat#PRIVATE PRIVATE} and {@link HardwareBuffer#USAGE0_VIDEO_ENCODE}. When an
+ * {@link ImageReader} object is created with a valid size and such format/usage flag
+ * combination, the application can send the {@link Image images} to an {@link ImageWriter} that
+ * is created with the input {@link android.view.Surface} provided by the
+ * {@link android.media.MediaCodec} or {@link android.media.MediaRecorder}.
+ * </p>
+ * <p>
+ * If the {@code format} is {@link ImageFormat#PRIVATE PRIVATE}, the created {@link ImageReader}
+ * will produce images that are not directly accessible by the application. The application can
+ * still acquire images from this {@link ImageReader}, and send them to the
+ * {@link android.hardware.camera2.CameraDevice camera} for reprocessing, or to the
+ * {@link android.media.MediaCodec} / {@link android.media.MediaRecorder} for hardware video
+ * encoding via {@link ImageWriter} interface. However, the {@link Image#getPlanes()
+ * getPlanes()} will return an empty array for {@link ImageFormat#PRIVATE PRIVATE} format
+ * images. The application can check if an existing reader's format by calling
+ * {@link #getImageFormat()}.
+ * </p>
+ * <p>
+ * {@link ImageFormat#PRIVATE PRIVATE} format {@link ImageReader ImageReaders} are more
+ * efficient to use when application access to image data is not necessary, compared to
+ * ImageReaders using other format such as {@link ImageFormat#YUV_420_888 YUV_420_888}.
+ * </p>
+ * <p>
+ * Note that not all format and usage flag combination is supported by the
+ * {@link ImageReader}. Below are the supported combinations by the {@link ImageReader}
+ * (assuming the consumer end-points support the such image consumption, e.g., hardware video
+ * encoding).
+ * <table>
+ * <tr>
+ * <th>Format</th>
+ * <th>Compatible usage flags</th>
+ * </tr>
+ * <tr>
+ * <td>non-{@link android.graphics.ImageFormat#PRIVATE PRIVATE} formats defined by
+ * {@link android.graphics.ImageFormat ImageFormat} or
+ * {@link android.graphics.PixelFormat PixelFormat}</td>
+ * <td>{@link HardwareBuffer#USAGE0_CPU_READ} or
+ * {@link HardwareBuffer#USAGE0_CPU_READ_OFTEN}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#PRIVATE}</td>
+ * <td>{@link HardwareBuffer#USAGE0_VIDEO_ENCODE} or
+ * {@link HardwareBuffer#USAGE0_GPU_SAMPLED_IMAGE}, or combined</td>
+ * </tr>
+ * </table>
+ * Using other combinations may result in {@link IllegalArgumentException}.
+ * </p>
+ * @param width The default width in pixels of the Images that this reader will produce.
+ * @param height The default height in pixels of the Images that this reader will produce.
+ * @param format The format of the Image that this reader will produce. This must be one of the
+ * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+ * constants. Note that not all formats are supported, like ImageFormat.NV21.
+ * @param maxImages The maximum number of images the user will want to access simultaneously.
+ * This should be as small as possible to limit memory use. Once maxImages Images are
+ * obtained by the user, one of them has to be released before a new Image will
+ * become available for access through {@link #acquireLatestImage()} or
+ * {@link #acquireNextImage()}. Must be greater than 0.
+ * @param usage The intended usage of the images produced by this ImageReader. It needs
+ * to be one of the Usage0 defined by {@link HardwareBuffer}, or an
+ * {@link IllegalArgumentException} will be thrown.
+ * @see Image
+ * @see HardwareBuffer
+ * @hide
+ */
+ public static ImageReader newInstance(int width, int height, int format, int maxImages,
+ long usage) {
+ if (!isFormatUsageCombinationAllowed(format, usage)) {
+ throw new IllegalArgumentException("Format usage combination is not supported:"
+ + " format = " + format + ", usage = " + usage);
+ }
+ return new ImageReader(width, height, format, maxImages, usage);
}
/**
* @hide
*/
- protected ImageReader(int width, int height, int format, int maxImages) {
+ protected ImageReader(int width, int height, int format, int maxImages, long usage) {
mWidth = width;
mHeight = height;
mFormat = format;
@@ -149,7 +248,7 @@
mNumPlanes = ImageUtils.getNumPlanesForFormat(mFormat);
- nativeInit(new WeakReference<ImageReader>(this), width, height, format, maxImages);
+ nativeInit(new WeakReference<>(this), width, height, format, maxImages, usage);
mSurface = nativeGetSurface();
@@ -617,6 +716,30 @@
return si.getReader() == this;
}
+ private static boolean isFormatUsageCombinationAllowed(int format, long usage) {
+ if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
+ return false;
+ }
+
+ // Valid usage needs to be provided.
+ if (usage == BUFFER_USAGE_UNKNOWN) {
+ return false;
+ }
+
+ if (format == ImageFormat.PRIVATE) {
+ // Usage need to be either USAGE0_GPU_SAMPLED_IMAGE or USAGE0_VIDEO_ENCODE or combined.
+ boolean isAllowed = (usage == HardwareBuffer.USAGE0_GPU_SAMPLED_IMAGE);
+ isAllowed = isAllowed || (usage == HardwareBuffer.USAGE0_VIDEO_ENCODE);
+ isAllowed = isAllowed || (usage ==
+ (HardwareBuffer.USAGE0_VIDEO_ENCODE | HardwareBuffer.USAGE0_GPU_SAMPLED_IMAGE));
+ return isAllowed;
+ } else {
+ // Usage need to make the buffer CPU readable for explicit format.
+ return ((usage == HardwareBuffer.USAGE0_CPU_READ) ||
+ (usage == HardwareBuffer.USAGE0_CPU_READ_OFTEN));
+ }
+ }
+
/**
* Called from Native code when an Event happens.
*
@@ -655,7 +778,7 @@
private ListenerHandler mListenerHandler;
// Keep track of the successfully acquired Images. This need to be thread safe as the images
// could be closed by different threads (e.g., application thread and GC thread).
- private List<Image> mAcquiredImages = new CopyOnWriteArrayList<Image>();
+ private List<Image> mAcquiredImages = new CopyOnWriteArrayList<>();
/**
* This field is used by native code, do not access or modify.
@@ -896,7 +1019,7 @@
}
private synchronized native void nativeInit(Object weakSelf, int w, int h,
- int fmt, int maxImgs);
+ int fmt, int maxImgs, long consumerUsage);
private synchronized native void nativeClose();
private synchronized native void nativeReleaseImage(Image i);
private synchronized native Surface nativeGetSurface();
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index b142ddd..349c9cb 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -17,6 +17,7 @@
package android.media;
import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.camera2.utils.SurfaceUtils;
import android.os.Handler;
@@ -89,7 +90,7 @@
private final int mMaxImages;
// Keep track of the currently dequeued Image. This need to be thread safe as the images
// could be closed by different threads (e.g., application thread and GC thread).
- private List<Image> mDequeuedImages = new CopyOnWriteArrayList<Image>();
+ private List<Image> mDequeuedImages = new CopyOnWriteArrayList<>();
private int mEstimatedNativeAllocBytes;
/**
@@ -118,22 +119,75 @@
* @return a new ImageWriter instance.
*/
public static ImageWriter newInstance(Surface surface, int maxImages) {
- return new ImageWriter(surface, maxImages);
+ return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN);
+ }
+
+ /**
+ * <p>
+ * Create a new ImageWriter with given number of max Images and format.
+ * </p>
+ * <p>
+ * The {@code maxImages} parameter determines the maximum number of
+ * {@link Image} objects that can be be dequeued from the
+ * {@code ImageWriter} simultaneously. Requesting more buffers will use up
+ * more memory, so it is important to use only the minimum number necessary.
+ * </p>
+ * <p>
+ * The format specifies the image format of this ImageWriter. The format
+ * from the {@code surface} will be overridden with this format. For example,
+ * if the surface is obtained from a {@link android.graphics.SurfaceTexture}, the default
+ * format may be {@link PixelFormat#RGBA_8888}. If the application creates an ImageWriter
+ * with this surface and {@link ImageFormat#PRIVATE}, this ImageWriter will be able to operate
+ * with {@link ImageFormat#PRIVATE} Images.
+ * </p>
+ * <p>
+ * Note that the consumer end-point may or may not be able to support Images with different
+ * format, for such case, the application should only use this method if the consumer is able
+ * to consume such images.
+ * </p>
+ * <p>
+ * The input Image size depends on the Surface that is provided by
+ * the downstream consumer end-point.
+ * </p>
+ *
+ * @param surface The destination Surface this writer produces Image data
+ * into.
+ * @param maxImages The maximum number of Images the user will want to
+ * access simultaneously for producing Image data. This should be
+ * as small as possible to limit memory use. Once maxImages
+ * Images are dequeued by the user, one of them has to be queued
+ * back before a new Image can be dequeued for access via
+ * {@link #dequeueInputImage()}.
+ * @param format The format of this ImageWriter. It can be any valid format specified by
+ * {@link ImageFormat} or {@link PixelFormat}.
+ *
+ * @return a new ImageWriter instance.
+ * @hide
+ */
+ public static ImageWriter newInstance(Surface surface, int maxImages, int format) {
+ if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
+ throw new IllegalArgumentException("Invalid format is specified: " + format);
+ }
+ return new ImageWriter(surface, maxImages, format);
}
/**
* @hide
*/
- protected ImageWriter(Surface surface, int maxImages) {
+ protected ImageWriter(Surface surface, int maxImages, int format) {
if (surface == null || maxImages < 1) {
throw new IllegalArgumentException("Illegal input argument: surface " + surface
+ ", maxImages: " + maxImages);
}
mMaxImages = maxImages;
+
+ if (format == ImageFormat.UNKNOWN) {
+ format = SurfaceUtils.getSurfaceFormat(surface);
+ }
// Note that the underlying BufferQueue is working in synchronous mode
// to avoid dropping any buffers.
- mNativeContext = nativeInit(new WeakReference<ImageWriter>(this), surface, maxImages);
+ mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format);
// Estimate the native buffer allocation size and register it so it gets accounted for
// during GC. Note that this doesn't include the buffers required by the buffer queue
@@ -142,7 +196,6 @@
// complex, and 1 buffer is enough for the VM to treat the ImageWriter as being of some
// size.
Size surfSize = SurfaceUtils.getSurfaceSize(surface);
- int format = SurfaceUtils.getSurfaceFormat(surface);
mEstimatedNativeAllocBytes =
ImageUtils.getEstimatedNativeAllocBytes(surfSize.getWidth(),surfSize.getHeight(),
format, /*buffer count*/ 1);
@@ -809,7 +862,8 @@
}
// Native implemented ImageWriter methods.
- private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs);
+ private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs,
+ int format);
private synchronized native void nativeClose(long nativeCtx);
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 431d5d8..c2ed8cf 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -51,6 +51,7 @@
libandroidfw
LOCAL_STATIC_LIBRARIES := \
+ libgrallocusage \
LOCAL_C_INCLUDES += \
external/libexif/ \
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index f5e19f9..163c4b0 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -31,6 +31,8 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <grallocusage/GrallocUsageConversion.h>
#include <jni.h>
#include <JNIHelp.h>
@@ -42,6 +44,7 @@
#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mNativeBuffer"
#define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID "mTimestamp"
+#define CONSUMER_BUFFER_USAGE_UNKNOWN 0;
// ----------------------------------------------------------------------------
using namespace android;
@@ -327,8 +330,8 @@
"Can not find SurfacePlane constructor");
}
-static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz,
- jint width, jint height, jint format, jint maxImages)
+static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint width, jint height,
+ jint format, jint maxImages, jlong ndkUsage)
{
status_t res;
int nativeFormat;
@@ -358,17 +361,29 @@
width, height, format, maxImages, getpid(),
createProcessUniqueId());
uint32_t consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN;
+ bool needUsageOverride = ndkUsage != CONSUMER_BUFFER_USAGE_UNKNOWN;
+ uint64_t outProducerUsage = 0;
+ uint64_t outConsumerUsage = 0;
+ android_hardware_HardwareBuffer_convertToGrallocUsageBits(&outProducerUsage, &outConsumerUsage,
+ ndkUsage, 0);
if (isFormatOpaque(nativeFormat)) {
// Use the SW_READ_NEVER usage to tell producer that this format is not for preview or video
// encoding. The only possibility will be ZSL output.
consumerUsage = GRALLOC_USAGE_SW_READ_NEVER;
+ if (needUsageOverride) {
+ consumerUsage = android_convertGralloc1To0Usage(0, outConsumerUsage);
+ }
+ } else if (needUsageOverride) {
+ ALOGW("Consumer usage override for non-opaque format is not implemented yet, "
+ "ignore the provided usage from the application");
}
bufferConsumer = new BufferItemConsumer(gbConsumer, consumerUsage, maxImages,
/*controlledByApp*/true);
if (bufferConsumer == nullptr) {
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
- "Failed to allocate native buffer consumer for format 0x%x", nativeFormat);
+ "Failed to allocate native buffer consumer for format 0x%x and usage 0x%x",
+ nativeFormat, consumerUsage);
return;
}
ctx->setBufferConsumer(bufferConsumer);
@@ -788,7 +803,7 @@
static const JNINativeMethod gImageReaderMethods[] = {
{"nativeClassInit", "()V", (void*)ImageReader_classInit },
- {"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init },
+ {"nativeInit", "(Ljava/lang/Object;IIIIJ)V", (void*)ImageReader_init },
{"nativeClose", "()V", (void*)ImageReader_close },
{"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
{"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup },
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 0149d76..ed5fbcf 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -33,7 +33,7 @@
#include <inttypes.h>
#define IMAGE_BUFFER_JNI_ID "mNativeBuffer"
-
+#define IMAGE_FORMAT_UNKNOWN 0 // This is the same value as ImageFormat#UNKNOWN.
// ----------------------------------------------------------------------------
using namespace android;
@@ -222,7 +222,7 @@
}
static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface,
- jint maxImages) {
+ jint maxImages, jint userFormat) {
status_t res;
ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages);
@@ -255,7 +255,7 @@
// Get the dimension and format of the producer.
sp<ANativeWindow> anw = producer;
- int32_t width, height, format;
+ int32_t width, height, surfaceFormat;
if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) {
ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res);
jniThrowRuntimeException(env, "Failed to query Surface width");
@@ -270,21 +270,27 @@
}
ctx->setBufferHeight(height);
- if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &format)) != OK) {
- ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res);
- jniThrowRuntimeException(env, "Failed to query Surface format");
- return 0;
+ // Query surface format if no valid user format is specified, otherwise, override surface format
+ // with user format.
+ if (userFormat == IMAGE_FORMAT_UNKNOWN) {
+ if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &surfaceFormat)) != OK) {
+ ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+ jniThrowRuntimeException(env, "Failed to query Surface format");
+ return 0;
+ }
+ } else {
+ surfaceFormat = userFormat;
}
- ctx->setBufferFormat(format);
- env->SetIntField(thiz, gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(format));
+ ctx->setBufferFormat(surfaceFormat);
+ env->SetIntField(thiz,
+ gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(surfaceFormat));
-
- if (!isFormatOpaque(format)) {
+ if (!isFormatOpaque(surfaceFormat)) {
res = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
if (res != OK) {
ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
__FUNCTION__, static_cast<unsigned int>(GRALLOC_USAGE_SW_WRITE_OFTEN),
- format, strerror(-res), res);
+ surfaceFormat, strerror(-res), res);
jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage");
return 0;
}
@@ -784,7 +790,7 @@
static JNINativeMethod gImageWriterMethods[] = {
{"nativeClassInit", "()V", (void*)ImageWriter_classInit },
- {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;I)J",
+ {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;II)J",
(void*)ImageWriter_init },
{"nativeClose", "(J)V", (void*)ImageWriter_close },
{"nativeAttachAndQueueImage", "(JJIJIIII)I", (void*)ImageWriter_attachAndQueueImage },
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ff310cc..34f2822 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -423,7 +423,7 @@
<string name="wifi_display_certification">Wireless display certification</string>
<!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] -->
<string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string>
- <!-- Setting Checkbox title whether to enable WiFi Aggressive Handover. [CHAR LIMIT=40] -->
+ <!-- Setting Checkbox title whether to enable WiFi Aggressive Handover (more actively switch from wifi to mobile data). [CHAR LIMIT=40] -->
<string name="wifi_aggressive_handover">Aggressive Wi\u2011Fi to mobile handover</string>
<!-- Setting Checkbox title whether to enable WiFi Scanning in the presence of traffic. [CHAR LIMIT=80] -->
<string name="wifi_allow_scan_with_traffic">Always allow Wi\u2011Fi Roam Scans</string>
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 64ee1e9..c1ffce3 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1421,6 +1421,11 @@
return INotificationManager.Stub.asInterface(mService);
}
+ @VisibleForTesting
+ NotificationManagerInternal getInternalService() {
+ return mInternalService;
+ }
+
private final IBinder mService = new INotificationManager.Stub() {
// Toasts
// ============================================================================
@@ -2984,25 +2989,27 @@
int userId) {
checkCallerIsSystem();
synchronized (mNotificationLock) {
- NotificationRecord r = findNotificationByListLocked(mNotificationList, pkg, null,
- notificationId, userId);
- if (r == null) {
- Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
- + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
- return;
- }
- StatusBarNotification sbn = r.sbn;
- // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
- // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
- // we have to revert to the flags we received initially *and* force remove
- // FLAG_FOREGROUND_SERVICE.
- sbn.getNotification().flags =
- (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
- mRankingHelper.sort(mNotificationList);
- mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
mHandler.post(new Runnable() {
@Override
public void run() {
+ NotificationRecord r =
+ findNotificationLocked(pkg, null, notificationId, userId);
+ if (r == null) {
+ Log.d(TAG,
+ "stripForegroundServiceFlag: Could not find notification with "
+ + "pkg=" + pkg + " / id=" + notificationId
+ + " / userId=" + userId);
+ return;
+ }
+ StatusBarNotification sbn = r.sbn;
+ // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
+ // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
+ // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
+ // initially *and* force remove FLAG_FOREGROUND_SERVICE.
+ sbn.getNotification().flags =
+ (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
+ mRankingHelper.sort(mNotificationList);
+ mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
mGroupHelper.onNotificationPosted(sbn);
}
});
@@ -4425,16 +4432,16 @@
}
}
- protected static boolean isUidSystem(int uid) {
+ protected boolean isUidSystem(int uid) {
final int appid = UserHandle.getAppId(uid);
return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
}
- private static boolean isCallerSystem() {
+ private boolean isCallerSystem() {
return isUidSystem(Binder.getCallingUid());
}
- private static void checkCallerIsSystem() {
+ protected void checkCallerIsSystem() {
if (isCallerSystem()) {
return;
}
diff --git a/services/tests/notification/Android.mk b/services/tests/notification/Android.mk
index a5d5570..940db79 100644
--- a/services/tests/notification/Android.mk
+++ b/services/tests/notification/Android.mk
@@ -23,7 +23,8 @@
guava \
android-support-test \
mockito-target-minus-junit4 \
- platform-test-annotations
+ platform-test-annotations \
+ testables
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4c23d79..d9214fa 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -47,6 +47,7 @@
import android.service.notification.StatusBarNotification;
import android.support.test.annotation.UiThreadTest;
import android.support.test.InstrumentationRegistry;
+import android.testing.TestableLooper;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
@@ -64,20 +65,29 @@
private final int uid = Binder.getCallingUid();
private NotificationManagerService mNotificationManagerService;
private INotificationManager mBinderService;
+ private NotificationManagerInternal mInternalService;
private IPackageManager mPackageManager = mock(IPackageManager.class);
private final PackageManager mPackageManagerClient = mock(PackageManager.class);
private Context mContext = InstrumentationRegistry.getTargetContext();
private final String PKG = mContext.getPackageName();
- private HandlerThread mThread;
+ private TestableLooper mTestableLooper;
private final RankingHelper mRankingHelper = mock(RankingHelper.class);
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ // Use a Testable subclass so we can simulate calls from the system without failing.
+ private static class TestableNotificationManagerService extends NotificationManagerService {
+ public TestableNotificationManagerService(Context context) { super(context); }
+
+ @Override
+ protected void checkCallerIsSystem() {}
+ }
+
@Before
@Test
@UiThreadTest
public void setUp() throws Exception {
- mNotificationManagerService = new NotificationManagerService(mContext);
+ mNotificationManagerService = new TestableNotificationManagerService(mContext);
// MockPackageManager - default returns ApplicationInfo with matching calling UID
final ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -88,9 +98,8 @@
.thenReturn(applicationInfo);
final LightsManager mockLightsManager = mock(LightsManager.class);
when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
- // Use a separate thread for service looper.
- mThread = new HandlerThread("TestThread");
- mThread.start();
+ // Use this testable looper.
+ mTestableLooper = new TestableLooper(false);
// Mock NotificationListeners to bypass security checks.
final NotificationManagerService.NotificationListeners mockNotificationListeners =
mock(NotificationManagerService.NotificationListeners.class);
@@ -98,32 +107,19 @@
mockNotificationListeners.new ManagedServiceInfo(null,
new ComponentName(PKG, "test_class"), uid, true, null, 0));
- mNotificationManagerService.init(mThread.getLooper(), mPackageManager,
+ mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager,
mPackageManagerClient, mockLightsManager, mockNotificationListeners);
// Tests call directly into the Binder.
mBinderService = mNotificationManagerService.getBinderService();
+ mInternalService = mNotificationManagerService.getInternalService();
mBinderService.createNotificationChannels(
PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
}
public void waitForIdle() throws Exception {
- MessageQueue queue = mThread.getLooper().getQueue();
- if (queue.isIdle()) {
- return;
- }
- CountDownLatch latch = new CountDownLatch(1);
- queue.addIdleHandler(new MessageQueue.IdleHandler() {
- @Override public boolean queueIdle() {
- latch.countDown();
- return false;
- }
- });
- // Timeout is valid in the cases where the queue goes idle before the IdleHandler
- // is added.
- latch.await(WAIT_FOR_IDLE_TIMEOUT, TimeUnit.SECONDS);
- waitForIdle();
+ mTestableLooper.processAllMessages();
}
private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
@@ -260,7 +256,6 @@
@Test
@UiThreadTest
- @Ignore("Flaky")
public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null).getNotification(), new int[1], 0);
@@ -380,6 +375,21 @@
@Test
@UiThreadTest
+ public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+ sbn.getId(), sbn.getNotification(), new int[1], sbn.getUserId());
+ mInternalService.removeForegroundServiceFlagFromNotification(PKG, sbn.getId(),
+ sbn.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(sbn.getPackageName());
+ assertEquals(0, notifs[0].getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE);
+ }
+
+ @Test
+ @UiThreadTest
public void testTvExtenderChannelOverride_onTv() throws Exception {
mNotificationManagerService.setIsTelevision(true);
mNotificationManagerService.setRankingHelper(mRankingHelper);