Merge "Only enable fingerprint auth after first regular auth" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index e3bb0b8..ff66cf6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9828,6 +9828,7 @@
public final class Resources.Theme {
method public void applyStyle(int, boolean);
method public void dump(int, java.lang.String, java.lang.String);
+ method public int getChangingConfigurations();
method public android.graphics.drawable.Drawable getDrawable(int) throws android.content.res.Resources.NotFoundException;
method public android.content.res.Resources getResources();
method public android.content.res.TypedArray obtainStyledAttributes(int[]);
@@ -15607,6 +15608,16 @@
field public static final int HEVCMainTierLevel62 = 16777216; // 0x1000000
field public static final int HEVCProfileMain = 1; // 0x1
field public static final int HEVCProfileMain10 = 2; // 0x2
+ field public static final int MPEG2LevelH14 = 2; // 0x2
+ field public static final int MPEG2LevelHL = 3; // 0x3
+ field public static final int MPEG2LevelLL = 0; // 0x0
+ field public static final int MPEG2LevelML = 1; // 0x1
+ field public static final int MPEG2Profile422 = 2; // 0x2
+ field public static final int MPEG2ProfileHigh = 5; // 0x5
+ field public static final int MPEG2ProfileMain = 1; // 0x1
+ field public static final int MPEG2ProfileSNR = 3; // 0x3
+ field public static final int MPEG2ProfileSimple = 0; // 0x0
+ field public static final int MPEG2ProfileSpatial = 4; // 0x4
field public static final int MPEG4Level0 = 1; // 0x1
field public static final int MPEG4Level0b = 2; // 0x2
field public static final int MPEG4Level1 = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index 535bfc7..ffcb86f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10122,6 +10122,7 @@
public final class Resources.Theme {
method public void applyStyle(int, boolean);
method public void dump(int, java.lang.String, java.lang.String);
+ method public int getChangingConfigurations();
method public android.graphics.drawable.Drawable getDrawable(int) throws android.content.res.Resources.NotFoundException;
method public android.content.res.Resources getResources();
method public android.content.res.TypedArray obtainStyledAttributes(int[]);
@@ -16829,6 +16830,16 @@
field public static final int HEVCMainTierLevel62 = 16777216; // 0x1000000
field public static final int HEVCProfileMain = 1; // 0x1
field public static final int HEVCProfileMain10 = 2; // 0x2
+ field public static final int MPEG2LevelH14 = 2; // 0x2
+ field public static final int MPEG2LevelHL = 3; // 0x3
+ field public static final int MPEG2LevelLL = 0; // 0x0
+ field public static final int MPEG2LevelML = 1; // 0x1
+ field public static final int MPEG2Profile422 = 2; // 0x2
+ field public static final int MPEG2ProfileHigh = 5; // 0x5
+ field public static final int MPEG2ProfileMain = 1; // 0x1
+ field public static final int MPEG2ProfileSNR = 3; // 0x3
+ field public static final int MPEG2ProfileSimple = 0; // 0x0
+ field public static final int MPEG2ProfileSpatial = 4; // 0x4
field public static final int MPEG4Level0 = 1; // 0x1
field public static final int MPEG4Level0b = 2; // 0x2
field public static final int MPEG4Level1 = 4; // 0x4
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index aea413d..470804d 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -231,7 +231,7 @@
* device owner app.
*
* <p>The broadcast will be limited to the {@link DeviceAdminReceiver} component specified in
- * the (@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME) field
+ * the {@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME} field
* of the original intent or NFC bump that started the provisioning process. You will generally
* handle this in {@link DeviceAdminReceiver#onReadyForUserInitialization}.
*
@@ -450,9 +450,9 @@
*
* <p>It is not assumed that the device initializer is finished when it returns from
* this call, as it may do additional setup asynchronously. The device initializer must call
- * {DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any additional
- * setup (such as adding an account by using the {@link AccountManager}) in order for the user
- * to be functional.
+ * {@link DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any
+ * additional setup (such as adding an account by using the {@link AccountManager}) in order for
+ * the user to be functional.
*
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9f71ea5..4b72dc3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -412,7 +412,7 @@
= "android.app.action.MANAGED_PROFILE_PROVISIONED";
/**
- * A boolean extra indicating whether device encryption is required as part of Device Owner
+ * A boolean extra indicating whether device encryption can be skipped as part of Device Owner
* provisioning.
*
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 16f6b1e..43cc63b 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -570,13 +570,16 @@
Configuration.NATIVE_CONFIG_DENSITY, // DENSITY
Configuration.NATIVE_CONFIG_LAYOUTDIR, // LAYOUT DIRECTION
};
- /** @hide
+
+ /**
* Convert Java change bits to native.
+ *
+ * @hide
*/
public static int activityInfoConfigToNative(int input) {
int output = 0;
- for (int i=0; i<CONFIG_NATIVE_BITS.length; i++) {
- if ((input&(1<<i)) != 0) {
+ for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
+ if ((input & (1 << i)) != 0) {
output |= CONFIG_NATIVE_BITS[i];
}
}
@@ -584,6 +587,21 @@
}
/**
+ * Convert native change bits to Java.
+ *
+ * @hide
+ */
+ public static int activityInfoConfigNativeToJava(int input) {
+ int output = 0;
+ for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
+ if ((input & CONFIG_NATIVE_BITS[i]) != 0) {
+ output |= (1 << i);
+ }
+ }
+ return output;
+ }
+
+ /**
* @hide
* Unfortunately some developers (OpenFeint I am looking at you) have
* compared the configChanges bit field against absolute values, so if we
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index a176593..525059f 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -789,6 +789,7 @@
TypedValue outValue,
boolean resolve);
/*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix);
+ /*package*/ native static final int getThemeChangingConfigurations(long theme);
private native final long openXmlAssetNative(int cookie, String fileName);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 6e77e33..ae41b69 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1731,6 +1731,19 @@
}
/**
+ * Returns a bit mask of configuration changes that will impact this
+ * theme (and thus require completely reloading it).
+ *
+ * @return a bit mask of configuration changes, as defined by
+ * {@link ActivityInfo}
+ * @see ActivityInfo
+ */
+ public int getChangingConfigurations() {
+ final int nativeChangingConfig = AssetManager.getThemeChangingConfigurations(mTheme);
+ return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
+ }
+
+ /**
* Print contents of this theme out to the log. For debugging only.
*
* @param priority The log priority to use.
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 7bebbfb..a55a08c 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -278,7 +278,7 @@
if (style[j] instanceof TypefaceSpan) {
String s = ((TypefaceSpan) style[j]).getFamily();
- if (s.equals("monospace")) {
+ if ("monospace".equals(s)) {
out.append("<tt>");
}
}
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 240d9df..d77b998 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -20,6 +20,8 @@
import android.os.SystemClock;
import android.util.EventLog;
+import dalvik.system.VMRuntime;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -96,7 +98,7 @@
public static void forceGc(String reason) {
EventLog.writeEvent(2741, reason);
- Runtime.getRuntime().gc();
+ VMRuntime.getRuntime().requestConcurrentGC();
}
static void forceBinderGc() {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index daf5a61..db495dd 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -999,6 +999,13 @@
return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
}
+static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz,
+ jlong themeHandle)
+{
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+ return theme->getChangingConfigurations();
+}
+
static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
jlong themeHandle, jint pri,
jstring tag, jstring prefix)
@@ -2103,6 +2110,8 @@
(void*) android_content_AssetManager_copyTheme },
{ "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadThemeAttributeValue },
+ { "getThemeChangingConfigurations", "(J)I",
+ (void*) android_content_AssetManager_getThemeChangingConfigurations },
{ "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
(void*) android_content_AssetManager_dumpTheme },
{ "applyStyle","(JIIJ[I[I[I)Z",
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index df278c8..587e7fa 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1662,6 +1662,12 @@
uint32_t* inoutTypeSpecFlags = NULL,
ResTable_config* inoutConfig = NULL) const;
+ /**
+ * Returns a bit mask of configuration changes that will impact this
+ * theme (and thus require completely reloading it).
+ */
+ uint32_t getChangingConfigurations() const;
+
void dumpToLog() const;
private:
@@ -1688,6 +1694,7 @@
const ResTable& mTable;
package_info* mPackages[Res_MAXPACKAGE];
+ uint32_t mTypeSpecFlags;
};
void setParameters(const ResTable_config* params);
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 04ebe70..19a5beb 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3147,6 +3147,7 @@
ResTable::Theme::Theme(const ResTable& table)
: mTable(table)
+ , mTypeSpecFlags(0)
{
memset(mPackages, 0, sizeof(mPackages));
}
@@ -3205,6 +3206,8 @@
return N;
}
+ mTypeSpecFlags |= bagTypeSpecFlags;
+
uint32_t curPackage = 0xffffffff;
ssize_t curPackageIndex = 0;
package_info* curPI = NULL;
@@ -3323,6 +3326,8 @@
}
}
+ mTypeSpecFlags = other.mTypeSpecFlags;
+
if (kDebugTableTheme) {
ALOGI("Final theme:");
dumpToLog();
@@ -3417,6 +3422,11 @@
inoutTypeSpecFlags, inoutConfig);
}
+uint32_t ResTable::Theme::getChangingConfigurations() const
+{
+ return mTypeSpecFlags;
+}
+
void ResTable::Theme::dumpToLog() const
{
ALOGI("Theme %p:\n", this);
diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk
index 51601b0..917e646 100644
--- a/libs/hwui/unit_tests/Android.mk
+++ b/libs/hwui/unit_tests/Android.mk
@@ -27,6 +27,7 @@
LOCAL_SRC_FILES += \
unit_tests/ClipAreaTests.cpp \
+ unit_tests/DamageAccumulatorTests.cpp \
unit_tests/LinearAllocatorTests.cpp \
unit_tests/main.cpp
diff --git a/libs/hwui/unit_tests/DamageAccumulatorTests.cpp b/libs/hwui/unit_tests/DamageAccumulatorTests.cpp
new file mode 100644
index 0000000..c8d6004
--- /dev/null
+++ b/libs/hwui/unit_tests/DamageAccumulatorTests.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <DamageAccumulator.h>
+#include <Matrix.h>
+#include <utils/LinearAllocator.h>
+
+#include <SkRect.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+// Test that push & pop are propegating the dirty rect
+// There is no transformation of the dirty rect, the input is the same
+// as the output.
+TEST(DamageAccumulator, identity) {
+ DamageAccumulator da;
+ Matrix4 identity;
+ SkRect curDirty;
+ identity.loadIdentity();
+ da.pushTransform(&identity);
+ da.dirty(50, 50, 100, 100);
+ da.pushTransform(&identity);
+ da.peekAtDirty(&curDirty);
+ ASSERT_EQ(SkRect(), curDirty);
+ da.popTransform();
+ da.peekAtDirty(&curDirty);
+ ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty);
+ da.popTransform();
+ da.finish(&curDirty);
+ ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty);
+}
+
+// Test that transformation is happening at the correct levels via
+// peekAtDirty & popTransform. Just uses a simple translate to test this
+TEST(DamageAccumulator, translate) {
+ DamageAccumulator da;
+ Matrix4 translate;
+ SkRect curDirty;
+ translate.loadTranslate(25, 25, 0);
+ da.pushTransform(&translate);
+ da.dirty(50, 50, 100, 100);
+ da.peekAtDirty(&curDirty);
+ ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty);
+ da.popTransform();
+ da.finish(&curDirty);
+ ASSERT_EQ(SkRect::MakeLTRB(75, 75, 125, 125), curDirty);
+}
+
+// Test that dirty rectangles are being unioned across "siblings
+TEST(DamageAccumulator, union) {
+ DamageAccumulator da;
+ Matrix4 identity;
+ SkRect curDirty;
+ identity.loadIdentity();
+ da.pushTransform(&identity);
+ da.pushTransform(&identity);
+ da.dirty(50, 50, 100, 100);
+ da.popTransform();
+ da.pushTransform(&identity);
+ da.dirty(150, 50, 200, 125);
+ da.popTransform();
+ da.popTransform();
+ da.finish(&curDirty);
+ ASSERT_EQ(SkRect::MakeLTRB(50, 50, 200, 125), curDirty);
+}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 974c9af..89d419a 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1733,6 +1733,72 @@
maxBlocks, maxBlocksPerSecond,
16 /* blockWidth */, 16 /* blockHeight */,
1 /* widthAlignment */, 1 /* heightAlignment */);
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG2)) {
+ int maxWidth = 11, maxHeight = 9, maxRate = 15;
+ maxBlocks = 99;
+ maxBlocksPerSecond = 1485;
+ maxBps = 64000;
+ for (CodecProfileLevel profileLevel: profileLevels) {
+ int MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
+ boolean supported = true;
+ switch (profileLevel.profile) {
+ case CodecProfileLevel.MPEG2ProfileSimple:
+ switch (profileLevel.level) {
+ case CodecProfileLevel.MPEG2LevelML:
+ FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break;
+ default:
+ Log.w(TAG, "Unrecognized profile/level "
+ + profileLevel.profile + "/"
+ + profileLevel.level + " for " + mime);
+ errors |= ERROR_UNRECOGNIZED;
+ }
+ break;
+ case CodecProfileLevel.MPEG2ProfileMain:
+ switch (profileLevel.level) {
+ case CodecProfileLevel.MPEG2LevelLL:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 4000; break;
+ case CodecProfileLevel.MPEG2LevelML:
+ FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break;
+ case CodecProfileLevel.MPEG2LevelH14:
+ FR = 60; W = 90; H = 68; MBPS = 367200; FS = 6120; BR = 60000; break;
+ case CodecProfileLevel.MPEG2LevelHL:
+ FR = 60; W = 120; H = 68; MBPS = 489600; FS = 8160; BR = 80000; break;
+ default:
+ Log.w(TAG, "Unrecognized profile/level "
+ + profileLevel.profile + "/"
+ + profileLevel.level + " for " + mime);
+ errors |= ERROR_UNRECOGNIZED;
+ }
+ break;
+ case CodecProfileLevel.MPEG2Profile422:
+ case CodecProfileLevel.MPEG2ProfileSNR:
+ case CodecProfileLevel.MPEG2ProfileSpatial:
+ case CodecProfileLevel.MPEG2ProfileHigh:
+ Log.i(TAG, "Unsupported profile "
+ + profileLevel.profile + " for " + mime);
+ errors |= ERROR_UNSUPPORTED;
+ supported = false;
+ break;
+ default:
+ Log.w(TAG, "Unrecognized profile "
+ + profileLevel.profile + " for " + mime);
+ errors |= ERROR_UNRECOGNIZED;
+ }
+ if (supported) {
+ errors &= ~ERROR_NONE_SUPPORTED;
+ }
+ maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond);
+ maxBlocks = Math.max(FS, maxBlocks);
+ maxBps = Math.max(BR * 1000, maxBps);
+ maxWidth = Math.max(W, maxWidth);
+ maxHeight = Math.max(H, maxHeight);
+ maxRate = Math.max(FR, maxRate);
+ }
+ applyMacroBlockLimits(maxWidth, maxHeight,
+ maxBlocks, maxBlocksPerSecond,
+ 16 /* blockWidth */, 16 /* blockHeight */,
+ 1 /* widthAlignment */, 1 /* heightAlignment */);
+ mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
int maxWidth = 11, maxHeight = 9, maxRate = 15;
maxBlocks = 99;
@@ -2343,6 +2409,20 @@
public static final int MPEG4Level4a = 0x40;
public static final int MPEG4Level5 = 0x80;
+ // from OMX_VIDEO_MPEG2PROFILETYPE
+ public static final int MPEG2ProfileSimple = 0x00;
+ public static final int MPEG2ProfileMain = 0x01;
+ public static final int MPEG2Profile422 = 0x02;
+ public static final int MPEG2ProfileSNR = 0x03;
+ public static final int MPEG2ProfileSpatial = 0x04;
+ public static final int MPEG2ProfileHigh = 0x05;
+
+ // from OMX_VIDEO_MPEG2LEVELTYPE
+ public static final int MPEG2LevelLL = 0x00;
+ public static final int MPEG2LevelML = 0x01;
+ public static final int MPEG2LevelH14 = 0x02;
+ public static final int MPEG2LevelHL = 0x03;
+
// from OMX_AUDIO_AACPROFILETYPE
public static final int AACObjectMain = 1;
public static final int AACObjectLC = 2;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 86c23c7..16b4c43 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -60,6 +60,7 @@
public static final String VIDEO_H264_AAC = "/sdcard/media_api/video/H264_320_AAC_64.3gp";
public static final String VIDEO_H264_AMR = "/sdcard/media_api/video/H264_320_AMRNB_6.3gp";
public static final String VIDEO_HEVC_AAC = "/sdcard/media_api/video/HEVC_320_AAC_128.mp4";
+ public static final String VIDEO_MPEG2_AAC = "/sdcard/media_api/video/MPEG2_1500_AAC_128.mp4";
public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp";
public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp";
public static final String VIDEO_WEBM = "/sdcard/media_api/video/big-buck-bunny_trailer.webm";
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 244b07f..c5281657 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -432,7 +432,22 @@
assertTrue("HEVC playback memory test", memoryResult);
}
- // Test case 4: Capture the memory usage after every 20 video only recorded
+ // Test case 4: Capture the memory usage after every 20 mpeg2 playback
+ @LargeTest
+ public void testMPEG2VideoPlaybackMemoryUsage() throws Exception {
+ boolean memoryResult = false;
+
+ mStartPid = getMediaserverPid();
+ for (int i = 0; i < NUM_STRESS_LOOP; i++) {
+ mediaStressPlayback(MediaNames.VIDEO_MPEG2_AAC);
+ getMemoryWriteToLog(i);
+ writeProcmemInfo();
+ }
+ memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
+ assertTrue("MPEG2 playback memory test", memoryResult);
+ }
+
+ // Test case 5: Capture the memory usage after every 20 video only recorded
@LargeTest
public void testH263RecordVideoOnlyMemoryUsage() throws Exception {
if (mCamcorderProfile != null) {
@@ -453,7 +468,7 @@
}
}
- // Test case 5: Capture the memory usage after every 20 video only recorded
+ // Test case 6: Capture the memory usage after every 20 video only recorded
@LargeTest
public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception {
if (mCamcorderProfile != null) {
@@ -474,7 +489,7 @@
}
}
- // Test case 6: Capture the memory usage after every 20 video and audio
+ // Test case 7: Capture the memory usage after every 20 video and audio
// recorded
@LargeTest
public void testRecordVideoAudioMemoryUsage() throws Exception {
@@ -496,7 +511,7 @@
}
}
- // Test case 7: Capture the memory usage after every 20 audio only recorded
+ // Test case 8: Capture the memory usage after every 20 audio only recorded
@LargeTest
public void testRecordAudioOnlyMemoryUsage() throws Exception {
boolean memoryResult = false;
@@ -511,7 +526,7 @@
assertTrue("audio record only memory test", memoryResult);
}
- // Test case 8: Capture the memory usage after every 20 camera preview
+ // Test case 9: Capture the memory usage after every 20 camera preview
@LargeTest
public void testCameraPreviewMemoryUsage() throws Exception {
boolean memoryResult = false;
diff --git a/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4 b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4
new file mode 100644
index 0000000..33f66a0
--- /dev/null
+++ b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4
Binary files differ
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 2e0bece..9dd2b20 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -449,7 +449,7 @@
InputStream src = null;
OutputStream dst = null;
- boolean errorOccurred = false;
+ IOException copyError = null;
try {
srcFile = mSrcClient.openFile(srcUri, "r", canceller);
dstFile = mDstClient.openFile(dstUri, "w", canceller);
@@ -462,24 +462,35 @@
dst.write(buffer, 0, len);
makeProgress(len);
}
+
srcFile.checkError();
- dstFile.checkError();
} catch (IOException e) {
- errorOccurred = true;
- Log.e(TAG, "Error while copying " + srcUri.toString(), e);
- try {
- mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri));
- } catch (FileNotFoundException ignore) {
- Log.w(TAG, "Source file gone: " + srcUri, e);
- // The source file is gone.
- }
+ copyError = e;
} finally {
+ if (copyError != null) {
+ try {
+ dstFile.closeWithError(copyError.getMessage());
+ } catch (IOException e) {
+ Log.e(TAG, "Error closing destination", e);
+ }
+ }
// This also ensures the file descriptors are closed.
IoUtils.closeQuietly(src);
IoUtils.closeQuietly(dst);
}
- if (errorOccurred || mIsCancelled) {
+ if (copyError != null) {
+ // Log errors.
+ Log.e(TAG, "Error while copying " + srcUri.toString(), copyError);
+ try {
+ mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri));
+ } catch (FileNotFoundException ignore) {
+ Log.w(TAG, "Source file gone: " + srcUri, copyError);
+ // The source file is gone.
+ }
+ }
+
+ if (copyError != null || mIsCancelled) {
// Clean up half-copied files.
canceller.cancel();
try {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
index 13f7daa..b1c84dd 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
@@ -16,23 +16,21 @@
package com.android.documentsui;
-import static com.android.documentsui.model.DocumentInfo.getCursorString;
-
-import android.app.NotificationManager;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
-import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.RemoteException;
import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
import android.test.MoreAsserts;
import android.test.ServiceTestCase;
+import android.test.mock.MockContentResolver;
import android.util.Log;
import com.android.documentsui.model.DocumentInfo;
@@ -43,40 +41,93 @@
import libcore.io.IoUtils;
import libcore.io.Streams;
-import org.mockito.Mockito;
-
+import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
public class CopyTest extends ServiceTestCase<CopyService> {
+ /**
+ * A test resolver that enables this test suite to listen for notifications that mark when copy
+ * operations are done.
+ */
+ class TestContentResolver extends MockContentResolver {
+ private CountDownLatch mReadySignal;
+ private CountDownLatch mNotificationSignal;
+
+ public TestContentResolver() {
+ mReadySignal = new CountDownLatch(1);
+ }
+
+ /**
+ * Wait for the given number of files to be copied to destination. Times out after 1 sec.
+ */
+ public void waitForChanges(int count) throws Exception {
+ // Wait for no more than 1 second by default.
+ waitForChanges(count, 1000);
+ }
+
+ /**
+ * Wait for files to be copied to destination.
+ *
+ * @param count Number of files to wait for.
+ * @param timeOut Timeout in ms. TimeoutException will be thrown if this function times out.
+ */
+ public void waitForChanges(int count, int timeOut) throws Exception {
+ mNotificationSignal = new CountDownLatch(count);
+ // Signal that the test is now waiting for files.
+ mReadySignal.countDown();
+ if (!mNotificationSignal.await(timeOut, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException("Timed out waiting for files to be copied.");
+ }
+ }
+
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+ // Wait until the test is ready to receive file notifications.
+ try {
+ mReadySignal.await();
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Interrupted while waiting for file copy readiness");
+ Thread.currentThread().interrupt();
+ }
+ if (DocumentsContract.isDocumentUri(mContext, uri)) {
+ Log.d(TAG, "Notification: " + uri);
+ // Watch for document URI change notifications - this signifies the end of a copy.
+ mNotificationSignal.countDown();
+ }
+ }
+ };
+
public CopyTest() {
super(CopyService.class);
}
- private static String TAG = "CopyTest";
- // This must match the authority for the StubProvider.
private static String AUTHORITY = "com.android.documentsui.stubprovider";
+ private static String DST = "sd1";
+ private static String SRC = "sd0";
+ private static String TAG = "CopyTest";
private List<RootInfo> mRoots;
private Context mContext;
- private ContentResolver mResolver;
+ private TestContentResolver mResolver;
private ContentProviderClient mClient;
- private NotificationManager mNotificationManager;
+ private StubProvider mStorage;
+ private Context mSystemContext;
@Override
protected void setUp() throws Exception {
super.setUp();
- setupTestContext();
- mResolver = mContext.getContentResolver();
+ setupTestContext();
mClient = mResolver.acquireContentProviderClient(AUTHORITY);
// Reset the stub provider's storage.
- mClient.call("clear", "", null);
+ mStorage.clearCacheAndBuildRoots();
mRoots = Lists.newArrayList();
Uri queryUri = DocumentsContract.buildRootsUri(AUTHORITY);
@@ -84,9 +135,7 @@
try {
cursor = mClient.query(queryUri, null, null, null, null);
while (cursor.moveToNext()) {
- final RootInfo root = RootInfo.fromRootsCursor(AUTHORITY, cursor);
- final String id = root.rootId;
- mRoots.add(root);
+ mRoots.add(RootInfo.fromRootsCursor(AUTHORITY, cursor));
}
} finally {
IoUtils.closeQuietly(cursor);
@@ -100,68 +149,94 @@
super.tearDown();
}
- public List<Uri> setupTestFiles() throws Exception {
- Uri rootUri = DocumentsContract.buildDocumentUri(AUTHORITY, mRoots.get(0).documentId);
- List<Uri> testFiles = Lists.newArrayList(
- DocumentsContract.createDocument(mClient, rootUri, "text/plain", "test0.txt"),
- DocumentsContract.createDocument(mClient, rootUri, "text/plain", "test1.txt"),
- DocumentsContract.createDocument(mClient, rootUri, "text/plain", "test2.txt")
- );
- String testContent[] = {
- "The five boxing wizards jump quickly",
- "The quick brown fox jumps over the lazy dog",
- "Jackdaws love my big sphinx of quartz"
- };
- for (int i = 0; i < testFiles.size(); ++i) {
- ParcelFileDescriptor pfd = null;
- OutputStream out = null;
- try {
- pfd = mClient.openFile(testFiles.get(i), "w");
- out = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);
- out.write(testContent[i].getBytes());
- } finally {
- IoUtils.closeQuietly(out);
- }
- }
- return testFiles;
- }
-
/**
* Test copying a single file.
*/
public void testCopyFile() throws Exception {
- Uri testFile = setupTestFiles().get(0);
+ String srcPath = "/test0.txt";
+ Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
+ "The five boxing wizards jump quickly".getBytes());
- // Just copy one file.
+ assertDstFileCountEquals(0);
+
copyToDestination(Lists.newArrayList(testFile));
- // A call to NotificationManager.cancel marks the end of the copy operation.
- Mockito.verify(mNotificationManager, Mockito.timeout(1000)).cancel(Mockito.anyString(),
- Mockito.anyInt());
+ // 2 operations: file creation, then writing data.
+ mResolver.waitForChanges(2);
// Verify that one file was copied; check file contents.
assertDstFileCountEquals(1);
- assertCopied(testFile);
+ assertCopied(srcPath);
}
/**
* Test copying multiple files.
*/
public void testCopyMultipleFiles() throws Exception {
- List<Uri> testFiles = setupTestFiles();
+ String testContent[] = {
+ "The five boxing wizards jump quickly",
+ "The quick brown fox jumps over the lazy dog",
+ "Jackdaws love my big sphinx of quartz"
+ };
+ String srcPaths[] = {
+ "/test0.txt",
+ "/test1.txt",
+ "/test2.txt"
+ };
+ List<Uri> testFiles = Lists.newArrayList(
+ mStorage.createFile(SRC, srcPaths[0], "text/plain", testContent[0].getBytes()),
+ mStorage.createFile(SRC, srcPaths[1], "text/plain", testContent[1].getBytes()),
+ mStorage.createFile(SRC, srcPaths[2], "text/plain", testContent[2].getBytes()));
+
+ assertDstFileCountEquals(0);
+
// Copy all the test files.
copyToDestination(testFiles);
- // A call to NotificationManager.cancel marks the end of the copy operation.
- Mockito.verify(mNotificationManager, Mockito.timeout(1000)).cancel(Mockito.anyString(),
- Mockito.anyInt());
+ // 3 file creations, 3 file writes.
+ mResolver.waitForChanges(6);
assertDstFileCountEquals(3);
- for (Uri testFile : testFiles) {
- assertCopied(testFile);
+ for (String path : srcPaths) {
+ assertCopied(path);
}
}
+ public void testCopyEmptyDir() throws Exception {
+ String srcPath = "/emptyDir";
+ Uri testDir = mStorage.createFile(SRC, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
+ null);
+
+ assertDstFileCountEquals(0);
+
+ copyToDestination(Lists.newArrayList(testDir));
+
+ // Just 1 operation: Directory creation.
+ mResolver.waitForChanges(1);
+
+ assertDstFileCountEquals(1);
+
+ File dst = mStorage.getFile(DST, srcPath);
+ assertTrue(dst.isDirectory());
+ }
+
+ public void testReadErrors() throws Exception {
+ String srcPath = "/test0.txt";
+ Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
+ "The five boxing wizards jump quickly".getBytes());
+
+ assertDstFileCountEquals(0);
+
+ mStorage.simulateReadErrors(true);
+
+ copyToDestination(Lists.newArrayList(testFile));
+
+ // 3 operations: file creation, writing, then deletion (due to failed copy).
+ mResolver.waitForChanges(3);
+
+ assertDstFileCountEquals(0);
+ }
+
/**
* Copies the given files to a pre-determined destination.
*
@@ -200,82 +275,55 @@
assertEquals("Incorrect file count after copy", expected, count);
}
- /**
- * Verifies that the file pointed to by the given URI was correctly copied to the destination.
- */
- private void assertCopied(Uri src) throws Exception {
- Cursor cursor = null;
- String srcName = null;
+ private void assertCopied(String path) throws Exception {
+ File srcFile = mStorage.getFile(SRC, path);
+ File dstFile = mStorage.getFile(DST, path);
+ assertNotNull(dstFile);
+
+ FileInputStream src = null;
+ FileInputStream dst = null;
try {
- cursor = mClient.query(src, null, null, null, null);
- if (cursor.moveToFirst()) {
- srcName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
- }
+ src = new FileInputStream(srcFile);
+ dst = new FileInputStream(dstFile);
+ byte[] srcbuf = Streams.readFully(src);
+ byte[] dstbuf = Streams.readFully(dst);
+
+ MoreAsserts.assertEquals(srcbuf, dstbuf);
} finally {
- IoUtils.closeQuietly(cursor);
+ IoUtils.closeQuietly(src);
+ IoUtils.closeQuietly(dst);
}
- Uri dst = getDstFileUri(srcName);
-
- InputStream in0 = null;
- InputStream in1 = null;
- try {
- in0 = new ParcelFileDescriptor.AutoCloseInputStream(mClient.openFile(src, "r"));
- in1 = new ParcelFileDescriptor.AutoCloseInputStream(mClient.openFile(dst, "r"));
-
- byte[] buffer0 = Streams.readFully(in0);
- byte[] buffer1 = Streams.readFully(in1);
-
- MoreAsserts.assertEquals(buffer0, buffer1);
- } finally {
- IoUtils.closeQuietly(in0);
- IoUtils.closeQuietly(in1);
- }
- }
-
- /**
- * Generates a file URI from a given filename. This assumes the file already exists in the
- * destination root.
- */
- private Uri getDstFileUri(String filename) throws RemoteException {
- final Uri dstFileQuery = DocumentsContract.buildChildDocumentsUri(AUTHORITY,
- mRoots.get(1).documentId);
- Cursor cursor = null;
- try {
- // StubProvider doesn't seem to support query strings; filter the results manually.
- cursor = mClient.query(dstFileQuery, null, null, null, null);
- while (cursor.moveToNext()) {
- if (filename.equals(getCursorString(cursor, Document.COLUMN_DISPLAY_NAME))) {
- return DocumentsContract.buildDocumentUri(AUTHORITY,
- getCursorString(cursor, Document.COLUMN_DOCUMENT_ID));
- }
- }
- } finally {
- IoUtils.closeQuietly(cursor);
- }
- return null;
}
/**
* Sets up a ContextWrapper that substitutes a stub NotificationManager. This allows the test to
* listen for notification events, to gauge copy progress.
+ *
+ * @throws FileNotFoundException
*/
- private void setupTestContext() {
- mContext = getSystemContext();
- System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
+ private void setupTestContext() throws FileNotFoundException {
+ mSystemContext = getSystemContext();
- mNotificationManager = Mockito.spy((NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE));
-
- // Insert a stub NotificationManager that enables us to listen for when copying is complete.
- setContext(new ContextWrapper(mContext) {
+ // Set up the context with the test content resolver.
+ mResolver = new TestContentResolver();
+ mContext = new ContextWrapper(mSystemContext) {
@Override
- public Object getSystemService(String name) {
- if (Context.NOTIFICATION_SERVICE.equals(name)) {
- return mNotificationManager;
- } else {
- return super.getSystemService(name);
- }
+ public ContentResolver getContentResolver() {
+ return mResolver;
}
- });
+ };
+ setContext(mContext);
+
+ // Create a local stub provider and add it to the content resolver.
+ ProviderInfo info = new ProviderInfo();
+ info.authority = AUTHORITY;
+ info.exported = true;
+ info.grantUriPermissions = true;
+ info.readPermission = android.Manifest.permission.MANAGE_DOCUMENTS;
+ info.writePermission = android.Manifest.permission.MANAGE_DOCUMENTS;
+
+ mStorage = new StubProvider();
+ mStorage.attachInfo(mContext, info);
+ mResolver.addProvider(AUTHORITY, mStorage);
}
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 438f6cd..8cef433 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -21,9 +21,10 @@
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
-import android.database.MatrixCursor.RowBuilder;
import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
+import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.FileUtils;
@@ -32,15 +33,16 @@
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
+import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.google.android.collect.Maps;
import libcore.io.IoUtils;
-import java.io.FileOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -70,6 +72,7 @@
private String mAuthority;
private SharedPreferences mPrefs;
private Map<String, RootInfo> mRoots;
+ private boolean mSimulateReadErrors;
@Override
public void attachInfo(Context context, ProviderInfo info) {
@@ -83,7 +86,8 @@
return true;
}
- private void clearCacheAndBuildRoots() {
+ @VisibleForTesting
+ public void clearCacheAndBuildRoots() {
final File cacheDir = getContext().getCacheDir();
removeRecursively(cacheDir);
mStorage.clear();
@@ -164,7 +168,7 @@
} else {
try {
if (!file.createNewFile()) {
- throw new FileNotFoundException();
+ throw new IllegalStateException("The file " + file.getPath() + " already exists");
}
} catch (IOException e) {
throw new FileNotFoundException();
@@ -173,6 +177,10 @@
final StubDocument document = new StubDocument(file, mimeType, parentDocument);
notifyParentChanged(document.parentId);
+ getContext().getContentResolver().notifyChange(
+ DocumentsContract.buildDocumentUri(mAuthority, document.documentId),
+ null, false);
+
return document.documentId;
}
@@ -187,6 +195,9 @@
document.rootInfo.size -= fileSize;
}
notifyParentChanged(document.parentId);
+ getContext().getContentResolver().notifyChange(
+ DocumentsContract.buildDocumentUri(mAuthority, document.documentId),
+ null, false);
}
@Override
@@ -226,7 +237,17 @@
throw new FileNotFoundException();
if ("r".equals(mode)) {
- return ParcelFileDescriptor.open(document.file, ParcelFileDescriptor.MODE_READ_ONLY);
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(document.file,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ if (mSimulateReadErrors) {
+ pfd = new ParcelFileDescriptor(pfd) {
+ @Override
+ public void checkError() throws IOException {
+ throw new IOException("Test error");
+ }
+ };
+ }
+ return pfd;
}
if ("w".equals(mode)) {
return startWrite(document);
@@ -235,6 +256,11 @@
throw new FileNotFoundException();
}
+ @VisibleForTesting
+ public void simulateReadErrors(boolean b) {
+ mSimulateReadErrors = b;
+ }
+
@Override
public AssetFileDescriptor openDocumentThumbnail(
String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
@@ -281,11 +307,15 @@
}
}
} catch (IOException e) {
+ Log.e(TAG, "Error on close", e);
closePipeWithErrorSilently(readPipe, e.getMessage());
} finally {
IoUtils.closeQuietly(inputStream);
IoUtils.closeQuietly(outputStream);
notifyParentChanged(document.parentId);
+ getContext().getContentResolver().notifyChange(
+ DocumentsContract.buildDocumentUri(mAuthority, document.documentId),
+ null, false);
}
}
}.start();
@@ -302,7 +332,6 @@
@Override
public Bundle call(String method, String arg, Bundle extras) {
- Log.d(TAG, "call: " + method + arg);
switch (method) {
case "clear":
clearCacheAndBuildRoots();
@@ -376,30 +405,51 @@
}
}
- public File createFile(String rootId, File parent, String mimeType, String name)
- throws IOException {
- StubDocument parentDoc = null;
+ @VisibleForTesting
+ public Uri createFile(String rootId, String path, String mimeType, byte[] content)
+ throws FileNotFoundException, IOException {
+ StubDocument root = mRoots.get(rootId).rootDocument;
+ if (root == null) {
+ throw new FileNotFoundException("No roots with the ID " + rootId + " were found");
+ }
+ File file = new File(root.file, path.substring(1));
+ StubDocument parent = mStorage.get(getDocumentIdForFile(file.getParentFile()));
if (parent == null) {
- // Use the root dir as the parent, if one wasn't specified.
- parentDoc = mRoots.get(rootId).rootDocument;
- } else {
- // Verify that the parent exists and is a directory.
- parentDoc = mStorage.get(getDocumentIdForFile(parent));
- if (parentDoc == null) {
- throw new IllegalArgumentException("Parent file not found.");
- }
- if (!Document.MIME_TYPE_DIR.equals(parentDoc.mimeType)) {
- throw new IllegalArgumentException("Parent file must be a directory.");
- }
+ parent = mStorage.get(createFile(rootId, file.getParentFile().getPath(),
+ DocumentsContract.Document.MIME_TYPE_DIR, null));
}
- File file = new File(parentDoc.file, name);
- if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- file.mkdir();
+
+ if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
+ if (!file.mkdirs()) {
+ throw new FileNotFoundException("Couldn't create directory " + file.getPath());
+ }
} else {
- file.createNewFile();
+ if (!file.createNewFile()) {
+ throw new FileNotFoundException("Couldn't create file " + file.getPath());
+ }
+ // Add content to the file.
+ FileOutputStream fout = new FileOutputStream(file);
+ fout.write(content);
+ fout.close();
}
- new StubDocument(file, mimeType, parentDoc);
- return file;
+ final StubDocument document = new StubDocument(file, mimeType, parent);
+ return DocumentsContract.buildDocumentUri(mAuthority, document.documentId);
+ }
+
+ @VisibleForTesting
+ public File getFile(String rootId, String path) throws FileNotFoundException {
+ StubDocument root = mRoots.get(rootId).rootDocument;
+ if (root == null) {
+ throw new FileNotFoundException("No roots with the ID " + rootId + " were found");
+ }
+ // Convert the path string into a path that's relative to the root.
+ File needle = new File(root.file, path.substring(1));
+
+ StubDocument found = mStorage.get(getDocumentIdForFile(needle));
+ if (found == null) {
+ return null;
+ }
+ return found.file;
}
final class RootInfo {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b2c9cd0..4082bf5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -111,8 +111,10 @@
<string name="bluetooth_profile_pbap_summary">Use for contact sharing</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (sharing this device's Internet connection). [CHAR LIMIT=40] -->
<string name="bluetooth_profile_pan_nap">Internet connection sharing</string>
- <!-- Bluetooth settings. The user-visible string that is used whenever referring to the map profile. -->
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the map profile. -->
<string name="bluetooth_profile_map">Message Access</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the SAP profile (sharing SIM card). -->
+ <string name="bluetooth_profile_sap">SIM Access</string>
<!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference when A2DP is connected. -->
<string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
@@ -122,6 +124,8 @@
<string name="bluetooth_opp_profile_summary_connected">Connected to file transfer server</string>
<!-- Bluetooth settings. Connection options screen. The summary for the map checkbox preference when map is connected. -->
<string name="bluetooth_map_profile_summary_connected">Connected to map</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the sap checkbox preference when SAP is connected. -->
+ <string name="bluetooth_sap_profile_summary_connected">Connected to SAP</string>
<!-- Bluetooth settings. Connection options screen. The summary for the OPP checkbox preference when OPP is not connected. -->
<string name="bluetooth_opp_profile_summary_not_connected">Not connected to file transfer server</string>
<!-- Bluetooth settings. Connection options screen. The summary for the HID checkbox preference when HID is connected. -->
@@ -137,6 +141,8 @@
<string name="bluetooth_pan_profile_summary_use_for">Use for Internet access</string>
<!-- Bluetooth settings. Connection options screen. The summary for the map checkbox preference that describes how checking it will set the map profile as preferred. -->
<string name="bluetooth_map_profile_summary_use_for">Use for map</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the sap checkbox preference that describes how checking it will set the sap profile as preferred. -->
+ <string name="bluetooth_sap_profile_summary_use_for">Use for SIM access</string>
<!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference that describes how checking it will set the A2DP profile as preferred. -->
<string name="bluetooth_a2dp_profile_summary_use_for">Use for media audio</string>
<!-- Bluetooth settings. Connection options screen. The summary for the headset checkbox preference that describes how checking it will set the headset profile as preferred. -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index dd2368f..7534b8e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -67,10 +67,6 @@
private boolean mVisible;
- private int mPhonebookPermissionChoice;
-
- private int mMessagePermissionChoice;
-
private int mMessageRejectionCount;
private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
@@ -556,6 +552,7 @@
mConnectAfterPairing = false; // cancel auto-connect
setPhonebookPermissionChoice(ACCESS_UNKNOWN);
setMessagePermissionChoice(ACCESS_UNKNOWN);
+ setSimPermissionChoice(ACCESS_UNKNOWN);
mMessageRejectionCount = 0;
saveMessageRejectionCount();
}
@@ -732,6 +729,26 @@
mDevice.setMessageAccessPermission(permission);
}
+ public int getSimPermissionChoice() {
+ int permission = mDevice.getSimAccessPermission();
+ if (permission == BluetoothDevice.ACCESS_ALLOWED) {
+ return ACCESS_ALLOWED;
+ } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
+ return ACCESS_REJECTED;
+ }
+ return ACCESS_UNKNOWN;
+ }
+
+ void setSimPermissionChoice(int permissionChoice) {
+ int permission = BluetoothDevice.ACCESS_UNKNOWN;
+ if (permissionChoice == ACCESS_ALLOWED) {
+ permission = BluetoothDevice.ACCESS_ALLOWED;
+ } else if (permissionChoice == ACCESS_REJECTED) {
+ permission = BluetoothDevice.ACCESS_REJECTED;
+ }
+ mDevice.setSimAccessPermission(permission);
+ }
+
// Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
// app's shared preferences).
private void migrateMessagePermissionChoice() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
new file mode 100644
index 0000000..25c53e6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2015 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 com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSap;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * SapProfile handles Bluetooth SAP profile.
+ */
+final class SapProfile implements LocalBluetoothProfile {
+ private static final String TAG = "SapProfile";
+ private static boolean V = true;
+
+ private BluetoothSap mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ static final ParcelUuid[] UUIDS = {
+ BluetoothUuid.SAP,
+ };
+
+ static final String NAME = "SAP";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 10;
+
+ // These callbacks run on the main thread.
+ private final class SapServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothSap) proxy;
+ // We just bound to the service, so refresh the UI for any connected SAP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "SapProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(SapProfile.this,
+ BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mProfileManager.callServiceDisconnectedListeners();
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ SapProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new SapServiceListener(),
+ BluetoothProfile.SAP);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> sinks = mService.getConnectedDevices();
+ if (sinks != null) {
+ for (BluetoothDevice sink : sinks) {
+ mService.disconnect(sink);
+ }
+ }
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ } else {
+ return false;
+ }
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+
+ return !deviceList.isEmpty() && deviceList.get(0).equals(device)
+ ? mService.getConnectionState(device)
+ : BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_sap;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_sap_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_sap_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_cellphone;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.SAP,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up SAP proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index 1cf7248..c81f22a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -32,7 +32,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageVolume;
@@ -349,6 +348,11 @@
final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
details);
+ if (mVolume == null || !mVolume.isMountedReadable()) {
+ finished.sendToTarget();
+ return;
+ }
+
if (mSharedVolume != null && mSharedVolume.isMountedReadable()) {
final File basePath = mSharedVolume.getPathForUser(currentUser);
@@ -375,8 +379,10 @@
}
final File file = mVolume.getPath();
- details.totalSize = file.getTotalSpace();
- details.availSize = file.getFreeSpace();
+ if (file != null) {
+ details.totalSize = file.getTotalSpace();
+ details.availSize = file.getFreeSpace();
+ }
// Measure all apps hosted on this volume for all users
if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) {
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 80fecac..b836af6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -951,6 +951,12 @@
<!-- Button label for ending zen mode in the volume dialog -->
<string name="volume_zen_end_now">End now</string>
+ <!-- Content description for accessibility (not shown on the screen): volume dialog expand button. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_volume_expand">Expand</string>
+
+ <!-- Content description for accessibility (not shown on the screen): volume dialog collapse button. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_volume_collapse">Collapse</string>
+
<!-- Screen pinning dialog title. -->
<string name="screen_pinning_title">Screen is pinned</string>
<!-- Screen pinning dialog description. -->
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 9434036..7115897 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -16,6 +16,9 @@
package com.android.systemui.volume;
+import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
@@ -32,6 +35,7 @@
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.AudioSystem;
+import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -43,6 +47,8 @@
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnClickListener;
import android.view.View.OnLayoutChangeListener;
import android.view.View.OnTouchListener;
@@ -50,6 +56,9 @@
import android.view.ViewGroup.MarginLayoutParams;
import android.view.Window;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageButton;
import android.widget.LinearLayout;
@@ -97,6 +106,7 @@
private final ZenFooter mZenFooter;
private final LayoutTransition mLayoutTransition;
private final Object mSafetyWarningLock = new Object();
+ private final Accessibility mAccessibility = new Accessibility();
private boolean mShowing;
private boolean mExpanded;
@@ -174,6 +184,8 @@
mZenFooter = (ZenFooter) mDialog.findViewById(R.id.volume_zen_footer);
mZenFooter.init(zenModeController);
+ mAccessibility.init();
+
controller.addCallback(mControllerCallbackH, mHandler);
controller.getState();
}
@@ -409,10 +421,13 @@
protected void rescheduleTimeoutH() {
mHandler.removeMessages(H.DISMISS);
- final int timeout = computeTimeoutH();
- if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout);
- mHandler.sendMessageDelayed(mHandler
- .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+ int timeout = -1;
+ if (!mAccessibility.mFeedbackEnabled) {
+ timeout = computeTimeoutH();
+ mHandler.sendMessageDelayed(mHandler
+ .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+ }
+ if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
mController.userActivity();
}
@@ -475,6 +490,8 @@
if (res == mExpandButtonRes) return;
mExpandButtonRes = res;
mExpandButton.setImageResource(res);
+ mExpandButton.setContentDescription(mContext.getString(mExpanded ?
+ R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
}
private boolean isVisibleH(VolumeRow row, boolean isActive) {
@@ -632,6 +649,7 @@
: (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes)
? Events.ICON_STATE_UNMUTE
: Events.ICON_STATE_UNKNOWN;
+ row.icon.setContentDescription(ss.name);
// update slider
updateVolumeRowSliderH(row, zenMuted);
@@ -931,6 +949,56 @@
}
}
+ private final class Accessibility {
+ private AccessibilityManager mMgr;
+ private boolean mFeedbackEnabled;
+
+ public void init() {
+ mMgr = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ mDialogView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ // noop
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ updateFeedbackEnabled();
+ }
+ });
+ mDialogView.setAccessibilityDelegate(new AccessibilityDelegate() {
+ @Override
+ public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
+ AccessibilityEvent event) {
+ rescheduleTimeoutH();
+ return super.onRequestSendAccessibilityEvent(host, child, event);
+ }
+ });
+ mMgr.addAccessibilityStateChangeListener(new AccessibilityStateChangeListener() {
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ updateFeedbackEnabled();
+ }
+ });
+ updateFeedbackEnabled();
+ }
+
+ private void updateFeedbackEnabled() {
+ mFeedbackEnabled = computeFeedbackEnabled();
+ }
+
+ private boolean computeFeedbackEnabled() {
+ final List<AccessibilityServiceInfo> services =
+ mMgr.getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
+ for (AccessibilityServiceInfo asi : services) {
+ if ((asi.feedbackType & FEEDBACK_ALL_MASK) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
private static class VolumeRow {
private View view;
private View space;
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index e7f210b..4f10699 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -302,8 +302,12 @@
long[] fieldIDs, long[] values, int[] sizes, long[] depClosures,
long[] depFieldIDs) {
validate();
- return rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
+ long c = rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
sizes, depClosures, depFieldIDs);
+ if (c == 0) {
+ throw new RSRuntimeException("Failed creating closure.");
+ }
+ return c;
}
native long rsnInvokeClosureCreate(long con, long invokeID, byte[] params,
@@ -311,8 +315,12 @@
synchronized long nInvokeClosureCreate(long invokeID, byte[] params,
long[] fieldIDs, long[] values, int[] sizes) {
validate();
- return rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
+ long c = rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
values, sizes);
+ if (c == 0) {
+ throw new RSRuntimeException("Failed creating closure.");
+ }
+ return c;
}
native void rsnClosureSetArg(long con, long closureID, int index,
@@ -337,7 +345,11 @@
synchronized long nScriptGroup2Create(String name, String cachePath,
long[] closures) {
validate();
- return rsnScriptGroup2Create(mContext, name, cachePath, closures);
+ long g = rsnScriptGroup2Create(mContext, name, cachePath, closures);
+ if (g == 0) {
+ throw new RSRuntimeException("Failed creating script group.");
+ }
+ return g;
}
native void rsnScriptGroup2Execute(long con, long groupID);
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 5ef807d..80d6515 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -43,6 +43,9 @@
//#define LOG_API ALOGE
static constexpr bool kLogApi = false;
+static constexpr size_t kMaxNumberArgsAndBindings = 1000;
+static constexpr size_t kMaxNumberClosuresInScriptGroup = 1000000;
+static constexpr size_t kMaxNumberKernelArguments = 256;
using namespace android;
@@ -328,79 +331,167 @@
jlong returnValue, jlongArray fieldIDArray,
jlongArray valueArray, jintArray sizeArray,
jlongArray depClosureArray, jlongArray depFieldIDArray) {
+ jlong ret = 0;
+
jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
- RsScriptFieldID* fieldIDs =
- (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
- for (int i = 0; i< fieldIDs_length; i++) {
- fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
- }
-
jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
jsize values_length = _env->GetArrayLength(valueArray);
- uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
- for (int i = 0; i < values_length; i++) {
- values[i] = (uintptr_t)jValues[i];
- }
-
- jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
+ jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
jsize sizes_length = _env->GetArrayLength(sizeArray);
-
jlong* jDepClosures =
_env->GetLongArrayElements(depClosureArray, nullptr);
jsize depClosures_length = _env->GetArrayLength(depClosureArray);
- RsClosure* depClosures =
- (RsClosure*)alloca(sizeof(RsClosure) * depClosures_length);
- for (int i = 0; i < depClosures_length; i++) {
- depClosures[i] = (RsClosure)jDepClosures[i];
- }
-
jlong* jDepFieldIDs =
_env->GetLongArrayElements(depFieldIDArray, nullptr);
jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray);
- RsScriptFieldID* depFieldIDs =
- (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * depFieldIDs_length);
- for (int i = 0; i < depClosures_length; i++) {
+
+ size_t numValues, numDependencies;
+ RsScriptFieldID* fieldIDs;
+ uintptr_t* values;
+ RsClosure* depClosures;
+ RsScriptFieldID* depFieldIDs;
+
+ if (fieldIDs_length != values_length || values_length != sizes_length) {
+ ALOGE("Unmatched field IDs, values, and sizes in closure creation.");
+ goto exit;
+ }
+
+ numValues = (size_t)fieldIDs_length;
+
+ if (depClosures_length != depFieldIDs_length) {
+ ALOGE("Unmatched closures and field IDs for dependencies in closure creation.");
+ goto exit;
+ }
+
+ numDependencies = (size_t)depClosures_length;
+
+ if (numDependencies > numValues) {
+ ALOGE("Unexpected number of dependencies in closure creation");
+ goto exit;
+ }
+
+ if (numValues > kMaxNumberArgsAndBindings) {
+ ALOGE("Too many arguments or globals in closure creation");
+ goto exit;
+ }
+
+ fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
+ if (fieldIDs == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numValues; i++) {
+ fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
+ }
+
+ values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
+ if (values == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numValues; i++) {
+ values[i] = (uintptr_t)jValues[i];
+ }
+
+ depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies);
+ if (depClosures == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numDependencies; i++) {
+ depClosures[i] = (RsClosure)jDepClosures[i];
+ }
+
+ depFieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numDependencies);
+ if (depFieldIDs == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numDependencies; i++) {
depFieldIDs[i] = (RsClosure)jDepFieldIDs[i];
}
- return (jlong)(uintptr_t)rsClosureCreate(
+ ret = (jlong)(uintptr_t)rsClosureCreate(
(RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
- fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
- (int*)sizes, (size_t)sizes_length,
- depClosures, (size_t)depClosures_length,
- depFieldIDs, (size_t)depFieldIDs_length);
+ fieldIDs, numValues, values, numValues,
+ (int*)jSizes, numValues,
+ depClosures, numDependencies,
+ depFieldIDs, numDependencies);
+
+exit:
+
+ _env->ReleaseLongArrayElements(depFieldIDArray, jDepFieldIDs, JNI_ABORT);
+ _env->ReleaseLongArrayElements(depClosureArray, jDepClosures, JNI_ABORT);
+ _env->ReleaseIntArrayElements (sizeArray, jSizes, JNI_ABORT);
+ _env->ReleaseLongArrayElements(valueArray, jValues, JNI_ABORT);
+ _env->ReleaseLongArrayElements(fieldIDArray, jFieldIDs, JNI_ABORT);
+
+ return ret;
}
static jlong
nInvokeClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong invokeID,
jbyteArray paramArray, jlongArray fieldIDArray, jlongArray valueArray,
jintArray sizeArray) {
+ jlong ret = 0;
+
jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr);
jsize jParamLength = _env->GetArrayLength(paramArray);
-
jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
- RsScriptFieldID* fieldIDs =
- (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
- for (int i = 0; i< fieldIDs_length; i++) {
+ jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
+ jsize values_length = _env->GetArrayLength(valueArray);
+ jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
+ jsize sizes_length = _env->GetArrayLength(sizeArray);
+
+ size_t numValues;
+ RsScriptFieldID* fieldIDs;
+ uintptr_t* values;
+
+ if (fieldIDs_length != values_length || values_length != sizes_length) {
+ ALOGE("Unmatched field IDs, values, and sizes in closure creation.");
+ goto exit;
+ }
+
+ numValues = (size_t) fieldIDs_length;
+
+ if (numValues > kMaxNumberArgsAndBindings) {
+ ALOGE("Too many arguments or globals in closure creation");
+ goto exit;
+ }
+
+ fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
+ if (fieldIDs == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i< numValues; i++) {
fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
}
- jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
- jsize values_length = _env->GetArrayLength(valueArray);
- uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
- for (int i = 0; i < values_length; i++) {
+ values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
+ if (values == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numValues; i++) {
values[i] = (uintptr_t)jValues[i];
}
- jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
- jsize sizes_length = _env->GetArrayLength(sizeArray);
-
- return (jlong)(uintptr_t)rsInvokeClosureCreate(
+ ret = (jlong)(uintptr_t)rsInvokeClosureCreate(
(RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
- fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
- (int*)sizes, (size_t)sizes_length);
+ fieldIDs, numValues, values, numValues,
+ (int*)jSizes, numValues);
+
+exit:
+
+ _env->ReleaseIntArrayElements (sizeArray, jSizes, JNI_ABORT);
+ _env->ReleaseLongArrayElements(valueArray, jValues, JNI_ABORT);
+ _env->ReleaseLongArrayElements(fieldIDArray, jFieldIDs, JNI_ABORT);
+ _env->ReleaseByteArrayElements(paramArray, jParams, JNI_ABORT);
+
+ return ret;
}
static void
@@ -420,20 +511,40 @@
static long
nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name,
jstring cacheDir, jlongArray closureArray) {
+ jlong ret = 0;
+
AutoJavaStringToUTF8 nameUTF(_env, name);
AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
jsize numClosures = _env->GetArrayLength(closureArray);
- RsClosure* closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
+
+ RsClosure* closures;
+
+ if (numClosures > (jsize) kMaxNumberClosuresInScriptGroup) {
+ ALOGE("Too many closures in script group");
+ goto exit;
+ }
+
+ closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
+ if (closures == nullptr) {
+ goto exit;
+ }
+
for (int i = 0; i < numClosures; i++) {
closures[i] = (RsClosure)jClosures[i];
}
- return (jlong)(uintptr_t)rsScriptGroup2Create(
+ ret = (jlong)(uintptr_t)rsScriptGroup2Create(
(RsContext)con, nameUTF.c_str(), nameUTF.length(),
cacheDirUTF.c_str(), cacheDirUTF.length(),
closures, numClosures);
+
+exit:
+
+ _env->ReleaseLongArrayElements(closureArray, jClosures, JNI_ABORT);
+
+ return ret;
}
static void
@@ -1756,8 +1867,14 @@
if (ains != nullptr) {
in_len = _env->GetArrayLength(ains);
- in_ptr = _env->GetLongArrayElements(ains, nullptr);
+ if (in_len > (jint)kMaxNumberKernelArguments) {
+ ALOGE("Too many arguments in kernel launch.");
+ // TODO (b/20758983): Report back to Java and throw an exception
+ return;
+ }
+ // TODO (b/20760800): Check in_ptr is not null
+ in_ptr = _env->GetLongArrayElements(ains, nullptr);
if (sizeof(RsAllocation) == sizeof(jlong)) {
in_allocs = (RsAllocation*)in_ptr;
@@ -1765,6 +1882,11 @@
// Convert from 64-bit jlong types to the native pointer type.
in_allocs = (RsAllocation*)alloca(in_len * sizeof(RsAllocation));
+ if (in_allocs == nullptr) {
+ ALOGE("Failed launching kernel for lack of memory.");
+ _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
+ return;
+ }
for (int index = in_len; --index >= 0;) {
in_allocs[index] = (RsAllocation)in_ptr[index];
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index b5d566a..fd0c06d 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -14,6 +14,7 @@
package android.telecom;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -35,15 +36,27 @@
private static final String TAG = "DefaultDialerManager";
/**
- * Sets the specified package name as the default dialer application. The caller of this method
- * needs to have permission to write to secure settings.
+ * Sets the specified package name as the default dialer application for the current user.
+ * The caller of this method needs to have permission to write to secure settings and
+ * manage users on the device.
*
* @hide
* */
public static void setDefaultDialerApplication(Context context, String packageName) {
+ setDefaultDialerApplication(context, packageName, ActivityManager.getCurrentUser());
+ }
+
+ /**
+ * Sets the specified package name as the default dialer application for the specified user.
+ * The caller of this method needs to have permission to write to secure settings and
+ * manage users on the device.
+ *
+ * @hide
+ * */
+ public static void setDefaultDialerApplication(Context context, String packageName, int user) {
// Get old package name
- String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
- Settings.Secure.DIALER_DEFAULT_APPLICATION);
+ String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
// No change
@@ -55,26 +68,44 @@
if (packageNames.contains(packageName)) {
// Update the secure setting.
- Settings.Secure.putString(context.getContentResolver(),
- Settings.Secure.DIALER_DEFAULT_APPLICATION, packageName);
+ Settings.Secure.putStringForUser(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, packageName, user);
}
}
/**
- * Returns the installed dialer application that will be used to receive incoming calls, and is
- * allowed to make emergency calls.
+ * Returns the installed dialer application for the current user that will be used to receive
+ * incoming calls, and is allowed to make emergency calls.
*
* The application will be returned in order of preference:
* 1) User selected phone application (if still installed)
* 2) Pre-installed system dialer (if not disabled)
* 3) Null
*
+ * The caller of this method needs to have permission to manage users on the device.
+ *
* @hide
* */
public static String getDefaultDialerApplication(Context context) {
- String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
- Settings.Secure.DIALER_DEFAULT_APPLICATION);
+ return getDefaultDialerApplication(context, ActivityManager.getCurrentUser());
+ }
+ /**
+ * Returns the installed dialer application for the specified user that will be used to receive
+ * incoming calls, and is allowed to make emergency calls.
+ *
+ * The application will be returned in order of preference:
+ * 1) User selected phone application (if still installed)
+ * 2) Pre-installed system dialer (if not disabled)
+ * 3) Null
+ *
+ * The caller of this method needs to have permission to manage users on the device.
+ *
+ * @hide
+ * */
+ public static String getDefaultDialerApplication(Context context, int user) {
+ String defaultPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
final List<String> packageNames = getInstalledDialerApplications(context);
diff --git a/tests/MemoryUsage/AndroidManifest.xml b/tests/MemoryUsage/AndroidManifest.xml
index 3932e5b..cd559c5 100644
--- a/tests/MemoryUsage/AndroidManifest.xml
+++ b/tests/MemoryUsage/AndroidManifest.xml
@@ -6,7 +6,9 @@
android:name="com.android.tests.memoryusage.MemoryUsageInstrumentation"
android:targetPackage="com.android.tests.memoryusage" />
+ <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+
<application android:label="Memory Usage Test">
<uses-library android:name="android.test.runner" />
</application>
-</manifest>
\ No newline at end of file
+</manifest>