Merge "Changes based on API council feedback for performContextClick" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index c8404ac..ef3c5e1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2024,6 +2024,8 @@
field public static final int TextAppearance_Material_Widget_ActionMode_Title = 16974355; // 0x1030213
field public static final int TextAppearance_Material_Widget_ActionMode_Title_Inverse = 16974356; // 0x1030214
field public static final int TextAppearance_Material_Widget_Button = 16974357; // 0x1030215
+ field public static final int TextAppearance_Material_Widget_Button_Borderless_Colored = 16974559; // 0x10302df
+ field public static final int TextAppearance_Material_Widget_Button_Colored = 16974558; // 0x10302de
field public static final int TextAppearance_Material_Widget_Button_Inverse = 16974548; // 0x10302d4
field public static final int TextAppearance_Material_Widget_DropDownHint = 16974358; // 0x1030216
field public static final int TextAppearance_Material_Widget_DropDownItem = 16974359; // 0x1030217
@@ -34569,7 +34571,7 @@
package android.service.notification {
- public class Condition implements android.os.Parcelable {
+ public final class Condition implements android.os.Parcelable {
ctor public Condition(android.net.Uri, java.lang.String, int);
ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
ctor public Condition(android.os.Parcel);
@@ -34606,7 +34608,7 @@
method public void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
- field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
+ field public static final java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
diff --git a/api/removed.txt b/api/removed.txt
index 73a439c..42c64ea 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -196,10 +196,6 @@
ctor public AudioFormat();
}
- public static abstract class AudioManager.AudioRecordingCallback {
- method public deprecated void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
- }
-
}
package android.media.tv {
diff --git a/api/system-current.txt b/api/system-current.txt
index e5dba78..d053103 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2136,6 +2136,8 @@
field public static final int TextAppearance_Material_Widget_ActionMode_Title = 16974355; // 0x1030213
field public static final int TextAppearance_Material_Widget_ActionMode_Title_Inverse = 16974356; // 0x1030214
field public static final int TextAppearance_Material_Widget_Button = 16974357; // 0x1030215
+ field public static final int TextAppearance_Material_Widget_Button_Borderless_Colored = 16974559; // 0x10302df
+ field public static final int TextAppearance_Material_Widget_Button_Colored = 16974558; // 0x10302de
field public static final int TextAppearance_Material_Widget_Button_Inverse = 16974548; // 0x10302d4
field public static final int TextAppearance_Material_Widget_DropDownHint = 16974358; // 0x1030216
field public static final int TextAppearance_Material_Widget_DropDownItem = 16974359; // 0x1030217
@@ -37292,7 +37294,7 @@
field public static final java.lang.String NEEDS_AUTOGROUPING_KEY = "autogroup_needed";
}
- public class Condition implements android.os.Parcelable {
+ public final class Condition implements android.os.Parcelable {
ctor public Condition(android.net.Uri, java.lang.String, int);
ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
ctor public Condition(android.os.Parcel);
@@ -37329,7 +37331,7 @@
method public void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
- field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
+ field public static final java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 0a758f8..4873605 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -194,10 +194,6 @@
ctor public AudioFormat();
}
- public static abstract class AudioManager.AudioRecordingCallback {
- method public deprecated void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
- }
-
}
package android.media.tv {
diff --git a/api/test-current.txt b/api/test-current.txt
index 1c21f72..4ade3d6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2024,6 +2024,8 @@
field public static final int TextAppearance_Material_Widget_ActionMode_Title = 16974355; // 0x1030213
field public static final int TextAppearance_Material_Widget_ActionMode_Title_Inverse = 16974356; // 0x1030214
field public static final int TextAppearance_Material_Widget_Button = 16974357; // 0x1030215
+ field public static final int TextAppearance_Material_Widget_Button_Borderless_Colored = 16974559; // 0x10302df
+ field public static final int TextAppearance_Material_Widget_Button_Colored = 16974558; // 0x10302de
field public static final int TextAppearance_Material_Widget_Button_Inverse = 16974548; // 0x10302d4
field public static final int TextAppearance_Material_Widget_DropDownHint = 16974358; // 0x1030216
field public static final int TextAppearance_Material_Widget_DropDownItem = 16974359; // 0x1030217
@@ -34646,7 +34648,7 @@
package android.service.notification {
- public class Condition implements android.os.Parcelable {
+ public final class Condition implements android.os.Parcelable {
ctor public Condition(android.net.Uri, java.lang.String, int);
ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
ctor public Condition(android.os.Parcel);
@@ -34683,7 +34685,7 @@
method public void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
- field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
+ field public static final java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 73a439c..42c64ea 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -196,10 +196,6 @@
ctor public AudioFormat();
}
- public static abstract class AudioManager.AudioRecordingCallback {
- method public deprecated void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
- }
-
}
package android.media.tv {
diff --git a/cmds/uiautomator/api/current.txt b/cmds/uiautomator/api/current.txt
index 7eeecf5..634ca4d 100644
--- a/cmds/uiautomator/api/current.txt
+++ b/cmds/uiautomator/api/current.txt
@@ -1,6 +1,6 @@
package com.android.uiautomator.core {
- public final class Configurator {
+ public final deprecated class Configurator {
method public long getActionAcknowledgmentTimeout();
method public static com.android.uiautomator.core.Configurator getInstance();
method public long getKeyInjectionDelay();
@@ -14,7 +14,7 @@
method public com.android.uiautomator.core.Configurator setWaitForSelectorTimeout(long);
}
- public class UiCollection extends com.android.uiautomator.core.UiObject {
+ public deprecated class UiCollection extends com.android.uiautomator.core.UiObject {
ctor public UiCollection(com.android.uiautomator.core.UiSelector);
method public com.android.uiautomator.core.UiObject getChildByDescription(com.android.uiautomator.core.UiSelector, java.lang.String) throws com.android.uiautomator.core.UiObjectNotFoundException;
method public com.android.uiautomator.core.UiObject getChildByInstance(com.android.uiautomator.core.UiSelector, int) throws com.android.uiautomator.core.UiObjectNotFoundException;
@@ -22,7 +22,7 @@
method public int getChildCount(com.android.uiautomator.core.UiSelector);
}
- public class UiDevice {
+ public deprecated class UiDevice {
method public void clearLastTraversedText();
method public boolean click(int, int);
method public boolean drag(int, int, int, int, int);
@@ -77,7 +77,7 @@
method public void wakeUp() throws android.os.RemoteException;
}
- public class UiObject {
+ public deprecated class UiObject {
ctor public UiObject(com.android.uiautomator.core.UiSelector);
method public void clearTextField() throws com.android.uiautomator.core.UiObjectNotFoundException;
method public boolean click() throws com.android.uiautomator.core.UiObjectNotFoundException;
@@ -130,13 +130,13 @@
field protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500L; // 0x157cL
}
- public class UiObjectNotFoundException extends java.lang.Exception {
+ public deprecated class UiObjectNotFoundException extends java.lang.Exception {
ctor public UiObjectNotFoundException(java.lang.String);
ctor public UiObjectNotFoundException(java.lang.String, java.lang.Throwable);
ctor public UiObjectNotFoundException(java.lang.Throwable);
}
- public class UiScrollable extends com.android.uiautomator.core.UiCollection {
+ public deprecated class UiScrollable extends com.android.uiautomator.core.UiCollection {
ctor public UiScrollable(com.android.uiautomator.core.UiSelector);
method protected boolean exists(com.android.uiautomator.core.UiSelector);
method public boolean flingBackward() throws com.android.uiautomator.core.UiObjectNotFoundException;
@@ -165,7 +165,7 @@
method public com.android.uiautomator.core.UiScrollable setSwipeDeadZonePercentage(double);
}
- public class UiSelector {
+ public deprecated class UiSelector {
ctor public UiSelector();
method public com.android.uiautomator.core.UiSelector checkable(boolean);
method public com.android.uiautomator.core.UiSelector checked(boolean);
@@ -198,7 +198,7 @@
method public com.android.uiautomator.core.UiSelector textStartsWith(java.lang.String);
}
- public abstract interface UiWatcher {
+ public abstract deprecated interface UiWatcher {
method public abstract boolean checkForCondition();
}
@@ -206,11 +206,11 @@
package com.android.uiautomator.testrunner {
- public abstract interface IAutomationSupport {
+ public abstract deprecated interface IAutomationSupport {
method public abstract void sendStatus(int, android.os.Bundle);
}
- public class UiAutomatorTestCase extends junit.framework.TestCase {
+ public deprecated class UiAutomatorTestCase extends junit.framework.TestCase {
ctor public UiAutomatorTestCase();
method public com.android.uiautomator.testrunner.IAutomationSupport getAutomationSupport();
method public android.os.Bundle getParams();
diff --git a/cmds/uiautomator/cmds/uiautomator/uiautomator b/cmds/uiautomator/cmds/uiautomator/uiautomator
index fe2c735..86a1dba 100755
--- a/cmds/uiautomator/cmds/uiautomator/uiautomator
+++ b/cmds/uiautomator/cmds/uiautomator/uiautomator
@@ -67,6 +67,11 @@
# special case pre-processing for 'runtest' command
if [ "${cmd}" == "runtest" ]; then
+ # Print deprecation warning
+ echo "Warning: This version of UI Automator is deprecated. New tests should be written using"
+ echo "UI Automator 2.0 which is available as part of the Android Testing Support Library."
+ echo "See https://developer.android.com/training/testing/ui-testing/uiautomator-testing.html"
+ echo "for more details."
# first parse the jar paths
while [ true ]; do
if [ -z "${1}" ] && [ -z "${jars}" ]; then
diff --git a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
index f0c60d2..6d75844 100644
--- a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
+++ b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
@@ -22,7 +22,10 @@
* Provides auxiliary support for running test cases
*
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public interface IAutomationSupport {
/**
diff --git a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
index ae763f2..af42653 100644
--- a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
+++ b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
@@ -28,7 +28,10 @@
/**
* Test runner for {@link UiAutomatorTestCase}s. Such tests are executed
* on the device and have access to an applications context.
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiAutomatorInstrumentationTestRunner extends InstrumentationTestRunner {
@Override
diff --git a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index b5f21c9..7c9aede 100644
--- a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -26,7 +26,10 @@
/**
* UI Automator test case that is executed on the device.
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiAutomatorTestCase extends InstrumentationTestCase {
private Bundle mParams;
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index 62fb0e4..ea77fbc 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -67,7 +67,6 @@
# Make sure to run droiddoc first to generate the stub source files.
$(full_classes_compiled_jar) : $(uiautomator_stubs_stamp)
$(built_dex_intermediate) : $(uiautomator_stubs_stamp)
-uiautomator_stubs_jar := $(full_classes_compiled_jar)
###############################################
# API check
@@ -92,7 +91,7 @@
$(uiautomator_internal_removed_api_file), \
$(checkapi_last_error_level_flags), \
cat $(LOCAL_PATH)/apicheck_msg_last.txt, \
- $(uiautomator_stubs_jar), \
+ uiautomator.core, \
$(uiautomator_stubs_stamp)))
checkapi_current_error_level_flags := \
@@ -111,7 +110,7 @@
$(uiautomator_internal_removed_api_file), \
$(checkapi_current_error_level_flags), \
cat $(LOCAL_PATH)/apicheck_msg_current.txt, \
- $(uiautomator_stubs_jar), \
+ uiautomator.core, \
$(uiautomator_stubs_stamp)))
.PHONY: update-uiautomator-api
@@ -128,7 +127,6 @@
uiautomator.core_java_libraries :=
uiautomator_stubs_stamp :=
uiautomator_internal_api_file :=
-uiautomator_stubs_jar :=
uiautomator_api_dir :=
checkapi_last_error_level_flags :=
checkapi_current_error_level_flags :=
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
index 249f404..255c49a 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
@@ -25,7 +25,10 @@
* tests with the modified parameters, make sure to also restore
* the original parameter values, otherwise this will impact other tests cases.
* @since API Level 18
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public final class Configurator {
private long mWaitForIdleTimeout = 10 * 1000;
private long mWaitForSelector = 10 * 1000;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
index e15beb2..06bacb0 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
@@ -19,7 +19,10 @@
* Used to enumerate a container's UI elements for the purpose of counting,
* or targeting a sub elements by a child's text or description.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiCollection extends UiObject {
/**
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
index a930eb4..b1b432b 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
@@ -42,7 +42,10 @@
* You can also use this class to simulate user actions on the device,
* such as pressing the d-pad or pressing the Home and Menu buttons.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiDevice {
private static final String LOG_TAG = UiDevice.class.getSimpleName();
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
index 2118da8..751bbe8 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
@@ -31,7 +31,10 @@
* its constructor. Once you create an instance of a UiObject, it can
* be reused for different views that match the selector criteria.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiObject {
private static final String LOG_TAG = UiObject.class.getSimpleName();
/**
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
index fc0891b..f3e2698 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
@@ -20,7 +20,10 @@
* Generated in test runs when a {@link UiSelector} selector could not be matched
* to any UI element displayed.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiObjectNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
index a8d20c3..6673dac 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
@@ -24,7 +24,10 @@
* for items in scrollable layout elements. This class can be used with
* horizontally or vertically scrollable controls.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiScrollable extends UiCollection {
private static final String LOG_TAG = UiScrollable.class.getSimpleName();
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
index 482a74d..9ba425e 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
@@ -27,7 +27,10 @@
* information. You can also target an element by its location in a layout
* hierarchy.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiSelector {
static final int SELECTOR_NIL = 0;
static final int SELECTOR_TEXT = 1;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
index 5403e30..030fb0a 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
@@ -22,7 +22,10 @@
* to locate its selector yet. Only during this time, the watchers are invoked to check if there is
* something else unexpected on the screen.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public interface UiWatcher {
/**
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
index f0c60d2..6d75844 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
@@ -22,7 +22,10 @@
* Provides auxiliary support for running test cases
*
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public interface IAutomationSupport {
/**
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index e7d961b..3d5476d 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -36,7 +36,10 @@
* {@link UiDevice} instance
* {@link Bundle} for command line parameters.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiAutomatorTestCase extends TestCase {
private static final String DISABLE_IME = "disable_ime";
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 8ff38bb..7841d29 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -465,20 +465,26 @@
/**
* The amount of time, in milliseconds, to delay starting the animation after
- * {@link #start()} is called.
-
+ * {@link #start()} is called. Note that the start delay should always be non-negative. Any
+ * negative start delay will be clamped to 0 on N and above.
+ *
* @param startDelay The amount of the delay, in milliseconds
*/
@Override
public void setStartDelay(long startDelay) {
- if (mStartDelay > 0) {
- mReversible = false;
+ // Clamp start delay to non-negative range.
+ if (startDelay < 0) {
+ Log.w(TAG, "Start delay should always be non-negative");
+ startDelay = 0;
}
long delta = startDelay - mStartDelay;
if (delta == 0) {
return;
}
mStartDelay = startDelay;
+ if (mStartDelay > 0) {
+ mReversible = false;
+ }
if (!mDependencyDirty) {
// Dependency graph already constructed, update all the nodes' start/end time
int size = mNodes.size();
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 0a9b5de..4edf249 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -708,12 +708,18 @@
/**
* The amount of time, in milliseconds, to delay starting the animation after
- * {@link #start()} is called.
-
+ * {@link #start()} is called. Note that the start delay should always be non-negative. Any
+ * negative start delay will be clamped to 0 on N and above.
+ *
* @param startDelay The amount of the delay, in milliseconds
*/
@Override
public void setStartDelay(long startDelay) {
+ // Clamp start delay to non-negative range.
+ if (startDelay < 0) {
+ Log.w(TAG, "Start delay should always be non-negative");
+ startDelay = 0;
+ }
mStartDelay = startDelay;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dff0769..30753c1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -62,6 +62,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
@@ -130,6 +131,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
@@ -4684,6 +4686,8 @@
+ config);
mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
+ updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+ mResourcesManager.getConfiguration().getLocales());
if (mConfiguration == null) {
mConfiguration = new Configuration();
@@ -4989,6 +4993,24 @@
return insInfo.nativeLibraryDir;
}
+ /**
+ * The LocaleList set for the app's resources may have been shuffled so that the preferred
+ * Locale is at position 0. We must find the index of this preferred Locale in the
+ * original LocaleList.
+ */
+ private void updateLocaleListFromAppContext(Context context, LocaleList newLocaleList) {
+ final Locale bestLocale = context.getResources().getConfiguration().getLocales().get(0);
+ final int newLocaleListSize = newLocaleList.size();
+ for (int i = 0; i < newLocaleListSize; i++) {
+ if (bestLocale.equals(newLocaleList.get(i))) {
+ LocaleList.setDefault(newLocaleList, i);
+ return;
+ }
+ }
+ throw new AssertionError("chosen locale " + bestLocale + " must be present in LocaleList: "
+ + newLocaleList.toLanguageTags());
+ }
+
private void handleBindApplication(AppBindData data) {
// Register the UI Thread as a sensitive thread to the runtime.
VMRuntime.registerSensitiveThread();
@@ -5047,6 +5069,24 @@
*/
TimeZone.setDefault(null);
+ /*
+ * Set the LocaleList. This may change once we create the App Context.
+ */
+ LocaleList.setDefault(data.config.getLocales());
+
+ synchronized (mResourcesManager) {
+ /*
+ * Update the system configuration since its preloaded and might not
+ * reflect configuration changes. The configuration object passed
+ * in AppBindData can be safely assumed to be up to date
+ */
+ mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+ mCurDefaultDisplayDpi = data.config.densityDpi;
+
+ // This calls mResourcesManager so keep it within the synchronized block.
+ applyCompatConfiguration(mCurDefaultDisplayDpi);
+ }
+
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
/**
@@ -5174,25 +5214,8 @@
}
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
- synchronized (mResourcesManager) {
- /*
- * Initialize the default locales in this process for the reasons we set the time zone.
- *
- * We do this through ResourcesManager, since we need to do locale negotiation.
- */
- mResourcesManager.setDefaultLocalesLocked(data.config.getLocales());
-
- /*
- * Update the system configuration since its preloaded and might not
- * reflect configuration changes. The configuration object passed
- * in AppBindData can be safely assumed to be up to date
- */
- mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
- mCurDefaultDisplayDpi = data.config.densityDpi;
-
- // This calls mResourcesManager so keep it within the synchronized block.
- applyCompatConfiguration(mCurDefaultDisplayDpi);
- }
+ updateLocaleListFromAppContext(appContext,
+ mResourcesManager.getConfiguration().getLocales());
if (!Process.isIsolated() && !"android".equals(appContext.getPackageName())) {
// This cache location probably points at credential-encrypted
@@ -5895,6 +5918,9 @@
// immediately, because upon returning the view
// hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
+ updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+ mResourcesManager.getConfiguration().getLocales());
+
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index b4e9db8..25a8b66 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -67,10 +67,6 @@
}
};
- private String[] mSystemLocales = null;
- private final HashSet<String> mNonSystemLocales = new HashSet<>();
- private boolean mHasNonSystemLocales = false;
-
/**
* The global compatibility settings.
*/
@@ -479,12 +475,7 @@
*/
private Resources getOrCreateResources(@Nullable IBinder activityToken,
@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
- final boolean findSystemLocales;
- final boolean hasNonSystemLocales;
synchronized (this) {
- findSystemLocales = (mSystemLocales == null || mSystemLocales.length == 0);
- hasNonSystemLocales = mHasNonSystemLocales;
-
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
@@ -538,24 +529,7 @@
// If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
ResourcesImpl resourcesImpl = createResourcesImpl(key);
- final String[] systemLocales = findSystemLocales
- ? AssetManager.getSystem().getLocales() : null;
- final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
-
- // Avoid checking for non-pseudo-locales if we already know there were some from a previous
- // Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
- // since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
- // able to affect mHasNonSystemLocales.
- final boolean isPseudoLocalesOnly = hasNonSystemLocales ||
- LocaleList.isPseudoLocalesOnly(nonSystemLocales);
-
synchronized (this) {
- if (mSystemLocales == null || mSystemLocales.length == 0) {
- mSystemLocales = systemLocales;
- }
- mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
- mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
-
ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
if (existingResourcesImpl != null) {
if (DEBUG) {
@@ -745,23 +719,6 @@
}
}
- /* package */ void setDefaultLocalesLocked(@NonNull LocaleList locales) {
- if (mSystemLocales == null) {
- throw new RuntimeException("ResourcesManager is not ready to negotiate locales.");
- }
- final int bestLocale;
- if (mHasNonSystemLocales) {
- bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mNonSystemLocales);
- } else {
- // We fallback to system locales if there was no locale specifically supported by the
- // assets. This is to properly support apps that only rely on the shared system assets
- // and don't need assets of their own.
- bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mSystemLocales);
- }
- // set it for Java, this also affects newly created Resources
- LocaleList.setDefault(locales, bestLocale);
- }
-
public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
try {
@@ -786,30 +743,7 @@
| ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
}
- Configuration localeAdjustedConfig = config;
- final LocaleList configLocales = config.getLocales();
- if (!configLocales.isEmpty()) {
- setDefaultLocalesLocked(configLocales);
- final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
- if (adjustedLocales
- != configLocales) { // has the same result as .equals() in this case
- // The first locale in the list was not chosen. So we create a modified
- // configuration with the adjusted locales (which moves the chosen locale to the
- // front).
- localeAdjustedConfig = new Configuration();
- localeAdjustedConfig.setTo(config);
- localeAdjustedConfig.setLocales(adjustedLocales);
- // Also adjust the locale list in mResConfiguration, so that the Resources
- // created later would have the same locale list.
- if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
- mResConfiguration.setLocales(adjustedLocales);
- changes |= ActivityInfo.CONFIG_LOCALE;
- }
- }
- }
-
- Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics,
- compat);
+ Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
ApplicationPackageManager.configurationChanged();
//Slog.i(TAG, "Configuration changed in " + currentPackageName());
@@ -821,7 +755,7 @@
ResourcesImpl r = mResourceImpls.valueAt(i).get();
if (r != null) {
if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
- + r + " config to: " + localeAdjustedConfig);
+ + r + " config to: " + config);
int displayId = key.mDisplayId;
boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
DisplayMetrics dm = defaultDisplayMetrics;
@@ -830,7 +764,7 @@
if (tmpConfig == null) {
tmpConfig = new Configuration();
}
- tmpConfig.setTo(localeAdjustedConfig);
+ tmpConfig.setTo(config);
if (!isDefaultDisplay) {
dm = getDisplayMetrics(displayId);
applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
@@ -840,7 +774,7 @@
}
r.updateConfiguration(tmpConfig, dm, compat);
} else {
- r.updateConfiguration(localeAdjustedConfig, dm, compat);
+ r.updateConfiguration(config, dm, compat);
}
//Slog.i(TAG, "Updated app resources " + v.getKey()
// + " " + r + ": " + r.getConfiguration());
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a672d80..63afdd8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2191,7 +2191,8 @@
* <em>Note: This API has been limited as of {@link android.os.Build.VERSION_CODES#N} for
* device admins that are not device owner and not profile owner.
* The password can now only be changed if there is currently no password set. Device owner
- * and profile owner can still do this.</em>
+ * and profile owner can still do this when user is unlocked and does not have a managed
+ * profile.</em>
* <p>
* The given password must be sufficient for the current password quality and length constraints
* as returned by {@link #getPasswordQuality(ComponentName)} and
@@ -2217,6 +2218,7 @@
* current constraints or if the user has not been decrypted yet.
* @throws SecurityException if the calling application does not own an active administrator
* that uses {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD}
+ * @throws IllegalStateException if the calling user is locked or has a managed profile.
*/
public boolean resetPassword(String password, int flags) {
throwIfParentInstance("resetPassword");
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 4c86578..9390bcd 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2045,12 +2045,15 @@
public void onBluetoothServiceDown() {
if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
- mServiceLock.writeLock().lock();
- mService = null;
- if (mLeScanClients != null) mLeScanClients.clear();
- if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup();
- if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup();
- mServiceLock.writeLock().unlock();
+ try {
+ mServiceLock.writeLock().lock();
+ mService = null;
+ if (mLeScanClients != null) mLeScanClients.clear();
+ if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup();
+ if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup();
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
synchronized (mProxyServiceStateCallbacks) {
for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index c1aac85..f6445e6 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1446,7 +1446,7 @@
*
* @return The locale list.
*/
- public LocaleList getLocales() {
+ public @NonNull LocaleList getLocales() {
fixUpLocaleList();
return mLocaleList;
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index dada612..32a2795 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -330,18 +330,43 @@
// doing the conversion here. This impl should be okay because
// we make sure to return a compatible display in the places
// where there are public APIs to retrieve the display... but
- // it would be cleaner and more maintainble to just be
+ // it would be cleaner and more maintainable to just be
// consistently dealing with a compatible display everywhere in
// the framework.
mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
final @Config int configChanges = calcConfigChanges(config);
+ // If even after the update there are no Locales set, grab the default locales.
LocaleList locales = mConfiguration.getLocales();
if (locales.isEmpty()) {
- locales = LocaleList.getAdjustedDefault();
+ locales = LocaleList.getDefault();
mConfiguration.setLocales(locales);
}
+
+ if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
+ if (locales.size() > 1) {
+ // The LocaleList has changed. We must query the AssetManager's available
+ // Locales and figure out the best matching Locale in the new LocaleList.
+ String[] availableLocales = mAssets.getNonSystemLocales();
+ if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+ // No app defined locales, so grab the system locales.
+ availableLocales = mAssets.getLocales();
+ if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+ availableLocales = null;
+ }
+ }
+
+ if (availableLocales != null) {
+ final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
+ availableLocales);
+ if (bestLocale != null && bestLocale != locales.get(0)) {
+ mConfiguration.setLocales(new LocaleList(bestLocale, locales));
+ }
+ }
+ }
+ }
+
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
mMetrics.densityDpi = mConfiguration.densityDpi;
mMetrics.density =
@@ -370,7 +395,7 @@
}
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
- adjustLanguageTag(locales.get(0).toLanguageTag()),
+ adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
mConfiguration.orientation,
mConfiguration.touchscreen,
mConfiguration.densityDpi, mConfiguration.keyboard,
diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java
index 6138b83..f8b5992 100644
--- a/core/java/android/net/metrics/DefaultNetworkEvent.java
+++ b/core/java/android/net/metrics/DefaultNetworkEvent.java
@@ -68,8 +68,28 @@
@Override
public String toString() {
- return String.format("DefaultNetworkEvent(%d -> %d, %s, IPv4: %b, IPv6: %b)", prevNetId,
- netId, NetworkCapabilities.transportNamesOf(transportTypes), prevIPv4, prevIPv6);
+ String prevNetwork = String.valueOf(prevNetId);
+ String newNetwork = String.valueOf(netId);
+ if (prevNetId != 0) {
+ prevNetwork += ":" + ipSupport();
+ }
+ if (netId != 0) {
+ newNetwork += ":" + NetworkCapabilities.transportNamesOf(transportTypes);
+ }
+ return String.format("DefaultNetworkEvent(%s -> %s)", prevNetwork, newNetwork);
+ }
+
+ private String ipSupport() {
+ if (prevIPv4 && prevIPv6) {
+ return "DUAL";
+ }
+ if (prevIPv6) {
+ return "IPv6";
+ }
+ if (prevIPv4) {
+ return "IPv4";
+ }
+ return "NONE";
}
public static final Parcelable.Creator<DefaultNetworkEvent> CREATOR
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 8136796..2dc3beb 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -148,17 +148,6 @@
}
/**
- * It is almost always better to call {@link #getEmptyLocaleList()} instead which returns
- * a pre-constructed empty locale list.
- *
- * @hide
- */
- public LocaleList() {
- mList = sEmptyList;
- mStringRepresentation = "";
- }
-
- /**
* Creates a new {@link LocaleList}.
*
* <p>For empty lists of {@link Locale} items it is better to use {@link #getEmptyLocaleList()},
@@ -409,6 +398,14 @@
}
/**
+ * {@hide}
+ */
+ public int getFirstMatchIndex(String[] supportedLocales) {
+ return computeFirstMatchIndex(Arrays.asList(supportedLocales),
+ false /* assume English is not supported */);
+ }
+
+ /**
* Same as getFirstMatch(), but with English assumed to be supported, even if it's not.
* {@hide}
*/
@@ -437,7 +434,11 @@
* Assumes that there is no repetition in the input.
* {@hide}
*/
- public static boolean isPseudoLocalesOnly(String[] supportedLocales) {
+ public static boolean isPseudoLocalesOnly(@Nullable String[] supportedLocales) {
+ if (supportedLocales == null) {
+ return true;
+ }
+
if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) {
// This is for optimization. Since there's no repetition in the input, if we have more
// than the number of pseudo-locales plus one for the empty string, it's guaranteed
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 403e06c..4abbf0e 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -25,8 +25,10 @@
import android.util.Log;
import java.io.ByteArrayInputStream;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
@@ -45,6 +47,8 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import com.android.internal.logging.MetricsLogger;
+
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.SignerInfo;
@@ -69,6 +73,7 @@
/** Used to communicate with recovery. See bootable/recovery/recovery.cpp. */
private static final File RECOVERY_DIR = new File("/cache/recovery");
private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
+ private static final File LAST_INSTALL_FILE = new File(RECOVERY_DIR, "last_install");
private static final String LAST_PREFIX = "last_";
/**
@@ -682,13 +687,64 @@
}
}
+ // Read last_install; then report time for update and I/O to tron.
+ // Only report on the reboots immediately after an OTA update.
+ private static void parseLastInstallLog(Context context) {
+ try (BufferedReader in = new BufferedReader(new FileReader(LAST_INSTALL_FILE))) {
+ String line = null;
+ int bytesWritten = -1, bytesStashed = -1;
+ int timeTotal = -1;
+ while ((line = in.readLine()) != null) {
+ // Here is an example of lines in last_install:
+ // ...
+ // time_total: 101
+ // bytes_written_vendor: 51074
+ // bytes_stashed_vendor: 200
+ int numIndex = line.indexOf(':');
+ if (numIndex == -1 || numIndex + 1 >= line.length()) {
+ continue;
+ }
+ String numString = line.substring(numIndex + 1).trim();
+ int parsedNum;
+ try {
+ parsedNum = Integer.parseInt(numString);
+ } catch (NumberFormatException ignored) {
+ Log.e(TAG, "Failed to parse numbers in " + line);
+ continue;
+ }
+
+ if (line.startsWith("time")) {
+ timeTotal = parsedNum;
+ } else if (line.startsWith("bytes_written")) {
+ bytesWritten = (bytesWritten == -1) ? parsedNum : bytesWritten + parsedNum;
+ } else if (line.startsWith("bytes_stashed")) {
+ bytesStashed = (bytesStashed == -1) ? parsedNum : bytesStashed + parsedNum;
+ }
+ }
+
+ // Don't report data to tron if corresponding entry isn't found in last_install.
+ if (timeTotal != -1) {
+ MetricsLogger.histogram(context, "ota_time_total", timeTotal);
+ }
+ if (bytesWritten != -1) {
+ MetricsLogger.histogram(context, "ota_bytes_written", bytesWritten);
+ }
+ if (bytesStashed != -1) {
+ MetricsLogger.histogram(context, "ota_bytes_stashed", bytesStashed);
+ }
+
+ } catch (IOException ignored) {
+ Log.e(TAG, "Failed to read lines in last_install", ignored);
+ }
+ }
+
/**
* Called after booting to process and remove recovery-related files.
* @return the log file from recovery, or null if none was found.
*
* @hide
*/
- public static String handleAftermath() {
+ public static String handleAftermath(Context context) {
// Record the tail of the LOG_FILE
String log = null;
try {
@@ -699,6 +755,10 @@
Log.e(TAG, "Error reading recovery log", e);
}
+ if (log != null) {
+ parseLastInstallLog(context);
+ }
+
// Only remove the OTA package if it's partially processed (uncrypt'd).
boolean reservePackage = BLOCK_MAP_FILE.exists();
if (!reservePackage && UNCRYPT_PACKAGE_FILE.exists()) {
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 0163b47..447afe6 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -33,7 +33,7 @@
* {@link ConditionProviderService} that owns the rule. Used to tell the system to enter Do Not
* Disturb mode and request that the system exit Do Not Disturb mode.
*/
-public class Condition implements Parcelable {
+public final class Condition implements Parcelable {
@SystemApi
public static final String SCHEME = "condition";
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 44c3887..9d4b0a4 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -95,7 +95,7 @@
/**
* A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
*/
- public static final String EXTRA_RULE_ID = "android.content.automatic.ruleId";
+ public static final String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
/**
* Called when this service is connected.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 16187589..4013b30 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1277,6 +1277,28 @@
return true;
}
}
+ } else {
+ if (startInputReason
+ == InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN) {
+ // We are here probably because of an obsolete window-focus-in message sent
+ // to windowGainingFocus. Since IMMS determines whether a Window can have
+ // IME focus or not by using the latest window focus state maintained in the
+ // WMS, this kind of race condition cannot be avoided. One obvious example
+ // would be that we have already received a window-focus-out message but the
+ // UI thread is still handling previous window-focus-in message here.
+ // TODO: InputBindResult should have the error code.
+ if (DEBUG) Log.w(TAG, "startInputOrWindowGainedFocus failed. "
+ + "Window focus may have already been lost. "
+ + "win=" + windowGainingFocus + " view=" + dumpViewInfo(view));
+ if (!mActive) {
+ // mHasBeenInactive is a latch switch to forcefully refresh IME focus
+ // state when an inactive (mActive == false) client is gaining window
+ // focus. In case we have unnecessary disable the latch due to this
+ // spurious wakeup, we re-enable the latch here.
+ // TODO: Come up with more robust solution.
+ mHasBeenInactive = true;
+ }
+ }
}
if (mCurMethod != null && mCompletions != null) {
try {
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 66896ab..ac44033 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -16,6 +16,8 @@
package android.widget;
+import com.android.internal.R;
+
import android.annotation.AttrRes;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -27,17 +29,16 @@
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.icu.util.Calendar;
+import android.icu.util.TimeZone;
import android.util.AttributeSet;
import android.util.Log;
-import com.android.internal.R;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
-import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
-import java.util.TimeZone;
/**
* This class is a calendar widget for displaying and selecting dates. The
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index f540479..2b91713 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -27,6 +27,7 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.icu.util.Calendar;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -37,7 +38,6 @@
import android.view.View;
import android.view.ViewGroup;
-import java.util.Calendar;
import java.util.Locale;
import libcore.icu.LocaleData;
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
index 0ed75d5..07b2000 100644
--- a/core/java/android/widget/CalendarViewMaterialDelegate.java
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -18,11 +18,10 @@
import android.annotation.StyleRes;
import android.content.Context;
+import android.icu.util.Calendar;
import android.util.AttributeSet;
import android.widget.DayPickerView.OnDaySelectedListener;
-import java.util.Calendar;
-
class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDelegate {
private final DayPickerView mDayPickerView;
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 0c5edc5..8613f99 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -16,11 +16,15 @@
package android.widget;
+import com.android.internal.R;
+
import android.annotation.Nullable;
import android.annotation.Widget;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
+import android.icu.util.Calendar;
+import android.icu.util.TimeZone;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -28,11 +32,7 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.R;
-
-import java.util.Calendar;
import java.util.Locale;
-import java.util.TimeZone;
/**
* Provides a widget for selecting a date.
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 0a23b34..2c80f28 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -26,6 +26,7 @@
import android.content.res.TypedArray;
import android.icu.text.DisplayContext;
import android.icu.text.SimpleDateFormat;
+import android.icu.util.Calendar;
import android.os.Parcelable;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
@@ -40,7 +41,6 @@
import android.widget.DayPickerView.OnDaySelectedListener;
import android.widget.YearPickerView.OnYearSelectedListener;
-import java.util.Calendar;
import java.util.Locale;
/**
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index d8a3c56..17af014 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
+import android.icu.util.Calendar;
import android.os.Parcelable;
import android.text.InputType;
import android.text.TextUtils;
@@ -37,7 +38,6 @@
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Locale;
import libcore.icu.ICU;
diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
index 97936e7..9f02b44 100644
--- a/core/java/android/widget/DayPickerPagerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -25,14 +25,13 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
+import android.icu.util.Calendar;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SimpleMonthView.OnDayClickListener;
-import java.util.Calendar;
-
/**
* An adapter for a list of {@link android.widget.SimpleMonthView} items.
*/
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index dc772fb..ab89c1f 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
+import android.icu.util.Calendar;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.view.LayoutInflater;
@@ -31,7 +32,6 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
-import java.util.Calendar;
import java.util.Locale;
import libcore.icu.LocaleData;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 622567c..15bba3a 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -76,6 +76,7 @@
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.ContextMenu;
+import android.view.ContextThemeWrapper;
import android.view.DisplayListCanvas;
import android.view.DragAndDropPermissions;
import android.view.DragEvent;
@@ -3004,8 +3005,16 @@
protected abstract int getTextOffset();
protected abstract int getVerticalLocalPosition(int line);
protected abstract int clipVertically(int positionY);
+ protected void setUp() {
+ }
public PinnedPopupWindow() {
+ // Due to calling subclass methods in base constructor, subclass constructor is not
+ // called before subclass methods, e.g. createPopupWindow or initContentView. To give
+ // a chance to initialize subclasses, call setUp() method here.
+ // TODO: It is good to extract non trivial initialization code from constructor.
+ setUp();
+
createPopupWindow();
mPopupWindow.setWindowLayoutType(
@@ -3267,8 +3276,7 @@
private boolean mCursorWasVisibleBeforeSuggestions;
private boolean mIsShowingUp = false;
private SuggestionAdapter mSuggestionsAdapter;
- private final TextAppearanceSpan mHighlightSpan = new TextAppearanceSpan(
- mTextView.getContext(), mTextView.mTextEditSuggestionHighlightStyle);
+ private TextAppearanceSpan mHighlightSpan; // TODO: Make mHighlightSpan final.
private TextView mAddToDictionaryButton;
private TextView mDeleteButton;
private ListView mSuggestionListView;
@@ -3276,8 +3284,10 @@
private int mContainerMarginWidth;
private int mContainerMarginTop;
private LinearLayout mContainerView;
+ private Context mContext; // TODO: Make mContext final.
private class CustomPopupWindow extends PopupWindow {
+
@Override
public void dismiss() {
if (!isShowing()) {
@@ -3301,6 +3311,23 @@
}
@Override
+ protected void setUp() {
+ mContext = applyDefaultTheme(mTextView.getContext());
+ mHighlightSpan = new TextAppearanceSpan(mContext,
+ mTextView.mTextEditSuggestionHighlightStyle);
+ }
+
+ private Context applyDefaultTheme(Context originalContext) {
+ TypedArray a = originalContext.obtainStyledAttributes(
+ new int[]{com.android.internal.R.attr.isLightTheme});
+ boolean isLightTheme = a.getBoolean(0, true);
+ int themeId = isLightTheme ? R.style.ThemeOverlay_Material_Light
+ : R.style.ThemeOverlay_Material_Dark;
+ a.recycle();
+ return new ContextThemeWrapper(originalContext, themeId);
+ }
+
+ @Override
protected void createPopupWindow() {
mPopupWindow = new CustomPopupWindow();
mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
@@ -3311,8 +3338,8 @@
@Override
protected void initContentView() {
- final LayoutInflater inflater = (LayoutInflater) mTextView.getContext().
- getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
mContentView = (ViewGroup) inflater.inflate(
mTextView.mTextEditSuggestionContainerLayout, null);
@@ -3405,8 +3432,8 @@
}
private class SuggestionAdapter extends BaseAdapter {
- private LayoutInflater mInflater = (LayoutInflater) mTextView.getContext().
- getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ private LayoutInflater mInflater = (LayoutInflater) mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
@Override
public int getCount() {
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 97d834a..588425f 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -32,6 +32,7 @@
import android.graphics.Typeface;
import android.icu.text.DisplayContext;
import android.icu.text.SimpleDateFormat;
+import android.icu.util.Calendar;
import android.os.Bundle;
import android.text.TextPaint;
import android.text.format.DateFormat;
@@ -48,7 +49,6 @@
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import java.text.NumberFormat;
-import java.util.Calendar;
import java.util.Locale;
import libcore.icu.LocaleData;
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 96624d2..a3f5a67 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -16,18 +16,17 @@
package android.widget;
+import com.android.internal.R;
+
import android.content.Context;
import android.content.res.Resources;
+import android.icu.util.Calendar;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import java.util.Calendar;
-
-import com.android.internal.R;
-
/**
* Displays a selectable list of years.
*/
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index 07f3801..ddf3a76 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -231,6 +231,7 @@
popupWindow.setAnchorView(mAnchorView);
popupWindow.setDropDownGravity(mDropDownGravity);
popupWindow.setModal(true);
+ popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
return popupWindow;
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a5485da..70941b6 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -141,6 +141,9 @@
// consider it a complex PIN/password.
public static final int MAX_ALLOWED_SEQUENCE = 3;
+ public static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
+ public static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
+
private final Context mContext;
private final ContentResolver mContentResolver;
private DevicePolicyManager mDevicePolicyManager;
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 6d6c162..fbc51cd 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -127,7 +127,7 @@
.append("\n").toString();
final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
- String recovery = RecoverySystem.handleAftermath();
+ String recovery = RecoverySystem.handleAftermath(ctx);
if (recovery != null && db != null) {
db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c2ff4ef..6c289dc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -273,6 +273,9 @@
<protected-broadcast android:name="com.android.server.WifiManager.action.DEVICE_IDLE" />
<protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED" />
<protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED" />
+ <protected-broadcast android:name="com.android.server.usb.ACTION_OPEN_IN_APPS" />
+ <protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" />
+ <protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" />
<protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
@@ -322,6 +325,9 @@
<protected-broadcast android:name="android.net.proxy.PAC_REFRESH" />
<protected-broadcast android:name="android.telecom.action.DEFAULT_DIALER_CHANGED" />
+ <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.CONTENT_CHANGED" />
+ <protected-broadcast android:name="android.provider.Telephony.MMS_DOWNLOADED" />
<protected-broadcast
android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
@@ -384,6 +390,7 @@
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_REMOVED" />
<protected-broadcast android:name="android.bluetooth.adapter.action.BLE_STATE_CHANGED" />
+ <protected-broadcast android:name="com.android.bluetooth.map.USER_CONFIRM_TIMEOUT" />
<protected-broadcast android:name="android.content.jobscheduler.JOB_DELAY_EXPIRED" />
<protected-broadcast android:name="android.content.syncmanager.SYNC_ALARM" />
<protected-broadcast android:name="android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION" />
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 30e23f5..e190bd1 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -22,296 +22,120 @@
<!-- Do not translate. These are all of the drawable resources that should be preloaded by
the zygote process before it starts forking application processes. -->
<array name="preloaded_drawables">
- <item>@drawable/toast_frame</item>
- <item>@drawable/btn_check_on_pressed_holo_light</item>
- <item>@drawable/btn_check_on_pressed_holo_dark</item>
- <item>@drawable/btn_check_on_holo_light</item>
- <item>@drawable/btn_check_on_holo_dark</item>
- <item>@drawable/btn_check_on_focused_holo_light</item>
- <item>@drawable/btn_check_on_focused_holo_dark</item>
- <item>@drawable/btn_check_on_disabled_holo_light</item>
- <item>@drawable/btn_check_on_disabled_holo_dark</item>
- <item>@drawable/btn_check_on_disabled_focused_holo_light</item>
- <item>@drawable/btn_check_on_disabled_focused_holo_dark</item>
- <item>@drawable/btn_check_off_pressed_holo_light</item>
- <item>@drawable/btn_check_off_pressed_holo_dark</item>
- <item>@drawable/btn_check_off_holo_light</item>
- <item>@drawable/btn_check_off_holo_dark</item>
- <item>@drawable/btn_check_off_focused_holo_light</item>
- <item>@drawable/btn_check_off_focused_holo_dark</item>
- <item>@drawable/btn_check_off_disabled_holo_light</item>
- <item>@drawable/btn_check_off_disabled_holo_dark</item>
- <item>@drawable/btn_check_off_disabled_focused_holo_light</item>
- <item>@drawable/btn_check_off_disabled_focused_holo_dark</item>
- <item>@drawable/btn_check_holo_light</item>
- <item>@drawable/btn_check_holo_dark</item>
- <item>@drawable/btn_radio_on_pressed_holo_light</item>
- <item>@drawable/btn_radio_on_pressed_holo_dark</item>
- <item>@drawable/btn_radio_on_holo_light</item>
- <item>@drawable/btn_radio_on_holo_dark</item>
- <item>@drawable/btn_radio_on_focused_holo_light</item>
- <item>@drawable/btn_radio_on_focused_holo_dark</item>
- <item>@drawable/btn_radio_on_disabled_holo_light</item>
- <item>@drawable/btn_radio_on_disabled_holo_dark</item>
- <item>@drawable/btn_radio_on_disabled_focused_holo_light</item>
- <item>@drawable/btn_radio_on_disabled_focused_holo_dark</item>
- <item>@drawable/btn_radio_off_pressed_holo_light</item>
- <item>@drawable/btn_radio_off_pressed_holo_dark</item>
- <item>@drawable/btn_radio_off_holo_light</item>
- <item>@drawable/btn_radio_off_holo_dark</item>
- <item>@drawable/btn_radio_off_focused_holo_light</item>
- <item>@drawable/btn_radio_off_focused_holo_dark</item>
- <item>@drawable/btn_radio_off_disabled_holo_light</item>
- <item>@drawable/btn_radio_off_disabled_holo_dark</item>
- <item>@drawable/btn_radio_off_disabled_focused_holo_light</item>
- <item>@drawable/btn_radio_off_disabled_focused_holo_dark</item>
- <item>@drawable/btn_default_pressed_holo_light</item>
- <item>@drawable/btn_default_pressed_holo_dark</item>
- <item>@drawable/btn_default_normal_holo_light</item>
- <item>@drawable/btn_default_normal_holo_dark</item>
- <item>@drawable/btn_default_focused_holo_light</item>
- <item>@drawable/btn_default_focused_holo_dark</item>
- <item>@drawable/btn_default_disabled_holo_light</item>
- <item>@drawable/btn_default_disabled_holo_dark</item>
- <item>@drawable/btn_default_disabled_focused_holo_light</item>
- <item>@drawable/btn_default_disabled_focused_holo_dark</item>
- <item>@drawable/btn_default_holo_dark</item>
- <item>@drawable/btn_default_holo_light</item>
- <item>@drawable/edit_text_holo_light</item>
- <item>@drawable/edit_text_holo_dark</item>
- <item>@drawable/text_cursor_holo_light</item>
- <item>@drawable/text_cursor_holo_dark</item>
- <item>@drawable/text_edit_paste_window</item>
- <item>@drawable/expander_close_holo_dark</item>
- <item>@drawable/expander_close_holo_light</item>
- <item>@drawable/expander_group_holo_dark</item>
- <item>@drawable/expander_group_holo_light</item>
- <item>@drawable/list_selector_holo_dark</item>
- <item>@drawable/list_selector_holo_light</item>
- <item>@drawable/list_section_divider_holo_light</item>
- <item>@drawable/list_section_divider_holo_dark</item>
- <item>@drawable/menu_dropdown_panel_holo_light</item>
- <item>@drawable/menu_dropdown_panel_holo_dark</item>
- <item>@drawable/menu_panel_holo_light</item>
- <item>@drawable/menu_panel_holo_dark</item>
- <item>@drawable/spinner_16_outer_holo</item>
- <item>@drawable/spinner_16_inner_holo</item>
- <item>@drawable/spinner_48_outer_holo</item>
- <item>@drawable/spinner_48_inner_holo</item>
- <item>@drawable/progress_bg_holo_dark</item>
- <item>@drawable/progress_bg_holo_light</item>
- <item>@drawable/progress_horizontal_holo_dark</item>
- <item>@drawable/progress_horizontal_holo_light</item>
- <item>@drawable/progress_large_holo</item>
- <item>@drawable/progress_medium_holo</item>
- <item>@drawable/progress_primary_holo_dark</item>
- <item>@drawable/progress_primary_holo_light</item>
- <item>@drawable/progress_secondary_holo_dark</item>
- <item>@drawable/progress_secondary_holo_light</item>
- <item>@drawable/progress_small_holo</item>
- <item>@drawable/scrubber_progress_horizontal_holo_dark</item>
- <item>@drawable/scrubber_progress_horizontal_holo_light</item>
- <item>@drawable/background_holo_light</item>
- <item>@drawable/background_holo_dark</item>
- <item>@drawable/screen_background_dark</item>
- <item>@drawable/screen_background_dark_transparent</item>
- <item>@drawable/screen_background_light</item>
- <item>@drawable/screen_background_light_transparent</item>
- <item>@drawable/screen_background_selector_dark</item>
- <item>@drawable/screen_background_selector_light</item>
- <item>@drawable/scrollbar_handle_holo_dark</item>
- <item>@drawable/scrollbar_handle_holo_light</item>
- <item>@drawable/spinner_background_holo_dark</item>
- <item>@drawable/spinner_background_holo_light</item>
- <item>@drawable/spinner_ab_default_holo_dark</item>
- <item>@drawable/spinner_ab_default_holo_light</item>
- <item>@drawable/spinner_ab_disabled_holo_dark</item>
- <item>@drawable/spinner_ab_disabled_holo_light</item>
- <item>@drawable/spinner_ab_focused_holo_dark</item>
- <item>@drawable/spinner_ab_focused_holo_light</item>
- <item>@drawable/spinner_ab_pressed_holo_dark</item>
- <item>@drawable/spinner_ab_pressed_holo_light</item>
- <item>@drawable/spinner_ab_holo_dark</item>
- <item>@drawable/spinner_ab_holo_light</item>
- <item>@drawable/spinner_default_holo_dark</item>
- <item>@drawable/spinner_default_holo_light</item>
- <item>@drawable/spinner_disabled_holo_dark</item>
- <item>@drawable/spinner_disabled_holo_light</item>
- <item>@drawable/spinner_focused_holo_dark</item>
- <item>@drawable/spinner_focused_holo_light</item>
- <item>@drawable/spinner_pressed_holo_dark</item>
- <item>@drawable/spinner_pressed_holo_light</item>
- <item>@drawable/cab_background_bottom_holo_dark</item>
- <item>@drawable/cab_background_top_holo_light</item>
- <item>@drawable/cab_background_bottom_holo_light</item>
- <item>@drawable/ic_cab_done_holo_dark</item>
- <item>@drawable/cab_background_top_holo_dark</item>
- <item>@drawable/ic_cab_done_holo_light</item>
- <item>@drawable/btn_cab_done_default_holo_dark</item>
- <item>@drawable/btn_cab_done_focused_holo_light</item>
- <item>@drawable/btn_cab_done_default_holo_light</item>
- <item>@drawable/btn_cab_done_pressed_holo_dark</item>
- <item>@drawable/btn_cab_done_focused_holo_dark</item>
- <item>@drawable/btn_cab_done_pressed_holo_light</item>
- <item>@drawable/btn_cab_done_holo_light</item>
- <item>@drawable/btn_cab_done_holo_dark</item>
- <item>@drawable/ic_menu_close_clear_cancel</item>
- <item>@drawable/ic_menu_copy_holo_dark</item>
- <item>@drawable/ic_menu_copy_holo_light</item>
- <item>@drawable/ic_menu_cut_holo_dark</item>
- <item>@drawable/ic_menu_cut_holo_light</item>
- <item>@drawable/ic_menu_more</item>
- <item>@drawable/ic_menu_moreoverflow_holo_dark</item>
- <item>@drawable/ic_menu_moreoverflow_holo_light</item>
- <item>@drawable/ic_menu_paste_holo_dark</item>
- <item>@drawable/ic_menu_paste_holo_light</item>
- <item>@drawable/ic_menu_selectall_holo_light</item>
- <item>@drawable/ic_menu_selectall_holo_dark</item>
- <item>@drawable/ic_clear</item>
- <item>@drawable/ic_clear_disabled</item>
- <item>@drawable/ic_clear_normal</item>
- <item>@drawable/ic_search_api_holo_dark</item>
- <item>@drawable/ic_search_api_holo_light</item>
- <item>@drawable/ic_go</item>
- <item>@drawable/ic_voice_search_api_holo_dark</item>
- <item>@drawable/ic_voice_search_api_holo_light</item>
- <item>@drawable/dialog_bottom_holo_dark</item>
- <item>@drawable/dialog_bottom_holo_light</item>
- <item>@drawable/dialog_middle_holo_dark</item>
- <item>@drawable/dialog_middle_holo_light</item>
- <item>@drawable/dialog_top_holo_dark</item>
- <item>@drawable/dialog_top_holo_light</item>
- <item>@drawable/ic_dialog_alert_holo_dark</item>
- <item>@drawable/ic_dialog_alert_holo_light</item>
- <item>@drawable/list_divider_holo_dark</item>
- <item>@drawable/list_divider_holo_light</item>
- <item>@drawable/list_divider_holo_light</item>
- <item>@drawable/ab_transparent_dark_holo</item>
- <item>@drawable/ab_stacked_transparent_dark_holo</item>
- <item>@drawable/ab_bottom_transparent_dark_holo</item>
- <item>@drawable/ab_solid_dark_holo</item>
- <item>@drawable/ab_stacked_solid_dark_holo</item>
- <item>@drawable/ab_bottom_solid_dark_holo</item>
- <item>@drawable/ab_transparent_light_holo</item>
- <item>@drawable/ab_stacked_transparent_light_holo</item>
- <item>@drawable/ab_bottom_transparent_light_holo</item>
- <item>@drawable/ab_solid_light_holo</item>
- <item>@drawable/ab_stacked_solid_light_holo</item>
- <item>@drawable/ab_bottom_solid_light_holo</item>
- <item>@drawable/ab_solid_shadow_holo</item>
- <item>@drawable/item_background_holo_dark</item>
- <item>@drawable/item_background_holo_light</item>
- <item>@drawable/fastscroll_thumb_holo</item>
- <item>@drawable/fastscroll_thumb_pressed_holo</item>
- <item>@drawable/fastscroll_thumb_default_holo</item>
- <item>@drawable/fastscroll_track_holo_dark</item>
- <item>@drawable/fastscroll_track_pressed_holo_dark</item>
- <item>@drawable/fastscroll_track_default_holo_dark</item>
- <item>@drawable/fastscroll_track_holo_light</item>
- <item>@drawable/fastscroll_track_pressed_holo_light</item>
- <item>@drawable/fastscroll_track_default_holo_light</item>
- <item>@drawable/editbox_dropdown_background_dark</item>
- <item>@drawable/textfield_searchview_holo_dark</item>
- <item>@drawable/textfield_searchview_right_holo_dark</item>
- <item>@drawable/textfield_searchview_holo_light</item>
- <item>@drawable/textfield_searchview_right_holo_light</item>
- <item>@drawable/textfield_search_selected_holo_dark</item>
- <item>@drawable/textfield_search_default_holo_dark</item>
- <item>@drawable/textfield_search_right_selected_holo_dark</item>
- <item>@drawable/textfield_search_right_default_holo_dark</item>
- <item>@drawable/textfield_search_selected_holo_light</item>
- <item>@drawable/textfield_search_default_holo_light</item>
- <item>@drawable/textfield_search_right_selected_holo_light</item>
- <item>@drawable/textfield_search_right_default_holo_light</item>
- <item>@drawable/tab_indicator_holo</item>
- <item>@drawable/tab_unselected_holo</item>
- <item>@drawable/tab_selected_holo</item>
- <item>@drawable/tab_unselected_focused_holo</item>
- <item>@drawable/tab_selected_focused_holo</item>
- <item>@drawable/tab_unselected_pressed_holo</item>
- <item>@drawable/tab_selected_pressed_holo</item>
- <item>@drawable/quickcontact_badge_overlay_dark</item>
- <item>@drawable/quickcontact_badge_overlay_normal_dark</item>
- <item>@drawable/quickcontact_badge_overlay_pressed_dark</item>
- <item>@drawable/quickcontact_badge_overlay_light</item>
- <item>@drawable/quickcontact_badge_overlay_normal_light</item>
- <item>@drawable/quickcontact_badge_overlay_pressed_light</item>
-
- <!-- Material assets -->
- <item>@drawable/ab_share_pack_material</item>
- <item>@drawable/ab_solid_shadow_material</item>
- <item>@drawable/activated_background_material</item>
- <item>@drawable/btn_borderless_material</item>
- <item>@drawable/btn_check_material_anim</item>
- <item>@drawable/btn_default_material</item>
- <item>@drawable/btn_radio_material_anim</item>
- <item>@drawable/btn_star_material</item>
- <item>@drawable/btn_toggle_material</item>
- <item>@drawable/cab_background_bottom_material</item>
- <item>@drawable/cab_background_top_material</item>
- <item>@drawable/dialog_background_material</item>
- <item>@drawable/edit_text_material</item>
- <item>@drawable/expander_group_material</item>
- <item>@drawable/fastscroll_label_left_material</item>
- <item>@drawable/fastscroll_label_right_material</item>
- <item>@drawable/fastscroll_thumb_material</item>
- <item>@drawable/fastscroll_track_material</item>
- <item>@drawable/floating_popup_background_dark</item>
- <item>@drawable/floating_popup_background_light</item>
- <item>@drawable/ic_ab_back_material</item>
- <item>@drawable/ic_ab_back_material_dark</item>
- <item>@drawable/ic_ab_back_material_light</item>
- <item>@drawable/ic_clear_material</item>
- <item>@drawable/ic_commit_search_api_material</item>
- <item>@drawable/ic_dialog_alert_material</item>
- <item>@drawable/ic_find_next_material</item>
- <item>@drawable/ic_find_previous_material</item>
- <item>@drawable/ic_go_search_api_material</item>
- <item>@drawable/ic_media_route_connecting_material</item>
- <item>@drawable/ic_media_route_material</item>
- <item>@drawable/ic_menu_copy_material</item>
- <item>@drawable/ic_menu_cut_material</item>
- <item>@drawable/ic_menu_find_material</item>
- <item>@drawable/ic_menu_moreoverflow_material</item>
- <item>@drawable/ic_menu_paste_material</item>
- <item>@drawable/ic_menu_search_material</item>
- <item>@drawable/ic_menu_selectall_material</item>
- <item>@drawable/ic_menu_share_material</item>
- <item>@drawable/ic_search_api_material</item>
- <item>@drawable/ic_voice_search_api_material</item>
- <item>@drawable/item_background_borderless_material</item>
- <item>@drawable/item_background_borderless_material_dark</item>
- <item>@drawable/item_background_borderless_material_light</item>
- <item>@drawable/item_background_material</item>
- <item>@drawable/item_background_material_dark</item>
- <item>@drawable/item_background_material_light</item>
- <item>@drawable/list_divider_material</item>
- <item>@drawable/list_section_divider_material</item>
- <item>@drawable/notification_material_action_background</item>
- <item>@drawable/notification_material_media_action_background</item>
- <item>@drawable/popup_background_material</item>
- <item>@drawable/progress_horizontal_material</item>
- <item>@drawable/progress_indeterminate_horizontal_material</item>
- <item>@drawable/progress_large_material</item>
- <item>@drawable/progress_medium_material</item>
- <item>@drawable/progress_small_material</item>
- <item>@drawable/ratingbar_material</item>
- <item>@drawable/ratingbar_small_material</item>
- <item>@drawable/ratingbar_indicator_material</item>
- <item>@drawable/scrollbar_handle_material</item>
- <item>@drawable/seekbar_thumb_material_anim</item>
- <item>@drawable/seekbar_track_material</item>
- <item>@drawable/spinner_background_material</item>
- <item>@drawable/spinner_textfield_background_material</item>
- <item>@drawable/switch_thumb_material_anim</item>
- <item>@drawable/switch_track_material</item>
- <item>@drawable/tab_indicator_material</item>
- <item>@drawable/text_cursor_material</item>
- <item>@drawable/textfield_search_material</item>
- <item>@drawable/text_select_handle_left_material</item>
- <item>@drawable/text_select_handle_middle_material</item>
- <item>@drawable/text_select_handle_right_material</item>
- <item>@drawable/ic_account_circle</item>
+ <item>@drawable/ab_share_pack_material</item>
+ <item>@drawable/ab_solid_shadow_material</item>
+ <item>@drawable/action_bar_item_background_material</item>
+ <item>@drawable/activated_background_material</item>
+ <item>@drawable/btn_borderless_material</item>
+ <item>@drawable/btn_check_material_anim</item>
+ <item>@drawable/btn_colored_material</item>
+ <item>@drawable/btn_default_material</item>
+ <item>@drawable/btn_group_holo_dark</item>
+ <item>@drawable/btn_group_holo_light</item>
+ <item>@drawable/btn_radio_material_anim</item>
+ <item>@drawable/btn_star_material</item>
+ <item>@drawable/btn_toggle_material</item>
+ <item>@drawable/button_inset</item>
+ <item>@drawable/cab_background_bottom_material</item>
+ <item>@drawable/cab_background_top_material</item>
+ <item>@drawable/control_background_32dp_material</item>
+ <item>@drawable/control_background_40dp_material</item>
+ <item>@drawable/dialog_background_material</item>
+ <item>@drawable/editbox_dropdown_background_dark</item>
+ <item>@drawable/edit_text_material</item>
+ <item>@drawable/expander_group_material</item>
+ <item>@drawable/fastscroll_label_left_material</item>
+ <item>@drawable/fastscroll_label_right_material</item>
+ <item>@drawable/fastscroll_thumb_material</item>
+ <item>@drawable/fastscroll_track_material</item>
+ <item>@drawable/floating_popup_background_dark</item>
+ <item>@drawable/floating_popup_background_light</item>
+ <item>@drawable/gallery_item_background</item>
+ <item>@drawable/ic_ab_back_material</item>
+ <item>@drawable/ic_ab_back_material_dark</item>
+ <item>@drawable/ic_ab_back_material_light</item>
+ <item>@drawable/ic_account_circle</item>
+ <item>@drawable/ic_arrow_drop_right_black_24dp</item>
+ <item>@drawable/ic_clear</item>
+ <item>@drawable/ic_clear_disabled</item>
+ <item>@drawable/ic_clear_material</item>
+ <item>@drawable/ic_clear_normal</item>
+ <item>@drawable/ic_commit_search_api_material</item>
+ <item>@drawable/ic_dialog_alert_material</item>
+ <item>@drawable/ic_find_next_material</item>
+ <item>@drawable/ic_find_previous_material</item>
+ <item>@drawable/ic_go</item>
+ <item>@drawable/ic_go_search_api_material</item>
+ <item>@drawable/ic_media_route_connecting_material</item>
+ <item>@drawable/ic_media_route_material</item>
+ <item>@drawable/ic_menu_close_clear_cancel</item>
+ <item>@drawable/ic_menu_copy_material</item>
+ <item>@drawable/ic_menu_cut_material</item>
+ <item>@drawable/ic_menu_find_material</item>
+ <item>@drawable/ic_menu_more</item>
+ <item>@drawable/ic_menu_moreoverflow_material</item>
+ <item>@drawable/ic_menu_paste_material</item>
+ <item>@drawable/ic_menu_search_material</item>
+ <item>@drawable/ic_menu_selectall_material</item>
+ <item>@drawable/ic_menu_share_material</item>
+ <item>@drawable/ic_search_api_material</item>
+ <item>@drawable/ic_voice_search_api_material</item>
+ <item>@drawable/indicator_check_mark_dark</item>
+ <item>@drawable/indicator_check_mark_light</item>
+ <item>@drawable/item_background_borderless_material</item>
+ <item>@drawable/item_background_borderless_material_dark</item>
+ <item>@drawable/item_background_borderless_material_light</item>
+ <item>@drawable/item_background_material</item>
+ <item>@drawable/item_background_material_dark</item>
+ <item>@drawable/item_background_material_light</item>
+ <item>@drawable/list_choice_background_material</item>
+ <item>@drawable/list_divider_material</item>
+ <item>@drawable/list_section_divider_material</item>
+ <item>@drawable/menu_background_fill_parent_width</item>
+ <item>@drawable/notification_material_action_background</item>
+ <item>@drawable/notification_material_media_action_background</item>
+ <item>@drawable/number_picker_divider_material</item>
+ <item>@drawable/popup_background_material</item>
+ <item>@drawable/popup_inline_error_above_holo_dark</item>
+ <item>@drawable/popup_inline_error_above_holo_light</item>
+ <item>@drawable/popup_inline_error_holo_dark</item>
+ <item>@drawable/popup_inline_error_holo_light</item>
+ <item>@drawable/progress_horizontal_material</item>
+ <item>@drawable/progress_indeterminate_horizontal_material</item>
+ <item>@drawable/progress_large_material</item>
+ <item>@drawable/progress_medium_material</item>
+ <item>@drawable/progress_small_material</item>
+ <item>@drawable/quickcontact_badge_overlay_dark</item>
+ <item>@drawable/quickcontact_badge_overlay_light</item>
+ <item>@drawable/quickcontact_badge_overlay_normal_dark</item>
+ <item>@drawable/quickcontact_badge_overlay_normal_light</item>
+ <item>@drawable/quickcontact_badge_overlay_pressed_dark</item>
+ <item>@drawable/quickcontact_badge_overlay_pressed_light</item>
+ <item>@drawable/ratingbar_indicator_material</item>
+ <item>@drawable/ratingbar_material</item>
+ <item>@drawable/ratingbar_small_material</item>
+ <item>@drawable/screen_background_dark</item>
+ <item>@drawable/screen_background_dark_transparent</item>
+ <item>@drawable/screen_background_light</item>
+ <item>@drawable/screen_background_light_transparent</item>
+ <item>@drawable/screen_background_selector_dark</item>
+ <item>@drawable/screen_background_selector_light</item>
+ <item>@drawable/scrollbar_handle_material</item>
+ <item>@drawable/seekbar_thumb_material_anim</item>
+ <item>@drawable/seekbar_tick_mark_material</item>
+ <item>@drawable/seekbar_track_material</item>
+ <item>@drawable/spinner_background_material</item>
+ <item>@drawable/spinner_textfield_background_material</item>
+ <item>@drawable/switch_thumb_material_anim</item>
+ <item>@drawable/switch_track_material</item>
+ <item>@drawable/tab_indicator_material</item>
+ <item>@drawable/text_cursor_material</item>
+ <item>@drawable/text_edit_paste_window</item>
+ <item>@drawable/textfield_search_material</item>
+ <item>@drawable/text_select_handle_left_material</item>
+ <item>@drawable/text_select_handle_middle_material</item>
+ <item>@drawable/text_select_handle_right_material</item>
+ <item>@drawable/toast_frame</item>
</array>
<!-- Do not translate. These are all of the color state list resources that should be
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8e9e978..fee2469 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2726,6 +2726,8 @@
<public type="style" name="Widget.Material.Light.CompoundButton.Switch" />
<public type="style" name="Widget.Material.NumberPicker" />
<public type="style" name="Widget.Material.Light.NumberPicker" />
+ <public type="style" name="TextAppearance.Material.Widget.Button.Colored" />
+ <public type="style" name="TextAppearance.Material.Widget.Button.Borderless.Colored" />
<public type="id" name="accessibilityActionSetProgress" />
<public type="id" name="icon_frame" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 45ee778..2232942 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4318,6 +4318,6 @@
<string name="audit_safemode_notification_details">Touch to learn more.</string>
<!-- Accessibilty string added to a widget that has been suspended [CHAR LIMIT=20] -->
- <string name="suspended_widget_accessibility">Suspended <xliff:g id="label" example="Calendar">%1$s</xliff:g></string>
+ <string name="suspended_widget_accessibility">Disabled <xliff:g id="label" example="Calendar">%1$s</xliff:g></string>
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 3ed8daa..c0060fd 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -298,6 +298,14 @@
<item name="textColor">?attr/textColorPrimaryInverse</item>
</style>
+ <style name="TextAppearance.Material.Widget.Button.Colored">
+ <item name="textColor">@color/btn_colored_text_material</item>
+ </style>
+
+ <style name="TextAppearance.Material.Widget.Button.Borderless.Colored" parent="TextAppearance.Material.Widget.Button">
+ <item name="textColor">@color/btn_colored_text_material</item>
+ </style>
+
<style name="TextAppearance.Material.Widget.EditText">
<item name="textColor">?attr/textColorPrimaryInverse</item>
<item name="textColorHint">?attr/textColorHintInverse</item>
@@ -485,8 +493,7 @@
<!-- Colored bordered ink button -->
<style name="Widget.Material.Button.Colored">
<item name="background">@drawable/btn_colored_material</item>
- <item name="textAppearance">@style/TextAppearance.Material.Widget.Button.Inverse</item>
- <item name="textColor">@color/btn_colored_text_material</item>
+ <item name="textAppearance">@style/TextAppearance.Material.Widget.Button.Colored</item>
</style>
<!-- Small bordered ink button -->
@@ -503,7 +510,7 @@
<!-- Colored borderless ink button -->
<style name="Widget.Material.Button.Borderless.Colored">
- <item name="textColor">@color/btn_colored_borderless_text_material</item>
+ <item name="textAppearance">@style/TextAppearance.Material.Widget.Button.Borderless.Colored</item>
</style>
<!-- Alert dialog button bar button -->
diff --git a/core/tests/coretests/apks/locales/Android.mk b/core/tests/coretests/apks/locales/Android.mk
new file mode 100644
index 0000000..9cb13dd
--- /dev/null
+++ b/core/tests/coretests/apks/locales/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := locales
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/locales/AndroidManifest.xml b/core/tests/coretests/apks/locales/AndroidManifest.xml
new file mode 100644
index 0000000..8a1f575
--- /dev/null
+++ b/core/tests/coretests/apks/locales/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.locales">
+
+ <application android:hasCode="false">
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/locales/res/values-pl/values.xml b/core/tests/coretests/apks/locales/res/values-pl/values.xml
new file mode 100644
index 0000000..22237fa8
--- /dev/null
+++ b/core/tests/coretests/apks/locales/res/values-pl/values.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="text">Text (pl)</string>
+</resources>
diff --git a/core/tests/coretests/apks/locales/res/values/values.xml b/core/tests/coretests/apks/locales/res/values/values.xml
new file mode 100644
index 0000000..cb990d2
--- /dev/null
+++ b/core/tests/coretests/apks/locales/res/values/values.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="text">Text</string>
+</resources>
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
new file mode 100644
index 0000000..e85666e
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.content.res;
+
+import android.os.FileUtils;
+import android.os.LocaleList;
+import android.support.test.filters.SmallTest;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.view.Display;
+
+import com.android.frameworks.coretests.R;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Locale;
+
+public class ResourcesLocaleTest extends AndroidTestCase {
+
+ private String extractApkAndGetPath(int id) throws Exception {
+ final Resources resources = getContext().getResources();
+ try (InputStream is = resources.openRawResource(id)) {
+ File path = new File(getContext().getFilesDir(), resources.getResourceEntryName(id));
+ FileUtils.copyToFileOrThrow(is, path);
+ return path.getAbsolutePath();
+ }
+ }
+
+ private Resources createResourcesWithApk(int rawApkId) throws Exception {
+ final AssetManager assets = new AssetManager();
+ assertTrue(assets.addAssetPath(extractApkAndGetPath(rawApkId)) != 0);
+
+ final DisplayMetrics dm = new DisplayMetrics();
+ dm.setToDefaults();
+ return new Resources(assets, dm, new Configuration());
+ }
+
+ private static void ensureNoLanguage(Resources resources, String language) {
+ final String[] supportedLocales = resources.getAssets().getNonSystemLocales();
+ for (String languageTag : supportedLocales) {
+ if ("en-XA".equals(languageTag)) {
+ continue;
+ }
+ assertFalse(
+ "supported locales: " + Arrays.toString(supportedLocales),
+ language.equals(Locale.forLanguageTag(languageTag).getLanguage()));
+ }
+ }
+
+ @SmallTest
+ public void testEnglishIsAlwaysConsideredSupported() throws Exception {
+ final Resources resources = createResourcesWithApk(R.raw.locales);
+ ensureNoLanguage(resources, "en");
+
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("en-US,pl-PL");
+ final Configuration config = new Configuration();
+ config.setLocales(preferredLocales);
+
+ resources.updateConfiguration(config, null);
+
+ // The APK we loaded has default and Polish languages. If English is first in the list,
+ // always take it the default (assumed to be English).
+ assertEquals(Locale.forLanguageTag("en-US"),
+ resources.getConfiguration().getLocales().get(0));
+ }
+
+ @SmallTest
+ public void testSelectFirstSupportedLanguage() throws Exception {
+ final Resources resources = createResourcesWithApk(R.raw.locales);
+ ensureNoLanguage(resources, "fr");
+
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("fr-FR,pl-PL");
+ final Configuration config = new Configuration();
+ config.setLocales(preferredLocales);
+
+ resources.updateConfiguration(config, null);
+
+ // The APK we loaded has default and Polish languages. We expect the Polish language to
+ // therefore be chosen.
+ assertEquals(Locale.forLanguageTag("pl-PL"),
+ resources.getConfiguration().getLocales().get(0));
+ }
+}
diff --git a/docs/html-intl/intl/es/preview/setup-sdk.jd b/docs/html-intl/intl/es/preview/setup-sdk.jd
index 01c715b..769121d 100644
--- a/docs/html-intl/intl/es/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/es/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
y <a href="{@docRoot}preview/api-overview.html">API y funciones de Android N
</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/in/preview/setup-sdk.jd b/docs/html-intl/intl/in/preview/setup-sdk.jd
index a842931..c6e43f1 100644
--- a/docs/html-intl/intl/in/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/in/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
dan <a href="{@docRoot}preview/api-overview.html">Android N API
dan Fiturnya</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/ja/preview/setup-sdk.jd b/docs/html-intl/intl/ja/preview/setup-sdk.jd
index fd5faba..4331641 100644
--- a/docs/html-intl/intl/ja/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/ja/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/ko/preview/setup-sdk.jd b/docs/html-intl/intl/ko/preview/setup-sdk.jd
index adb9edf..91f17c1 100644
--- a/docs/html-intl/intl/ko/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/ko/preview/setup-sdk.jd
@@ -136,7 +136,7 @@
<h2 id="java8">Java 8 JDK 및 JRE 가져오기</h2>
-<p>Android N 플랫폼에 대해 앱을 컴파일하려면
+<p>Android N 플랫폼에 대해 앱을 컴파일하려면
Java 8 개발자 키트(JDK 8)를 사용해야 하며, Android
Studio 2.1의 일부 도구를 사용하려면 Java 8 런타임 환경(JRE 8)을 설치해야
합니다. 따라서 각각 최신 버전이 없는 경우, 지금 JDK 8 및 JRE 8를
@@ -221,4 +221,4 @@
및 기능</a>에서 Android N 플랫폼에
대해 자세히 알아봅니다.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/pt-br/preview/setup-sdk.jd b/docs/html-intl/intl/pt-br/preview/setup-sdk.jd
index 4159c25..9035e50 100644
--- a/docs/html-intl/intl/pt-br/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/pt-br/preview/setup-sdk.jd
@@ -79,7 +79,7 @@
<h2 id="get-sdk">Obter o N Preview SDK</h2>
-<p>Para começar a desenvolver com as APIs do Android N, instale o
+<p>Para começar a desenvolver com as APIs do Android N, instale o
Android N Preview SDK no Android Studio da seguinte maneira:</p>
<ol>
@@ -112,7 +112,7 @@
<p>
Informações detalhadas sobre as APIs do Android N são disponibilizadas na documentação de referência do N Preview
, que pode ser baixada pela tabela a seguir.
- Este pacote contém uma versão off-line resumida do site de desenvolvedores do Android
+ Este pacote contém uma versão off-line resumida do site de desenvolvedores do Android
e inclui uma referência atualizada para as APIs do Android N, além de um relatório
das diferenças entre as APIs.
</p>
@@ -168,7 +168,7 @@
Para usar as APIs do Android N, seu projeto deve ser configurado da maneira apropriada.
</p>
-<p>Se planeja usar os recursos de linguagem do Java 8, consulte
+<p>Se planeja usar os recursos de linguagem do Java 8, consulte
<a href="{@docRoot}preview/j8-jack.html">Recursos de linguagem do Java 8</a>
para saber mais sobre os recursos do Java 8 com suporte e
como configurar seu projeto com o compilador Jack.</p>
@@ -216,9 +216,9 @@
<ul>
<li>Siga o guia para <a href="{@docRoot}preview/download.html">Testar em um dispositivo Android N</a>.</li>
- <li>Saiba mais sobre a plataforma Android N com
+ <li>Saiba mais sobre a plataforma Android N com
<a href="{@docRoot}preview/behavior-changes.html">Mudanças de comportamento</a>
e <a href="{@docRoot}preview/api-overview.html">Recursos de APIs do
Android N</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/ru/preview/setup-sdk.jd b/docs/html-intl/intl/ru/preview/setup-sdk.jd
index 3838700..582fe9f 100644
--- a/docs/html-intl/intl/ru/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/ru/preview/setup-sdk.jd
@@ -79,7 +79,7 @@
<h2 id="get-sdk">Установка N Preview SDK</h2>
-<p>Чтобы начать разработку с использованием API-интерфейсов Android N, необходимо установить
+<p>Чтобы начать разработку с использованием API-интерфейсов Android N, необходимо установить
Android N Preview SDK в Android Studio, выполнив следующие шаги.</p>
<ol>
@@ -136,7 +136,7 @@
<h2 id="java8">Установка Java 8 JDK и JRE</h2>
-<p>Чтобы скомпилировать ваше приложение для платформы Android N, необходимо использовать
+<p>Чтобы скомпилировать ваше приложение для платформы Android N, необходимо использовать
Java 8 Developer Kit (JDK 8), а для использования некоторых инструментов с Android
Studio 2.1 необходимо установить Java 8 Runtime Environment (JRE 8). Поэтому если у вас еще
нет последних версий каждого из этих пакетов, загрузите JDK 8 и JRE 8
@@ -221,4 +221,4 @@
и в описании <a href="{@docRoot}preview/api-overview.html">API-интерфейсов и возможностей Android N</a>.
</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/vi/preview/setup-sdk.jd b/docs/html-intl/intl/vi/preview/setup-sdk.jd
index 2e3a18b..6f6cd20 100644
--- a/docs/html-intl/intl/vi/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/vi/preview/setup-sdk.jd
@@ -98,7 +98,7 @@
<li>Nhấp vào tab <strong>SDK Tools</strong>, rồi chọn
<strong>Android SDK Build Tools</strong>, <strong>Android SDK
- Platform-Tools</strong>, và các hộp kiểm <strong>Android SDK Tools</strong>
+ Platform-Tools</strong>, và các hộp kiểm <strong>Android SDK Tools</strong>
.
</li>
@@ -221,4 +221,4 @@
và<a href="{@docRoot}preview/api-overview.html">API Android N
và Các tính năng</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd b/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
index 216d464..eaef7a1 100644
--- a/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd b/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
index d2a9d9e..7a46aca 100644
--- a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html/preview/features/security-config.jd b/docs/html/preview/features/security-config.jd
index 633c0e5..f8c0325 100644
--- a/docs/html/preview/features/security-config.jd
+++ b/docs/html/preview/features/security-config.jd
@@ -18,7 +18,7 @@
</ol>
</li>
<li><a href="#TrustingDebugCa">Debugging-only CAs</a></li>
- <li><a href="#UsesCleartextTraffic">Opting Out of Cleartext Traffic</a></li>
+ <li><a href="#CleartextTrafficPermitted">Opting Out of Cleartext Traffic</a></li>
<li><a href="#CertificatePinning">Pinning Certificates</a></li>
<li><a href="#ConfigInheritance">Configuration Inheritance Behavior</a></li>
<li><a href="#FileFormat">Configuration File Format</a></li>
@@ -240,7 +240,7 @@
</p>
-<h2 id="UsesCleartextTraffic">Opting Out of Cleartext Traffic</h2>
+<h2 id="CleartextTrafficPermitted">Opting Out of Cleartext Traffic</h2>
<p>
Applications intending to connect to destinations using only secure
@@ -263,7 +263,7 @@
<pre>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
- <domain-config usesCleartextTraffic="false">
+ <domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">secure.example.com</domain>
</domain-config>
</network-security-config>
@@ -436,7 +436,7 @@
<pre class="stx">
<base-config <a href=
-"#usesCleartextTraffic">usesCleartextTraffic</a>=["true" | "false"]>
+"#CleartextTrafficPermitted">cleartextTrafficPermitted</a>=["true" | "false"]>
...
</base-config>
</pre>
@@ -463,7 +463,7 @@
</p>
<pre>
-<base-config usesCleartextTraffic="true">
+<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
@@ -471,7 +471,7 @@
</pre>
The default configuration for apps targeting API level 23 and below is:
<pre>
-<base-config usesCleartextTraffic="true">
+<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
@@ -486,7 +486,7 @@
<dl class="xml">
<dt>syntax:</dt>
<dd>
-<pre class="stx"><domain-config <a href="#usesCleartextTraffic">usesCleartextTraffic</a>=["true" | "false"]>
+<pre class="stx"><domain-config <a href="#CleartextTrafficPermitted">cleartextTrafficPermitted</a>=["true" | "false"]>
...
</domain-config></pre>
</dd>
diff --git a/docs/html/preview/setup-sdk.jd b/docs/html/preview/setup-sdk.jd
index 28fc406..ebeae74 100644
--- a/docs/html/preview/setup-sdk.jd
+++ b/docs/html/preview/setup-sdk.jd
@@ -189,4 +189,4 @@
and <a href="{@docRoot}preview/api-overview.html">Android N APIs
and Features</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index c67b008..1864206 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -104,6 +104,9 @@
private Rect mHotspotBounds;
private boolean mMutated;
+ private boolean mSuspendChildInvalidation;
+ private boolean mChildRequestedInvalidation;
+
/**
* Creates a new layer drawable with the list of specified layers.
*
@@ -944,9 +947,37 @@
return mLayerState.mPaddingMode;
}
+ /**
+ * Temporarily suspends child invalidation.
+ *
+ * @see #resumeChildInvalidation()
+ */
+ private void suspendChildInvalidation() {
+ mSuspendChildInvalidation = true;
+ }
+
+ /**
+ * Resumes child invalidation after suspension, immediately performing an
+ * invalidation if one was requested by a child during suspension.
+ *
+ * @see #suspendChildInvalidation()
+ */
+ private void resumeChildInvalidation() {
+ mSuspendChildInvalidation = false;
+
+ if (mChildRequestedInvalidation) {
+ mChildRequestedInvalidation = false;
+ invalidateSelf();
+ }
+ }
+
@Override
public void invalidateDrawable(@NonNull Drawable who) {
- invalidateSelf();
+ if (mSuspendChildInvalidation) {
+ mChildRequestedInvalidation = true;
+ } else {
+ invalidateSelf();
+ }
}
@Override
@@ -1482,6 +1513,15 @@
}
private void updateLayerBounds(Rect bounds) {
+ try {
+ suspendChildInvalidation();
+ updateLayerBoundsInternal(bounds);
+ } finally {
+ resumeChildInvalidation();
+ }
+ }
+
+ private void updateLayerBoundsInternal(Rect bounds) {
int paddingL = 0;
int paddingT = 0;
int paddingR = 0;
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 746e99a..cb8e55f 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -878,11 +878,49 @@
restoreForLayer();
+ // saveLayer will clip & translate the draw contents, so we need
+ // to translate the drawLayer by how much the contents was translated
+ // TODO: Unify this with beginLayerOp so we don't have to calculate this
+ // twice
+ uint32_t layerWidth = (uint32_t) beginLayerOp.unmappedBounds.getWidth();
+ uint32_t layerHeight = (uint32_t) beginLayerOp.unmappedBounds.getHeight();
+
+ auto previous = mCanvasState.currentSnapshot();
+ Vector3 lightCenter = previous->getRelativeLightCenter();
+
+ // Combine all transforms used to present saveLayer content:
+ // parent content transform * canvas transform * bounds offset
+ Matrix4 contentTransform(*(previous->transform));
+ contentTransform.multiply(beginLayerOp.localMatrix);
+ contentTransform.translate(beginLayerOp.unmappedBounds.left,
+ beginLayerOp.unmappedBounds.top);
+
+ Matrix4 inverseContentTransform;
+ inverseContentTransform.loadInverse(contentTransform);
+
+ // map the light center into layer-relative space
+ inverseContentTransform.mapPoint3d(lightCenter);
+
+ // Clip bounds of temporary layer to parent's clip rect, so:
+ Rect saveLayerBounds(layerWidth, layerHeight);
+ // 1) transform Rect(width, height) into parent's space
+ // note: left/top offsets put in contentTransform above
+ contentTransform.mapRect(saveLayerBounds);
+ // 2) intersect with parent's clip
+ saveLayerBounds.doIntersect(previous->getRenderTargetClip());
+ // 3) and transform back
+ inverseContentTransform.mapRect(saveLayerBounds);
+ saveLayerBounds.doIntersect(Rect(layerWidth, layerHeight));
+ saveLayerBounds.roundOut();
+
+ Matrix4 localMatrix(beginLayerOp.localMatrix);
+ localMatrix.translate(saveLayerBounds.left, saveLayerBounds.top);
+
// record the draw operation into the previous layer's list of draw commands
// uses state from the associated beginLayerOp, since it has all the state needed for drawing
LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(
beginLayerOp.unmappedBounds,
- beginLayerOp.localMatrix,
+ localMatrix,
beginLayerOp.localClip,
beginLayerOp.paint,
&(mLayerBuilders[finishedLayerIndex]->offscreenBuffer));
diff --git a/libs/hwui/OpDumper.cpp b/libs/hwui/OpDumper.cpp
index cab93e8..ec9ffde 100644
--- a/libs/hwui/OpDumper.cpp
+++ b/libs/hwui/OpDumper.cpp
@@ -45,5 +45,9 @@
}
}
+const char* OpDumper::opName(const RecordedOp& op) {
+ return sOpNameLut[op.opId];
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/OpDumper.h b/libs/hwui/OpDumper.h
index c99b517..a82289c 100644
--- a/libs/hwui/OpDumper.h
+++ b/libs/hwui/OpDumper.h
@@ -26,6 +26,7 @@
class OpDumper {
public:
static void dump(const RecordedOp& op, std::ostream& output, int level = 0);
+ static const char* opName(const RecordedOp& op);
};
}; // namespace uirenderer
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 0f16b15..af1fbd8 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -2025,6 +2025,7 @@
uint32_t layerHeight = 0;
Rect rectClippedBounds;
Matrix4 rectMatrix;
+ Matrix4 drawLayerMatrix;
};
/**
* Constructs a view to hit the temporary layer alpha property implementation:
@@ -2060,6 +2061,7 @@
}
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
EXPECT_EQ(3, mIndex++);
+ mOutData->drawLayerMatrix = state.computedState.transform;
}
void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
EXPECT_EQ(4, mIndex++);
@@ -2108,6 +2110,9 @@
expected.loadTranslate(0, -2000, 0);
EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
<< "expect content to be translated as part of being clipped";
+ expected.loadTranslate(10, 0, 0);
+ EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix)
+ << "expect drawLayer to be translated as part of being clipped";
}
RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7178313..4bdc70e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2747,13 +2747,6 @@
*/
public static abstract class AudioRecordingCallback {
/**
- * This method is deprecated, override {@link #onRecordingConfigChanged(List)} instead.
- * @param configs
- * @removed
- */
- @Deprecated
- public void onRecordingConfigChanged(AudioRecordingConfiguration[] configs) {}
- /**
* Called whenever the device recording configuration has changed.
* @param configs list containing the results of
* {@link AudioManager#getActiveRecordingConfigurations()}.
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
index e718742..423c3b0 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
@@ -38,6 +38,7 @@
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.media.ImageReader;
+import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -67,6 +68,7 @@
import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedPreviewSizes;
import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedStillSizes;
import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSortedSizesForFormat;
import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
/**
@@ -122,6 +124,8 @@
protected List<Size> mOrderedPreviewSizes; // In descending order.
protected List<Size> mOrderedVideoSizes; // In descending order.
protected List<Size> mOrderedStillSizes; // In descending order.
+ protected List<Size> mOrderedRAW10Sizes; // In descending order.
+ protected List<Size> mOrderedYUV420888Sizes; // In descending order.
protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
protected WindowManager mWindowManager;
@@ -589,6 +593,7 @@
mReaderSurface = null;
}
+
/**
* Open a camera device and get the StaticMetadata for a given camera id.
*
@@ -603,8 +608,13 @@
if (mStaticInfo.isColorOutputSupported()) {
mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
- mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+ mOrderedVideoSizes = getSupportedVideoSizes(
+ cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
+ mOrderedRAW10Sizes = getSortedSizesForFormat(
+ cameraId, mCameraManager, ImageFormat.RAW10, null);
+ mOrderedYUV420888Sizes = getSortedSizesForFormat(
+ cameraId, mCameraManager, ImageFormat.YUV_420_888, null);
// Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
// in public API side.
mMinPreviewFrameDurationMap =
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java
new file mode 100644
index 0000000..11327ca
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2016 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.mediaframeworktest.stress;
+
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
+import com.android.mediaframeworktest.helpers.Camera2Focuser;
+import com.android.mediaframeworktest.helpers.CameraTestUtils;
+import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
+
+import android.graphics.ImageFormat;
+import android.graphics.Point;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
+import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.DngCreator;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.media.Image;
+import android.media.ImageReader;
+import android.media.CamcorderProfile;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaRecorder;
+import android.os.ConditionVariable;
+import android.os.Environment;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Rational;
+import android.util.Size;
+import android.view.Surface;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.util.Range;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.basicValidateJpegImage;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.dumpFile;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getDataFromImage;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
+import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_CLOSED;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SESSION_CLOSE_TIMEOUT_MS;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_1080P;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_2160P;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
+
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
+import com.android.mediaframeworktest.helpers.CameraTestUtils;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * <p>Tests Back/Front camera switching and Camera/Video modes witching.</p>
+ *
+ * adb shell am instrument \
+ * -e class com.android.mediaframeworktest.stress.Camera2SwitchPreviewTest \
+ * -e iterations 200 \
+ * -e waitIntervalMs 1000 \
+ * -e resultToFile false \
+ * -r -w com.android.mediaframeworktest/.Camera2InstrumentationTestRunner
+ */
+public class Camera2SwitchPreviewTest extends Camera2SurfaceViewTestCase {
+ private static final String TAG = "SwitchPreviewTest";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ // 60 second to accommodate the possible long exposure time.
+ private static final int MAX_REGIONS_AE_INDEX = 0;
+ private static final int MAX_REGIONS_AWB_INDEX = 1;
+ private static final int MAX_REGIONS_AF_INDEX = 2;
+ private static final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 6000;
+ private static final double AE_COMPENSATION_ERROR_TOLERANCE = 0.2;
+ // 5 percent error margin for resulting metering regions
+ private static final float METERING_REGION_ERROR_PERCENT_DELTA = 0.05f;
+ private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
+
+ private static final boolean DEBUG_DUMP = Log.isLoggable(TAG, Log.DEBUG);
+ private static final int RECORDING_DURATION_MS = 3000;
+ private static final float DURATION_MARGIN = 0.2f;
+ private static final double FRAME_DURATION_ERROR_TOLERANCE_MS = 3.0;
+ private static final int BIT_RATE_1080P = 16000000;
+ private static final int BIT_RATE_MIN = 64000;
+ private static final int BIT_RATE_MAX = 40000000;
+ private static final int VIDEO_FRAME_RATE = 30;
+ private static final int[] mCamcorderProfileList = {
+ CamcorderProfile.QUALITY_HIGH,
+ CamcorderProfile.QUALITY_2160P,
+ CamcorderProfile.QUALITY_1080P,
+ CamcorderProfile.QUALITY_720P,
+ CamcorderProfile.QUALITY_480P,
+ CamcorderProfile.QUALITY_CIF,
+ CamcorderProfile.QUALITY_QCIF,
+ CamcorderProfile.QUALITY_QVGA,
+ CamcorderProfile.QUALITY_LOW,
+ };
+ private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5;
+ private static final int BURST_VIDEO_SNAPSHOT_NUM = 3;
+ private static final int SLOWMO_SLOW_FACTOR = 4;
+ private static final int MAX_NUM_FRAME_DROP_INTERVAL_ALLOWED = 4;
+ private List<Size> mSupportedVideoSizes;
+ private Surface mRecordingSurface;
+ private Surface mPersistentSurface;
+ private MediaRecorder mMediaRecorder;
+ private String mOutMediaFileName;
+ private int mVideoFrameRate;
+ private Size mVideoSize;
+ private long mRecordingStartTime;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Test normal still preview switch.
+ * <p>
+ * Preview jpeg output streams are configured. Max still capture
+ * size is used for jpeg capture.
+ * </p>
+ */
+ public void testPreviewSwitchBackFrontCamera() throws Exception {
+ List<String> mCameraColorOutputIds = cameraColorOutputCheck();
+ // Test iteration starts...
+ Log.i(TAG, "Testing preview switch back/front camera in still capture mode");
+ for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
+ for (String id : mCameraColorOutputIds) {
+ try {
+ openDevice(id);
+ // Preview for basic still capture:
+ Log.v(TAG, String.format("Preview pictures: %d/%d", iteration + 1,
+ getIterationCount()));
+ stillCapturePreviewPreparer(id);
+ getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
+ } finally {
+ closeDevice();
+ closeImageReader();
+ }
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Test basic video preview switch.
+ * </p>
+ * <p>
+ * This test covers the typical basic use case of video preview switch.
+ * MediaRecorder is used to record the audio and video, CamcorderProfile is
+ * used to configure the MediaRecorder. Preview is set to the video size.
+ * </p>
+ */
+ public void testPreviewSwitchBackFrontVideo() throws Exception {
+ List<String> mCameraColorOutputIds = cameraColorOutputCheck();
+ // Test iteration starts...
+ Log.i(TAG, "Testing preview switch back/front camera in video mode");
+ for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
+ for (String id : mCameraColorOutputIds) {
+ try {
+ openDevice(id);
+ // Preview for basic video recording:
+ Log.v(TAG, String.format("Preview for recording videos: %d/%d", iteration + 1,
+ getIterationCount()));
+ recordingPreviewPreparer(id);
+ getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
+ } finally {
+ closeDevice();
+ releaseRecorder();
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Test back camera preview switch between still capture and recording mode.
+ * <p>
+ * This test covers the basic case of preview switch camera mode, between
+ * still capture (photo) and recording (video) mode. The preview settings
+ * are same with the settings in "testPreviewSwitchBackFrontCamera" and
+ * "testPreviewSwitchBackFrontVideo"
+ * </p>
+ */
+ public void testPreviewSwitchBackCameraVideo() throws Exception {
+ String id = mCameraIds[0];
+ openDevice(id);
+ if (!mStaticInfo.isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + id +
+ " does not support color outputs, skipping");
+ return;
+ }
+ closeDevice();
+ // Test iteration starts...
+ Log.i(TAG, "Testing preview switch between still capture/video modes for back camera");
+ for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
+ try {
+ openDevice(id);
+
+ // Preview for basic still capture:
+ Log.v(TAG, String.format("Preview pictures: %d/%d", iteration + 1,
+ getIterationCount()));
+ stillCapturePreviewPreparer(id);
+ getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
+
+ // Preview for basic video recording:
+ Log.v(TAG, String.format("Preview for recording videos: %d/%d", iteration + 1,
+ getIterationCount()));
+ recordingPreviewPreparer(id);
+ getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
+ } finally {
+ closeDevice();
+ closeImageReader();
+ }
+ }
+ }
+
+ /**
+ * Test front camera preview switch between still capture and recording mode.
+ * <p>
+ * This test covers the basic case of preview switch camera mode, between
+ * still capture (photo) and recording (video) mode. The preview settings
+ * are same with the settings in "testPreviewSwitchBackFrontCamera" and
+ * "testPreviewSwitchBackFrontVideo"
+ * </p>
+ */
+ public void testPreviewSwitchFrontCameraVideo() throws Exception{
+ String id = mCameraIds[1];
+ openDevice(id);
+ if (!mStaticInfo.isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + id +
+ " does not support color outputs, skipping");
+ return;
+ }
+ closeDevice();
+ // Test iteration starts...
+ Log.i(TAG, "Testing preview switch between still capture/video modes for front camera");
+ for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
+ try {
+ openDevice(id);
+
+ // Preview for basic still capture:
+ Log.v(TAG, String.format("Preview pictures: %d/%d", iteration + 1,
+ getIterationCount()));
+ stillCapturePreviewPreparer(id);
+ getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
+
+ // Preview for basic video recording:
+ Log.v(TAG, String.format("Preview for recording videos: %d/%d", iteration + 1,
+ getIterationCount()));
+ recordingPreviewPreparer(id);
+ getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
+ } finally {
+ closeDevice();
+ closeImageReader();
+ }
+ }
+ }
+
+ private void stillCapturePreviewPreparer(String id) throws Exception{
+ CaptureResult result;
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+ CaptureRequest.Builder previewRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ CaptureRequest.Builder stillRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+ // Preview Setup:
+ prepareCapturePreview(previewRequest, stillRequest, resultListener, imageListener);
+
+ Thread.sleep(getTestWaitIntervalMs());
+ }
+
+ private void recordingPreviewPreparer(String id) throws Exception{
+ // Re-use the MediaRecorder object for the same camera device.
+ mMediaRecorder = new MediaRecorder();
+ initSupportedVideoSize(id);
+ // preview Setup:
+ basicRecordingPreviewTestByCamera(mCamcorderProfileList);
+
+ Thread.sleep(getTestWaitIntervalMs());
+ }
+
+
+ /**
+ * Initialize the supported video sizes.
+ */
+ private void initSupportedVideoSize(String cameraId) throws Exception {
+ Size maxVideoSize = SIZE_BOUND_1080P;
+ if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P)) {
+ maxVideoSize = SIZE_BOUND_2160P;
+ }
+ mSupportedVideoSizes =
+ getSupportedVideoSizes(cameraId, mCameraManager, maxVideoSize);
+ }
+
+
+ /**
+ * Test camera recording preview by using each available CamcorderProfile for a
+ * given camera. preview size is set to the video size.
+ */
+ private void basicRecordingPreviewTestByCamera(int[] camcorderProfileList)
+ throws Exception {
+ Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+ List<Range<Integer> > fpsRanges = Arrays.asList(
+ mStaticInfo.getAeAvailableTargetFpsRangesChecked());
+ int cameraId = Integer.parseInt(mCamera.getId());
+ int maxVideoFrameRate = -1;
+ int profileId = camcorderProfileList[0];
+ if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
+ allowedUnsupported(cameraId, profileId)) {
+ return;
+ }
+
+ CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
+ Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+ Range<Integer> fpsRange = new Range(profile.videoFrameRate, profile.videoFrameRate);
+ if (maxVideoFrameRate < profile.videoFrameRate) {
+ maxVideoFrameRate = profile.videoFrameRate;
+ }
+ if (mStaticInfo.isHardwareLevelLegacy() &&
+ (videoSz.getWidth() > maxPreviewSize.getWidth() ||
+ videoSz.getHeight() > maxPreviewSize.getHeight())) {
+ // Skip. Legacy mode can only do recording up to max preview size
+ return;
+ }
+ assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId +
+ " must be one of the camera device supported video size!",
+ mSupportedVideoSizes.contains(videoSz));
+ assertTrue("Frame rate range " + fpsRange + " (for profile ID " + profileId +
+ ") must be one of the camera device available FPS range!",
+ fpsRanges.contains(fpsRange));
+
+ if (VERBOSE) {
+ Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
+ }
+
+ // Configure preview and recording surfaces.
+ mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+ if (DEBUG_DUMP) {
+ mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
+ + videoSz.toString() + ".mp4";
+ }
+
+ prepareRecordingWithProfile(profile);
+
+ // prepare preview surface by using video size.
+ updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
+
+ CaptureRequest.Builder previewRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ CaptureRequest.Builder recordingRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+
+ prepareVideoPreview(previewRequest, recordingRequest, resultListener, imageListener);
+
+ // Can reuse the MediaRecorder object after reset.
+ mMediaRecorder.reset();
+
+ if (maxVideoFrameRate != -1) {
+ // At least one CamcorderProfile is present, check FPS
+ assertTrue("At least one CamcorderProfile must support >= 24 FPS",
+ maxVideoFrameRate >= 24);
+ }
+ }
+
+ private void releaseRecorder() {
+ if (mMediaRecorder != null) {
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+ }
+ }
+
+ private List<String> cameraColorOutputCheck() throws Exception {
+ List<String> mCameraColorOutputIds = new ArrayList<String>();
+ for (String id : mCameraIds) {
+ openDevice(id);
+ if (!mStaticInfo.isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + id +
+ " does not support color outputs, skipping");
+ continue;
+ }
+ mCameraColorOutputIds.add(id);
+ closeDevice();
+ }
+ return mCameraColorOutputIds;
+ }
+
+ /**
+ * Returns {@code true} if the {@link CamcorderProfile} ID is allowed to be unsupported.
+ *
+ * <p>This only allows unsupported profiles when using the LEGACY mode of the Camera API.</p>
+ *
+ * @param profileId a {@link CamcorderProfile} ID to check.
+ * @return {@code true} if supported.
+ */
+ private boolean allowedUnsupported(int cameraId, int profileId) {
+ if (!mStaticInfo.isHardwareLevelLegacy()) {
+ return false;
+ }
+
+ switch(profileId) {
+ case CamcorderProfile.QUALITY_2160P:
+ case CamcorderProfile.QUALITY_1080P:
+ case CamcorderProfile.QUALITY_HIGH:
+ return !CamcorderProfile.hasProfile(cameraId, profileId) ||
+ CamcorderProfile.get(cameraId, profileId).videoFrameWidth >= 1080;
+ }
+ return false;
+ }
+
+ /**
+ * Configure MediaRecorder recording session with CamcorderProfile, prepare
+ * the recording surface.
+ */
+ private void prepareRecordingWithProfile(CamcorderProfile profile)
+ throws Exception {
+ // Prepare MediaRecorder.
+ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+ mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+ mMediaRecorder.setProfile(profile);
+ mMediaRecorder.setOutputFile(mOutMediaFileName);
+ if (mPersistentSurface != null) {
+ mMediaRecorder.setInputSurface(mPersistentSurface);
+ mRecordingSurface = mPersistentSurface;
+ }
+ mMediaRecorder.prepare();
+ if (mPersistentSurface == null) {
+ mRecordingSurface = mMediaRecorder.getSurface();
+ }
+ assertNotNull("Recording surface must be non-null!", mRecordingSurface);
+ mVideoFrameRate = profile.videoFrameRate;
+ mVideoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+ }
+
+ /**
+ * Update preview size with video size.
+ *
+ * <p>Preview size will be capped with max preview size.</p>
+ *
+ * @param videoSize The video size used for preview.
+ * @param videoFrameRate The video frame rate
+ *
+ */
+ private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate) throws Exception {
+ if (mOrderedPreviewSizes == null) {
+ throw new IllegalStateException("supported preview size list is not initialized yet");
+ }
+ final float FRAME_DURATION_TOLERANCE = 0.01f;
+ long videoFrameDuration = (long) (1e9 / videoFrameRate *
+ (1.0 + FRAME_DURATION_TOLERANCE));
+ HashMap<Size, Long> minFrameDurationMap = mStaticInfo.
+ getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE);
+ Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+ Size previewSize = null;
+ if (videoSize.getWidth() > maxPreviewSize.getWidth() ||
+ videoSize.getHeight() > maxPreviewSize.getHeight()) {
+ for (Size s : mOrderedPreviewSizes) {
+ Long frameDuration = minFrameDurationMap.get(s);
+ if (mStaticInfo.isHardwareLevelLegacy()) {
+ // Legacy doesn't report min frame duration
+ frameDuration = new Long(0);
+ }
+ assertTrue("Cannot find minimum frame duration for private size" + s,
+ frameDuration != null);
+ if (frameDuration <= videoFrameDuration &&
+ s.getWidth() <= videoSize.getWidth() &&
+ s.getHeight() <= videoSize.getHeight()) {
+ Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
+ " to " + s.toString());
+ previewSize = s;
+ break;
+ // If all preview size doesn't work then we fallback to video size
+ }
+ }
+ }
+ if (previewSize == null) {
+ previewSize = videoSize;
+ }
+
+ updatePreviewSurface(previewSize);
+ }
+
+ protected void prepareVideoPreview(CaptureRequest.Builder previewRequest,
+ CaptureRequest.Builder recordingRequest,
+ CaptureCallback resultListener,
+ ImageReader.OnImageAvailableListener imageListener) throws Exception {
+
+ // Configure output streams with preview and jpeg streams.
+ List<Surface> outputSurfaces = new ArrayList<Surface>();
+ outputSurfaces.add(mPreviewSurface);
+ outputSurfaces.add(mRecordingSurface);
+
+ mSessionListener = new BlockingSessionCallback();
+ mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+
+ previewRequest.addTarget(mPreviewSurface);
+ recordingRequest.addTarget(mPreviewSurface);
+ recordingRequest.addTarget(mRecordingSurface);
+
+ // Start preview.
+ mSession.setRepeatingRequest(previewRequest.build(), null, mHandler);
+ }
+
+ protected void prepareCapturePreview(CaptureRequest.Builder previewRequest,
+ CaptureRequest.Builder stillRequest,
+ CaptureCallback resultListener,
+ ImageReader.OnImageAvailableListener imageListener) throws Exception {
+
+ Size captureSz = mOrderedStillSizes.get(0);
+ Size previewSz = mOrderedPreviewSizes.get(1);
+
+ if (VERBOSE) {
+ Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)",
+ captureSz.toString(), previewSz.toString()));
+ }
+
+ // Update preview size.
+ updatePreviewSurface(previewSz);
+
+ // Create ImageReader.
+ createImageReader(captureSz, ImageFormat.JPEG, MAX_READER_IMAGES, imageListener);
+
+ // Configure output streams with preview and jpeg streams.
+ List<Surface> outputSurfaces = new ArrayList<Surface>();
+ outputSurfaces.add(mPreviewSurface);
+ outputSurfaces.add(mReaderSurface);
+ mSessionListener = new BlockingSessionCallback();
+ mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+
+ // Configure the requests.
+ previewRequest.addTarget(mPreviewSurface);
+ stillRequest.addTarget(mPreviewSurface);
+ stillRequest.addTarget(mReaderSurface);
+
+ // Start preview.
+ mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+ }
+
+}
diff --git a/packages/PrintRecommendationService/res/values/donottranslate.xml b/packages/PrintRecommendationService/res/values/donottranslate.xml
index 68effbf..86027cd 100644
--- a/packages/PrintRecommendationService/res/values/donottranslate.xml
+++ b/packages/PrintRecommendationService/res/values/donottranslate.xml
@@ -32,8 +32,15 @@
<item>Hewlett Packard</item>
</string-array>
+ <string-array name="known_print_vendor_info_for_samsung" translatable="false">
+ <item>com.sec.app.samsungprintservice</item>
+ <item>Samsung Electronics</item>
+ <item>Samsung</item>
+ </string-array>
+
<array name="known_print_plugin_vendors" translatable="false">
<item>@array/known_print_vendor_info_for_mopria</item>
<item>@array/known_print_vendor_info_for_hp</item>
+ <item>@array/known_print_vendor_info_for_samsung</item>
</array>
</resources>
diff --git a/packages/PrintRecommendationService/res/values/strings.xml b/packages/PrintRecommendationService/res/values/strings.xml
index 97281de..348fcac 100644
--- a/packages/PrintRecommendationService/res/values/strings.xml
+++ b/packages/PrintRecommendationService/res/values/strings.xml
@@ -23,7 +23,7 @@
<string name="plugin_vendor_brother">Brother</string>
<string name="plugin_vendor_canon">Canon</string>
<string name="plugin_vendor_xerox">Xerox</string>
- <string name="plugin_vendor_samsung">Samsung Electronics</string>
+ <string name="plugin_vendor_samsung">Samsung</string>
<string name="plugin_vendor_epson">Epson</string>
<string name="plugin_vendor_konica_minolta">Konica Minolta</string>
<string name="plugin_vendor_fuji">Fuji</string>
diff --git a/packages/PrintRecommendationService/res/xml/vendorconfigs.xml b/packages/PrintRecommendationService/res/xml/vendorconfigs.xml
index b7925df..108ea66 100644
--- a/packages/PrintRecommendationService/res/xml/vendorconfigs.xml
+++ b/packages/PrintRecommendationService/res/xml/vendorconfigs.xml
@@ -51,14 +51,6 @@
</vendor>
<vendor>
- <name>@string/plugin_vendor_samsung</name>
- <package>com.sec.app.samsungprintservice</package>
- <mdns-names>
- <mdns-name>Samsung</mdns-name>
- </mdns-names>
- </vendor>
-
- <vendor>
<name>@string/plugin_vendor_epson</name>
<package>com.epson.mobilephone.android.epsonprintserviceplugin</package>
<mdns-names>
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
index 4b4b470..d95654f 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
@@ -25,6 +25,7 @@
import com.android.printservice.recommendation.plugin.mdnsFilter.MDNSFilterPlugin;
import com.android.printservice.recommendation.plugin.mdnsFilter.VendorConfig;
import com.android.printservice.recommendation.plugin.mopria.MopriaRecommendationPlugin;
+import com.android.printservice.recommendation.plugin.samsung.SamsungRecommendationPlugin;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@@ -74,6 +75,14 @@
" plugin", e);
}
+ try {
+ mPlugins.add(new RemotePrintServicePlugin(new SamsungRecommendationPlugin(this), this,
+ false));
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Could not initiate " + getString(R.string.plugin_vendor_samsung) +
+ " plugin", e);
+ }
+
final int numPlugins = mPlugins.size();
for (int i = 0; i < numPlugins; i++) {
try {
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/MDnsUtils.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/MDnsUtils.java
new file mode 100644
index 0000000..963e09b
--- /dev/null
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/MDnsUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 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.printservice.recommendation.plugin.samsung;
+
+import android.net.nsd.NsdServiceInfo;
+import android.text.TextUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+import java.util.Map;
+
+public class MDnsUtils {
+ public static final String ATTRIBUTE__TY = "ty";
+ public static final String ATTRIBUTE__PRODUCT = "product";
+ public static final String ATTRIBUTE__USB_MFG = "usb_MFG";
+ public static final String ATTRIBUTE__MFG = "mfg";
+
+ public static String getString(byte[] value) {
+ if (value != null) return new String(value,StandardCharsets.UTF_8);
+ return null;
+ }
+
+ public static boolean isVendorPrinter(NsdServiceInfo networkDevice, String[] vendorValues) {
+
+ Map<String,byte[]> attributes = networkDevice.getAttributes();
+ String product = getString(attributes.get(ATTRIBUTE__PRODUCT));
+ String ty = getString(attributes.get(ATTRIBUTE__TY));
+ String usbMfg = getString(attributes.get(ATTRIBUTE__USB_MFG));
+ String mfg = getString(attributes.get(ATTRIBUTE__MFG));
+ return containsVendor(product, vendorValues) || containsVendor(ty, vendorValues) || containsVendor(usbMfg, vendorValues) || containsVendor(mfg, vendorValues);
+
+ }
+
+ public static String getVendor(NsdServiceInfo networkDevice) {
+ String vendor;
+
+ Map<String,byte[]> attributes = networkDevice.getAttributes();
+ vendor = getString(attributes.get(ATTRIBUTE__MFG));
+ if (!TextUtils.isEmpty(vendor)) return vendor;
+ vendor = getString(attributes.get(ATTRIBUTE__USB_MFG));
+ if (!TextUtils.isEmpty(vendor)) return vendor;
+
+ return null;
+ }
+
+ private static boolean containsVendor(String container, String[] vendorValues) {
+ if ((container == null) || (vendorValues == null)) return false;
+ for (String value : vendorValues) {
+ if (containsString(container, value)
+ || containsString(container.toLowerCase(Locale.US), value.toLowerCase(Locale.US))
+ || containsString(container.toUpperCase(Locale.US), value.toUpperCase(Locale.US)))
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean containsString(String container, String contained) {
+ return (container != null) && (contained != null) && (container.equalsIgnoreCase(contained) || container.contains(contained + " "));
+ }
+}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterHashMap.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterHashMap.java
new file mode 100644
index 0000000..032fe22
--- /dev/null
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterHashMap.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 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.printservice.recommendation.plugin.samsung;
+
+import android.net.nsd.NsdServiceInfo;
+
+import java.util.HashMap;
+
+final class PrinterHashMap extends HashMap<String, NsdServiceInfo> {
+ public static String getKey(NsdServiceInfo serviceInfo) {
+ return serviceInfo.getServiceName();
+ }
+ public NsdServiceInfo addPrinter(NsdServiceInfo device) {
+ return put(getKey(device), device);
+ }
+ public NsdServiceInfo removePrinter(NsdServiceInfo device) {
+ return remove(getKey(device));
+ }
+}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/SamsungRecommendationPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/SamsungRecommendationPlugin.java
new file mode 100644
index 0000000..e5b8a0f
--- /dev/null
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/SamsungRecommendationPlugin.java
@@ -0,0 +1,102 @@
+/*
+(c) Copyright 2016 Samsung Electronics..
+
+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.printservice.recommendation.plugin.samsung;
+
+import android.content.Context;
+import android.net.nsd.NsdServiceInfo;
+import android.text.TextUtils;
+
+import java.util.Locale;
+import java.util.Map;
+
+import com.android.printservice.recommendation.R;
+
+public class SamsungRecommendationPlugin extends ServiceRecommendationPlugin {
+
+ private static final String TAG = "SamsungRecommendation";
+
+ private static final String ATTR_USB_MFG = "usb_MFG";
+ private static final String ATTR_MFG = "mfg";
+ private static final String ATTR_USB_MDL = "usb_MDL";
+ private static final String ATTR_MDL = "mdl";
+ private static final String ATTR_PRODUCT = "product";
+ private static final String ATTR_TY = "ty";
+
+ private static String[] mNotSupportedDevices = new String[]{
+ "SCX-5x15",
+ "SF-555P",
+ "CF-555P",
+ "SCX-4x16",
+ "SCX-4214F",
+ "CLP-500",
+ "CJX-",
+ "MJC-"
+ };
+
+ private static boolean isSupportedModel(String model) {
+ if (!TextUtils.isEmpty(model)) {
+ String modelToUpper = model.toUpperCase(Locale.US);
+ for (String unSupportedPrinter : mNotSupportedDevices) {
+ if (modelToUpper.contains(unSupportedPrinter)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public SamsungRecommendationPlugin(Context context) {
+ super(context, R.string.plugin_vendor_samsung, new VendorInfo(context.getResources(), R.array.known_print_vendor_info_for_samsung), new String[]{"_pdl-datastream._tcp"});
+ }
+
+ @Override
+ public boolean matchesCriteria(String vendor, NsdServiceInfo nsdServiceInfo) {
+ if (!TextUtils.equals(vendor, mVendorInfo.mVendorID)) return false;
+
+ String modelName = getModelName(nsdServiceInfo);
+ if (modelName != null) {
+ return (isSupportedModel(modelName));
+ }
+ return false;
+ }
+
+ private String getModelName(NsdServiceInfo resolvedDevice) {
+ Map<String,byte[]> attributes = resolvedDevice.getAttributes();
+ String usb_mfg = MDnsUtils.getString(attributes.get(ATTR_USB_MFG));
+ if (TextUtils.isEmpty(usb_mfg)) {
+ usb_mfg = MDnsUtils.getString(attributes.get(ATTR_MFG));
+ }
+
+ String usb_mdl = MDnsUtils.getString(attributes.get(ATTR_USB_MDL));
+ if (TextUtils.isEmpty(usb_mdl)) {
+ usb_mdl = MDnsUtils.getString(attributes.get(ATTR_MDL));
+ }
+
+ String modelName = null;
+ if (!TextUtils.isEmpty(usb_mfg) && !TextUtils.isEmpty(usb_mdl)) {
+ modelName = usb_mfg.trim() + " " + usb_mdl.trim();
+ } else {
+ modelName = MDnsUtils.getString(attributes.get(ATTR_PRODUCT));
+ if (TextUtils.isEmpty(modelName)) {
+ modelName = MDnsUtils.getString(attributes.get(ATTR_TY));
+ }
+ }
+
+ return modelName;
+ }
+}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/ServiceListener.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/ServiceListener.java
new file mode 100644
index 0000000..7bb83c9
--- /dev/null
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/ServiceListener.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 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.printservice.recommendation.plugin.samsung;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.android.printservice.recommendation.R;
+import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
+
+public class ServiceListener implements ServiceResolveQueue.ResolveCallback {
+
+ private final NsdManager mNSDManager;
+ private final Map<String, VendorInfo> mVendorInfoHashMap;
+ private final String[] mServiceType;
+ private final Observer mObserver;
+ private final ServiceResolveQueue mResolveQueue;
+ private List<NsdManager.DiscoveryListener> mListeners = new ArrayList<>();
+ public HashMap<String, PrinterHashMap> mVendorHashMap = new HashMap<>();
+
+ public interface Observer {
+ boolean matchesCriteria(String vendor, NsdServiceInfo serviceInfo);
+ void dataSetChanged();
+ }
+
+ public ServiceListener(Context context, Observer observer, String[] serviceTypes) {
+ mObserver = observer;
+ mServiceType = serviceTypes;
+ mNSDManager = (NsdManager)context.getSystemService(Context.NSD_SERVICE);
+ mResolveQueue = ServiceResolveQueue.getInstance(mNSDManager);
+
+ Map<String, VendorInfo> vendorInfoMap = new HashMap<>();
+ TypedArray testArray = context.getResources().obtainTypedArray(R.array.known_print_plugin_vendors);
+ for(int i = 0; i < testArray.length(); i++) {
+ int arrayID = testArray.getResourceId(i, 0);
+ if (arrayID != 0) {
+ VendorInfo info = new VendorInfo(context.getResources(), arrayID);
+ vendorInfoMap.put(info.mVendorID, info);
+ vendorInfoMap.put(info.mPackageName, info);
+ }
+ }
+ testArray.recycle();
+ mVendorInfoHashMap = vendorInfoMap;
+ }
+
+ @Override
+ public void serviceResolved(NsdServiceInfo nsdServiceInfo) {
+ printerFound(nsdServiceInfo);
+ }
+
+ private synchronized void printerFound(NsdServiceInfo nsdServiceInfo) {
+ if (nsdServiceInfo == null) return;
+ if (TextUtils.isEmpty(PrinterHashMap.getKey(nsdServiceInfo))) return;
+ String vendor = MDnsUtils.getVendor(nsdServiceInfo);
+ if (vendor == null) vendor = "";
+ for(Map.Entry<String,VendorInfo> entry : mVendorInfoHashMap.entrySet()) {
+ for(String vendorValues : entry.getValue().mDNSValues) {
+ if (vendor.equalsIgnoreCase(vendorValues)) {
+ vendor = entry.getValue().mVendorID;
+ break;
+ }
+ }
+ // intentional pointer check
+ //noinspection StringEquality
+ if ((vendor != entry.getValue().mVendorID) &&
+ MDnsUtils.isVendorPrinter(nsdServiceInfo, entry.getValue().mDNSValues)) {
+ vendor = entry.getValue().mVendorID;
+ }
+ // intentional pointer check
+ //noinspection StringEquality
+ if (vendor == entry.getValue().mVendorID) break;
+ }
+
+ if (TextUtils.isEmpty(vendor)) {
+ return;
+ }
+
+ if (!mObserver.matchesCriteria(vendor, nsdServiceInfo))
+ return;
+ boolean mapsChanged;
+
+ PrinterHashMap vendorHash = mVendorHashMap.get(vendor);
+ if (vendorHash == null) {
+ vendorHash = new PrinterHashMap();
+ }
+ mapsChanged = (vendorHash.addPrinter(nsdServiceInfo) == null);
+ mVendorHashMap.put(vendor, vendorHash);
+
+ if (mapsChanged) {
+ mObserver.dataSetChanged();
+ }
+ }
+
+ private synchronized void printerRemoved(NsdServiceInfo nsdServiceInfo) {
+ boolean wasRemoved = false;
+ Set<String> vendors = mVendorHashMap.keySet();
+ for(String vendor : vendors) {
+ PrinterHashMap map = mVendorHashMap.get(vendor);
+ wasRemoved |= (map.removePrinter(nsdServiceInfo) != null);
+ if (map.isEmpty()) wasRemoved |= (mVendorHashMap.remove(vendor) != null);
+ }
+ if (wasRemoved) {
+ mObserver.dataSetChanged();
+ }
+ }
+
+ public void start() {
+ stop();
+ for(final String service :mServiceType) {
+ NsdManager.DiscoveryListener listener = new NsdManager.DiscoveryListener() {
+ @Override
+ public void onStartDiscoveryFailed(String s, int i) {
+
+ }
+
+ @Override
+ public void onStopDiscoveryFailed(String s, int i) {
+
+ }
+
+ @Override
+ public void onDiscoveryStarted(String s) {
+
+ }
+
+ @Override
+ public void onDiscoveryStopped(String s) {
+
+ }
+
+ @Override
+ public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
+ mResolveQueue.queueRequest(nsdServiceInfo, ServiceListener.this);
+ }
+
+ @Override
+ public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
+ mResolveQueue.removeRequest(nsdServiceInfo, ServiceListener.this);
+ printerRemoved(nsdServiceInfo);
+ }
+ };
+ DiscoveryListenerMultiplexer.addListener(mNSDManager, service, listener);
+ mListeners.add(listener);
+ }
+ }
+
+ public void stop() {
+ for(NsdManager.DiscoveryListener listener : mListeners) {
+ DiscoveryListenerMultiplexer.removeListener(mNSDManager, listener);
+ }
+ mVendorHashMap.clear();
+ mListeners.clear();
+ }
+
+ public Pair<Integer, Integer> getCount() {
+ int count = 0;
+ for (PrinterHashMap map : mVendorHashMap.values()) {
+ count += map.size();
+ }
+ return Pair.create(mVendorHashMap.size(), count);
+ }
+}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/ServiceRecommendationPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/ServiceRecommendationPlugin.java
new file mode 100644
index 0000000..9d15f30
--- /dev/null
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/ServiceRecommendationPlugin.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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.printservice.recommendation.plugin.samsung;
+
+import android.content.Context;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.annotation.NonNull;
+import android.text.TextUtils;
+import com.android.printservice.recommendation.PrintServicePlugin;
+
+public abstract class ServiceRecommendationPlugin implements PrintServicePlugin, ServiceListener.Observer {
+
+ protected static final String PDL_ATTRIBUTE = "pdl";
+
+ protected final Object mLock = new Object();
+ protected PrinterDiscoveryCallback mCallback = null;
+ protected final ServiceListener mListener;
+ protected final NsdManager mNSDManager;
+ protected final VendorInfo mVendorInfo;
+ private final int mVendorStringID;
+
+ protected ServiceRecommendationPlugin(Context context, int vendorStringID, VendorInfo vendorInfo, String[] services) {
+ mNSDManager = (NsdManager)context.getSystemService(Context.NSD_SERVICE);
+ mVendorStringID = vendorStringID;
+ mVendorInfo = vendorInfo;
+ mListener = new ServiceListener(context, this, services);
+ }
+
+ @Override
+ public int getName() {
+ return mVendorStringID;
+ }
+
+ @NonNull
+ @Override
+ public CharSequence getPackageName() {
+ return mVendorInfo.mPackageName;
+ }
+
+ @Override
+ public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
+ synchronized (mLock) {
+ mCallback = callback;
+ }
+ mListener.start();
+ }
+
+ @Override
+ public void stop() throws Exception {
+ synchronized (mLock) {
+ mCallback = null;
+ }
+ mListener.stop();
+ }
+
+ @Override
+ public void dataSetChanged() {
+ synchronized (mLock) {
+ if (mCallback != null) mCallback.onChanged(getCount());
+ }
+ }
+
+ @Override
+ public boolean matchesCriteria(String vendor, NsdServiceInfo nsdServiceInfo) {
+ return TextUtils.equals(vendor, mVendorInfo.mVendorID);
+ }
+
+ public int getCount() {
+ return mListener.getCount().second;
+ }
+}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/ServiceResolveQueue.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/ServiceResolveQueue.java
new file mode 100644
index 0000000..e5691b7
--- /dev/null
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/ServiceResolveQueue.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 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.printservice.recommendation.plugin.samsung;
+
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.util.Pair;
+import com.android.printservice.recommendation.util.NsdResolveQueue;
+
+import java.util.LinkedList;
+
+final class ServiceResolveQueue {
+
+ private final NsdManager mNsdManager;
+ private final LinkedList<Pair<NsdServiceInfo, ResolveCallback>> mQueue = new LinkedList<>();
+ private final Object mLock = new Object();
+
+ private static Object sLock = new Object();
+ private static ServiceResolveQueue sInstance = null;
+ private final NsdResolveQueue mNsdResolveQueue;
+ private Pair<NsdServiceInfo, ResolveCallback> mCurrentRequest = null;
+
+ public static void createInstance(NsdManager nsdManager) {
+ if (sInstance == null) sInstance = new ServiceResolveQueue(nsdManager);
+ }
+
+ public static ServiceResolveQueue getInstance(NsdManager nsdManager) {
+ synchronized (sLock) {
+ createInstance(nsdManager);
+ return sInstance;
+ }
+ }
+
+ public static void destroyInstance() {
+ sInstance = null;
+ }
+
+ public interface ResolveCallback {
+ void serviceResolved(NsdServiceInfo nsdServiceInfo);
+ }
+
+ public ServiceResolveQueue(NsdManager nsdManager) {
+ mNsdManager = nsdManager;
+ mNsdResolveQueue = NsdResolveQueue.getInstance();
+ }
+
+ public void queueRequest(NsdServiceInfo serviceInfo, ResolveCallback callback) {
+ synchronized (mLock) {
+ Pair<NsdServiceInfo, ResolveCallback> newRequest = Pair.create(serviceInfo, callback);
+ if (mQueue.contains(newRequest)) return;
+ mQueue.add(newRequest);
+ makeNextRequest();
+ }
+ }
+
+ public void removeRequest(NsdServiceInfo serviceInfo, ResolveCallback callback) {
+ synchronized (mLock) {
+ Pair<NsdServiceInfo, ResolveCallback> newRequest = Pair.create(serviceInfo, callback);
+ mQueue.remove(newRequest);
+ if ((mCurrentRequest != null) && newRequest.equals(mCurrentRequest)) mCurrentRequest = null;
+ }
+ }
+
+ private void makeNextRequest() {
+ synchronized (mLock) {
+ if (mCurrentRequest != null) return;
+ if (mQueue.isEmpty()) return;
+ mCurrentRequest = mQueue.removeFirst();
+ mNsdResolveQueue.resolve(mNsdManager, mCurrentRequest.first,
+ new NsdManager.ResolveListener() {
+ @Override
+ public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int i) {
+ synchronized (mLock) {
+ if (mCurrentRequest != null) mQueue.add(mCurrentRequest);
+ makeNextRequest();
+ }
+ }
+
+ @Override
+ public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
+ synchronized (mLock) {
+ if (mCurrentRequest != null) {
+ mCurrentRequest.second.serviceResolved(nsdServiceInfo);
+ mCurrentRequest = null;
+ }
+ makeNextRequest();
+ }
+ }
+ });
+
+ }
+ }
+
+
+}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/VendorInfo.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/VendorInfo.java
new file mode 100644
index 0000000..0ebb4e4
--- /dev/null
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/VendorInfo.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.printservice.recommendation.plugin.samsung;
+
+import android.content.res.Resources;
+
+import java.util.Arrays;
+
+public final class VendorInfo {
+
+ public final String mPackageName;
+ public final String mVendorID;
+ public final String[] mDNSValues;
+ public final int mID;
+
+ public VendorInfo(Resources resources, int vendor_info_id) {
+ mID = vendor_info_id;
+ String[] data = resources.getStringArray(vendor_info_id);
+ if ((data == null) || (data.length < 2)) {
+ data = new String[] { null, null };
+ }
+ mPackageName = data[0];
+ mVendorID = data[1];
+ mDNSValues = (data.length > 2) ? Arrays.copyOfRange(data, 2, data.length) : new String[]{};
+ }
+}
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 6438564..ca0248e 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -35,6 +35,7 @@
android:importantForAccessibility="no" />
<View
+ android:id="@+id/nav_bar_background"
android:layout_width="match_parent"
android:layout_height="@dimen/navigation_bar_size"
android:layout_gravity="bottom"
diff --git a/packages/SystemUI/res/values/values_tv.xml b/packages/SystemUI/res/values/values_tv.xml
index dc0483f..9259415 100644
--- a/packages/SystemUI/res/values/values_tv.xml
+++ b/packages/SystemUI/res/values/values_tv.xml
@@ -16,5 +16,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item format="float" type="integer" name="unselected_scale">1.0</item>
<item format="float" type="integer" name="selected_scale">1.259</item>
- <item format="float" type="integer" name="dismiss_unselected_alpha">0.1</item>
+ <item format="float" type="integer" name="dismiss_unselected_alpha">0.3</item>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 61933b4..4bf85c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -19,6 +19,7 @@
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
+import android.content.res.Configuration;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -103,6 +104,17 @@
mRecyclerView.setItemAnimator(animator);
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ View navBackdrop = findViewById(R.id.nav_bar_background);
+ if (navBackdrop != null) {
+ boolean shouldShow = newConfig.smallestScreenWidthDp >= 600
+ || newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE;
+ navBackdrop.setVisibility(shouldShow ? View.VISIBLE : View.GONE);
+ }
+ }
+
public void setHost(QSTileHost host) {
mHost = host;
mPhoneStatusBar = host.getPhoneStatusBar();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 61a92b4..60c24d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -286,10 +286,11 @@
private void selectPosition(int position, View v) {
// Remove the placeholder.
mAccessibilityMoving = false;
- move(mAccessibilityFromIndex, position, v);
- mTiles.remove(mEditIndex);
+ mTiles.remove(mEditIndex--);
notifyItemRemoved(mEditIndex - 1);
+ move(mAccessibilityFromIndex, position, v);
updateDividerLocations();
+ notifyDataSetChanged();
saveSpecs(mHost);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 212f179..16b1158 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -81,6 +81,8 @@
private IQSService mService;
private boolean mUnbindImmediate;
private TileChangeListener mChangeListener;
+ // Return value from bindServiceAsUser, determines whether safe to call unbind.
+ private boolean mIsBound;
public TileLifecycleManager(Handler handler, Context context, Intent intent, UserHandle user) {
mContext = context;
@@ -132,7 +134,7 @@
}
if (DEBUG) Log.d(TAG, "Binding service " + mIntent + " " + mUser);
mBindTryCount++;
- mContext.bindServiceAsUser(mIntent, this,
+ mIsBound = mContext.bindServiceAsUser(mIntent, this,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
mUser);
} else {
@@ -140,7 +142,10 @@
// Give it another chance next time it needs to be bound, out of kindness.
mBindTryCount = 0;
mWrapper = null;
- mContext.unbindService(this);
+ if (mIsBound) {
+ mContext.unbindService(this);
+ mIsBound = false;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
index c3efe64..8218599 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
@@ -104,6 +104,7 @@
mTargetView.getDismissIconView().setAlpha(mDismissIconAlpha * level);
mTargetView.getThumbnailView().setZ(z);
+ mTargetView.getDismissIconView().setZ(z);
}
public float getFocusProgress() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 2eede99..bd85748 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -38,6 +38,8 @@
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
@@ -85,6 +87,24 @@
mCornerRadius = getResources().getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.isTouchExplorationEnabled()) {
+ mDismissIconView.setFocusable(true);
+ mDismissIconView.setFocusableInTouchMode(true);
+ mDismissIconView.setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ setDismissState(true);
+ } else {
+ setDismissState(false);
+ }
+ }
+ });
+ } else {
+ mDismissIconView.setFocusable(false);
+ mDismissIconView.setFocusableInTouchMode(false);
+ }
mViewFocusAnimator = new ViewFocusAnimator(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index e66dc74..63933d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -189,6 +189,7 @@
protected boolean mVisible;
protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
+ protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
/**
* Notifications with keys in this set are not actually around anymore. We kept them around
@@ -968,7 +969,7 @@
return vetoButton;
}
- private void performRemoveNotification(StatusBarNotification n, boolean removeView) {
+ protected void performRemoveNotification(StatusBarNotification n, boolean removeView) {
final String pkg = n.getPackageName();
final String tag = n.getTag();
final int id = n.getId();
@@ -980,6 +981,9 @@
mKeysKeptForRemoteInput.remove(n.getKey());
removeView = true;
}
+ if (mRemoteInputEntriesToRemoveOnCollapse.remove(mNotificationData.get(n.getKey()))) {
+ removeView = true;
+ }
if (removeView) {
removeNotification(n.getKey(), null);
}
@@ -2085,8 +2089,7 @@
/**
* The LEDs are turned off when the notification panel is shown, even just a little bit.
- * See also NotificationStackScrollLayout.setIsExpanded() for another place where we
- * attempt to do this.
+ * See also PhoneStatusBar.setPanelExpanded for another place where we attempt to do this.
*/
protected void handleVisibleToUserChanged(boolean visibleToUser) {
try {
@@ -2326,8 +2329,9 @@
Entry entry = mNotificationData.get(key);
if (entry == null) {
return;
- } else if (mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
+ } else {
mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+ mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
}
Notification n = notification.getNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 0a28331..c53ab59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -702,8 +702,13 @@
return mRemoved;
}
- public void setRemoved(boolean removed) {
- mRemoved = removed;
+ public void setRemoved() {
+ mRemoved = true;
+
+ mPrivateLayout.setRemoved();
+ if (mChildrenContainer != null) {
+ mChildrenContainer.setRemoved();
+ }
}
public NotificationChildrenContainer getChildrenContainer() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index a0c1372..f3c666f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -263,6 +263,7 @@
invalidateOutline();
selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
mForceSelectNextLayout = false;
+ updateExpandButtons(mExpandable);
}
@Override
@@ -1053,4 +1054,13 @@
public HybridNotificationView getSingleLineView() {
return mSingleLineView;
}
+
+ public void setRemoved() {
+ if (mExpandedRemoteInput != null) {
+ mExpandedRemoteInput.setRemoved();
+ }
+ if (mHeadsUpRemoteInput != null) {
+ mHeadsUpRemoteInput.setRemoved();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6a37099..522f250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -220,6 +220,20 @@
super.onFinishInflate();
mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view);
+ mClockView = (TextView) findViewById(R.id.clock_view);
+
+ mNotificationContainerParent = (NotificationsQuickSettingsContainer)
+ findViewById(R.id.notification_container_parent);
+ mNotificationStackScroller = (NotificationStackScrollLayout)
+ findViewById(R.id.notification_stack_scroller);
+ mNotificationStackScroller.setOnHeightChangedListener(this);
+ mNotificationStackScroller.setOverscrollTopChangedListener(this);
+ mNotificationStackScroller.setOnEmptySpaceClickListener(this);
+ mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
+ mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
+ mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
+ mLastOrientation = getResources().getConfiguration().orientation;
+
mQsAutoReinflateContainer =
(AutoReinflateContainer) findViewById(R.id.qs_auto_reinflate_container);
mQsAutoReinflateContainer.addInflateListener(new InflateListener() {
@@ -229,32 +243,20 @@
mQsContainer.setPanelView(NotificationPanelView.this);
mQsContainer.getHeader().findViewById(R.id.expand_indicator)
.setOnClickListener(NotificationPanelView.this);
- }
- });
- mClockView = (TextView) findViewById(R.id.clock_view);
- mNotificationContainerParent = (NotificationsQuickSettingsContainer)
- findViewById(R.id.notification_container_parent);
- mNotificationStackScroller = (NotificationStackScrollLayout)
- findViewById(R.id.notification_stack_scroller);
- mNotificationStackScroller.setOnHeightChangedListener(this);
- mNotificationStackScroller.setOverscrollTopChangedListener(this);
- mNotificationStackScroller.setOnEmptySpaceClickListener(this);
- mNotificationStackScroller.setQsContainer(mQsContainer);
- mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
- mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
- mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
- mLastOrientation = getResources().getConfiguration().orientation;
- // recompute internal state when qspanel height changes
- mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- final int height = bottom - top;
- final int oldHeight = oldBottom - oldTop;
- if (height != oldHeight) {
- onQsHeightChanged();
- }
+ // recompute internal state when qspanel height changes
+ mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ final int height = bottom - top;
+ final int oldHeight = oldBottom - oldTop;
+ if (height != oldHeight) {
+ onQsHeightChanged();
+ }
+ }
+ });
+ mNotificationStackScroller.setQsContainer(mQsContainer);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f487357..31fa3bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -263,6 +263,14 @@
* This affects the status bar UI. */
private static final boolean FREEFORM_WINDOW_MANAGEMENT;
+ /**
+ * How long to wait before auto-dismissing a notification that was kept for remote input, and
+ * has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel
+ * these given that they technically don't exist anymore. We wait a bit in case the app issues
+ * an update.
+ */
+ private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200;
+
static {
boolean onlyCoreApps;
boolean freeformWindowManagement;
@@ -1181,16 +1189,24 @@
mIconPolicy.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
- if (FORCE_REMOTE_INPUT_HISTORY) {
- mRemoteInputController.addCallback(new RemoteInputController.Callback() {
- @Override
- public void onRemoteInputSent(Entry entry) {
- if (mKeysKeptForRemoteInput.contains(entry.key)) {
- removeNotification(entry.key, null);
- }
+ mRemoteInputController.addCallback(new RemoteInputController.Callback() {
+ @Override
+ public void onRemoteInputSent(Entry entry) {
+ if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(entry.key)) {
+ removeNotification(entry.key, null);
+ } else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) {
+ // We're currently holding onto this notification, but from the apps point of
+ // view it is already canceled, so we'll need to cancel it on the apps behalf
+ // after sending - unless the app posts an update in the mean time, so wait a
+ // bit.
+ mHandler.postDelayed(() -> {
+ if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) {
+ removeNotification(entry.key, null);
+ }
+ }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
}
- });
- }
+ }
+ });
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
@@ -1507,8 +1523,15 @@
return;
}
Entry entry = mNotificationData.get(key);
+
+ if (entry != null && mRemoteInputController.isRemoteInputActive(entry)) {
+ mLatestRankingMap = ranking;
+ mRemoteInputEntriesToRemoveOnCollapse.add(entry);
+ return;
+ }
+
if (entry != null && entry.row != null) {
- entry.row.setRemoved(true);
+ entry.row.setRemoved();
}
// Let's remove the children if this was a summary
handleGroupSummaryRemoved(key, ranking);
@@ -1556,7 +1579,7 @@
toRemove.get(i).setKeepInParent(true);
// we need to set this state earlier as otherwise we might generate some weird
// animations
- toRemove.get(i).setRemoved(true);
+ toRemove.get(i).setRemoved();
}
for (int i = 0; i < toRemove.size(); i++) {
removeNotification(toRemove.get(i).getStatusBarNotification().getKey(), ranking);
@@ -1568,6 +1591,15 @@
}
@Override
+ protected void performRemoveNotification(StatusBarNotification n, boolean removeView) {
+ Entry entry = mNotificationData.get(n.getKey());
+ if (mRemoteInputController.isRemoteInputActive(entry)) {
+ mRemoteInputController.removeRemoteInput(entry);
+ }
+ super.performRemoveNotification(n, removeView);
+ }
+
+ @Override
protected void refreshLayout(int layoutDirection) {
if (mNavigationBarView != null) {
mNavigationBarView.setLayoutDirection(layoutDirection);
@@ -2464,6 +2496,26 @@
public void setPanelExpanded(boolean isExpanded) {
mStatusBarWindowManager.setPanelExpanded(isExpanded);
+
+ if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
+ if (DEBUG) {
+ Log.v(TAG, "clearing notification effects from setPanelExpanded");
+ }
+ clearNotificationEffects();
+ }
+
+ if (!isExpanded) {
+ removeRemoteInputEntriesKeptUntilCollapsed();
+ }
+ }
+
+ private void removeRemoteInputEntriesKeptUntilCollapsed() {
+ for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) {
+ Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i);
+ mRemoteInputController.removeRemoteInput(entry);
+ removeNotification(entry.key, mLatestRankingMap);
+ }
+ mRemoteInputEntriesToRemoveOnCollapse.clear();
}
public void onScreenTurnedOff() {
@@ -4207,6 +4259,9 @@
|| (state == StatusBarState.SHADE && isGoingToNotificationShade()))) {
clearNotificationEffects();
}
+ if (state == StatusBarState.KEYGUARD) {
+ removeRemoteInputEntriesKeptUntilCollapsed();
+ }
mState = state;
mGroupManager.setStatusBarState(state);
mFalsingManager.setStatusBarState(state);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 095265a..2f522f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,6 +73,7 @@
private ScrollContainer mScrollContainer;
private View mScrollContainerChild;
+ private boolean mRemoved;
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -171,7 +172,12 @@
public void onDefocus() {
mController.removeRemoteInput(mEntry);
mEntry.remoteInputText = mEditText.getText();
- setVisibility(INVISIBLE);
+
+ // During removal, we get reattached and lose focus. Not hiding in that
+ // case to prevent flicker.
+ if (!mRemoved) {
+ setVisibility(INVISIBLE);
+ }
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
mEntry.notification.getPackageName());
}
@@ -347,6 +353,10 @@
return mPendingIntent;
}
+ public void setRemoved() {
+ mRemoved = true;
+ }
+
/**
* An EditText that changes appearance based on whether it's focusable and becomes
* un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -416,6 +426,15 @@
}
@Override
+ public boolean onCheckIsTextEditor() {
+ // Stop being editable while we're being removed. During removal, we get reattached,
+ // and editable views get their spellchecking state re-evaluated which is too costly
+ // during the removal animation.
+ boolean flyingOut = mRemoteInputView != null && mRemoteInputView.mRemoved;
+ return !flyingOut && super.onCheckIsTextEditor();
+ }
+
+ @Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 701a844..cb0b848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -846,4 +846,12 @@
mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
mNotificationParent.getNotificationColor());
}
+
+ public void setRemoved() {
+ int childCount = mChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ ExpandableNotificationRow child = mChildren.get(i);
+ child.setRemoved();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 3ce9866..19f4074 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2511,9 +2511,9 @@
mNeedsAnimation = false;
}
if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
+ setAnimationRunning(true);
mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
mGoToFullShadeDelay);
- setAnimationRunning(true);
mAnimationEvents.clear();
updateBackground();
updateViewShadows();
@@ -2958,16 +2958,6 @@
if (changed) {
if (!mIsExpanded) {
mGroupManager.collapseAllGroups();
- } else {
- // XXX: HACK: we should not be clearing notification effects from way down here.
- // But at the moment we don't have a reliable way to know when the window is
- // actually exposed to the air, so
- if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) {
- if (DEBUG) {
- Log.v(TAG, "clearing notification effects from scroller");
- }
- mPhoneStatusBar.clearNotificationEffects();
- }
}
updateNotificationAnimationStates();
updateChronometers();
@@ -3141,6 +3131,7 @@
mListener.onChildLocationsChanged(this);
}
runAnimationFinishedRunnables();
+ setAnimationRunning(false);
updateBackground();
updateViewShadows();
}
@@ -3257,6 +3248,7 @@
maxLength = Math.max(mDarkAnimationOriginIndex,
getNotGoneChildCount() - mDarkAnimationOriginIndex - 1);
}
+ maxLength = Math.max(0, maxLength);
long delay = maxLength * StackStateAnimator.ANIMATION_DELAY_PER_ELEMENT_DARK;
fadeAnimator.setStartDelay(delay);
fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index dab7d70..b5b0cd8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -2180,8 +2180,9 @@
Preconditions.checkNotNull(token);
for (int i = 0; i < AppOpsManager._NUM_OP; i++) {
String restriction = AppOpsManager.opToRestriction(i);
- if (restriction != null && restrictions.getBoolean(restriction, false)) {
- setUserRestrictionNoCheck(i, true, token, userHandle, null);
+ if (restriction != null) {
+ setUserRestrictionNoCheck(i, restrictions.getBoolean(restriction, false), token,
+ userHandle, null);
}
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index f30a126..1f88be5 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1457,14 +1457,17 @@
BluetoothAdapter.STATE_OFF);
sendBluetoothServiceDownCallback();
- mBluetoothLock.writeLock().lock();
- if (mBluetooth != null) {
- mBluetooth = null;
- // Unbind
- mContext.unbindService(mConnection);
+ try {
+ mBluetoothLock.writeLock().lock();
+ if (mBluetooth != null) {
+ mBluetooth = null;
+ // Unbind
+ mContext.unbindService(mConnection);
+ }
+ mBluetoothGatt = null;
+ } finally {
+ mBluetoothLock.writeLock().unlock();
}
- mBluetoothGatt = null;
- mBluetoothLock.writeLock().unlock();
SystemClock.sleep(100);
@@ -1765,14 +1768,17 @@
sendBluetoothServiceDownCallback();
- mBluetoothLock.writeLock().lock();
- if (mBluetooth != null) {
- mBluetooth = null;
- // Unbind
- mContext.unbindService(mConnection);
+ try {
+ mBluetoothLock.writeLock().lock();
+ if (mBluetooth != null) {
+ mBluetooth = null;
+ // Unbind
+ mContext.unbindService(mConnection);
+ }
+ mBluetoothGatt = null;
+ } finally {
+ mBluetoothLock.writeLock().unlock();
}
- mBluetoothGatt = null;
- mBluetoothLock.writeLock().unlock();
mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
mState = BluetoothAdapter.STATE_OFF;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 95a9781..2f2d8a4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2245,6 +2245,11 @@
final boolean wasDefault = isDefaultNetwork(nai);
if (wasDefault) {
mDefaultInetConditionPublished = 0;
+ // Log default network disconnection before required book-keeping.
+ // Let rematchAllNetworksAndRequests() below record a new default network event
+ // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
+ // whose timestamps tell how long it takes to recover a default network.
+ logDefaultNetworkEvent(null, nai);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -2278,10 +2283,6 @@
}
mLegacyTypeTracker.remove(nai, wasDefault);
rematchAllNetworksAndRequests(null, 0);
- if (wasDefault && getDefaultNetwork() == null) {
- // Log that we lost the default network and there is no replacement.
- logDefaultNetworkEvent(null, nai);
- }
if (nai.created) {
// Tell netd to clean up the configuration for this network
// (routing rules, DNS, etc).
@@ -4584,7 +4585,7 @@
teardownUnneededNetwork(oldNetwork);
}
- private void makeDefault(NetworkAgentInfo newNetwork, NetworkAgentInfo prevNetwork) {
+ private void makeDefault(NetworkAgentInfo newNetwork) {
if (DBG) log("Switching to new default network: " + newNetwork);
setupDataActivityTracking(newNetwork);
try {
@@ -4596,7 +4597,6 @@
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
updateTcpBufferSizes(newNetwork);
setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
- logDefaultNetworkEvent(newNetwork, prevNetwork);
}
// Handles a network appearing or improving its score.
@@ -4747,7 +4747,9 @@
}
if (isNewDefault) {
// Notify system services that this network is up.
- makeDefault(newNetwork, oldDefaultNetwork);
+ makeDefault(newNetwork);
+ // Log 0 -> X and Y -> X default network transitions, where X is the new default.
+ logDefaultNetworkEvent(newNetwork, oldDefaultNetwork);
synchronized (ConnectivityService.this) {
// have a new default network, release the transition wakelock in
// a second if it's held. The second pause is to allow apps
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 4477e8b..1d8bb6b 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -43,6 +43,7 @@
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.Parcel;
@@ -111,13 +112,12 @@
private static final int FBE_ENCRYPTED_NOTIFICATION = 0;
private static final boolean DEBUG = false;
- private static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
- private static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
private static final int PROFILE_KEY_IV_SIZE = 12;
private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
private final Object mSeparateChallengeLock = new Object();
private final Context mContext;
+ private final Handler mHandler;
private final LockSettingsStorage mStorage;
private final LockSettingsStrongAuth mStrongAuth;
private final SynchronizedStrongAuthTracker mStrongAuthTracker;
@@ -244,6 +244,7 @@
public LockSettingsService(Context context) {
mContext = context;
+ mHandler = new Handler();
mStrongAuth = new LockSettingsStrongAuth(context);
// Open the database
@@ -367,10 +368,20 @@
hideEncryptionNotification(new UserHandle(userId));
}
- public void onUnlockUser(int userId) {
+ public void onUnlockUser(final int userId) {
// Hide notification first, as tie managed profile lock takes time
hideEncryptionNotification(new UserHandle(userId));
- tieManagedProfileLockIfNecessary(userId, null);
+
+ if (mUserManager.getUserInfo(userId).isManagedProfile()) {
+ // As tieManagedProfileLockIfNecessary() may try to unlock user, we should not do it
+ // in onUnlockUser() synchronously, otherwise it may cause a deadlock
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ tieManagedProfileLockIfNecessary(userId, null);
+ }
+ });
+ }
// Now we have unlocked the parent user we should show notifications
// about any profiles that exist.
@@ -687,7 +698,7 @@
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey decryptionKey = (SecretKey) keyStore.getKey(
- PROFILE_KEY_NAME_DECRYPT + userId, null);
+ LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId, null);
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
@@ -734,12 +745,6 @@
}
};
- // Turn off quite mode if it's enabled, only managed profile can return true for now, it
- // will return false if it is not a managed profile.
- if (mUserManager.isQuietModeEnabled(new UserHandle(userId))) {
- mUserManager.setQuietModeEnabled(userId, false);
- }
-
try {
ActivityManagerNative.getDefault().unlockUser(userId, token, secret, listener);
} catch (RemoteException e) {
@@ -758,7 +763,6 @@
// Unlock managed profile with unified lock
if (pi.isManagedProfile()
&& !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id)
- && !pi.isQuietModeEnabled()
&& mStorage.hasChildProfileLock(pi.id)) {
unlockChildProfile(pi.id);
}
@@ -976,14 +980,14 @@
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
keyStore.setEntry(
- PROFILE_KEY_NAME_ENCRYPT + userId,
+ LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId,
new java.security.KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
keyStore.setEntry(
- PROFILE_KEY_NAME_DECRYPT + userId,
+ LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId,
new java.security.KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
@@ -994,7 +998,7 @@
// Key imported, obtain a reference to it.
SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
- PROFILE_KEY_NAME_ENCRYPT + userId, null);
+ LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId, null);
// The original key can now be discarded.
Cipher cipher = Cipher.getInstance(
@@ -1393,8 +1397,8 @@
try {
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
- keyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + targetUserId);
- keyStore.deleteEntry(PROFILE_KEY_NAME_DECRYPT + targetUserId);
+ keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + targetUserId);
+ keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + targetUserId);
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
| IOException e) {
// We have tried our best to remove all keys
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7a59703..a8a8553 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -201,6 +201,7 @@
import android.service.voice.VoiceInteractionSession;
import android.text.format.DateUtils;
import android.text.format.Time;
+import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -16863,6 +16864,13 @@
return false;
}
+ // If the app is a regular app (uid >= 10000) and not the system server or phone
+ // process, etc, then mark it as being in full backup so that certain calls to the
+ // process can be blocked. This is not reset to false anywhere because we kill the
+ // process after the full backup is done and the ProcessRecord will vaporize anyway.
+ if (UserHandle.isApp(app.uid) && backupMode == IApplicationThread.BACKUP_MODE_FULL) {
+ proc.inFullBackup = true;
+ }
r.app = proc;
mBackupTarget = r;
mBackupAppName = app.packageName;
@@ -17390,15 +17398,24 @@
|| AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
|| LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
- || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)) {
+ || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
+ || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)) {
// Broadcast is either protected, or it's a public action that
// we've relaxed, so it's fine for system internals to send.
} else {
// The vast majority of broadcasts sent from system internals
// should be protected to avoid security holes, so yell loudly
// to ensure we examine these cases.
- Log.wtf(TAG, "Sending non-protected broadcast " + action
- + " from system", new Throwable());
+ if (callerApp != null) {
+ Log.wtf(TAG, "Sending non-protected broadcast " + action
+ + " from system " + callerApp.toShortString() + " pkg " + callerPackage,
+ new Throwable());
+ } else {
+ Log.wtf(TAG, "Sending non-protected broadcast " + action
+ + " from system uid " + UserHandle.formatUid(callingUid)
+ + " pkg " + callerPackage,
+ new Throwable());
+ }
}
} else {
@@ -18325,21 +18342,21 @@
EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
- final Locale locale;
- if (values.getLocales().size() == 1) {
- // This is an optimization to avoid the JNI call when the result of
- // getFirstMatch() does not depend on the supported locales.
- locale = values.getLocales().get(0);
- } else {
+ final LocaleList locales = values.getLocales();
+ int bestLocaleIndex = 0;
+ if (locales.size() > 1) {
if (mSupportedSystemLocales == null) {
mSupportedSystemLocales =
Resources.getSystem().getAssets().getLocales();
}
- locale = values.getLocales().getFirstMatch(mSupportedSystemLocales);
+ bestLocaleIndex = Math.max(0,
+ locales.getFirstMatchIndex(mSupportedSystemLocales));
}
- SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
+ SystemProperties.set("persist.sys.locale",
+ locales.get(bestLocaleIndex).toLanguageTag());
+ LocaleList.setDefault(locales, bestLocaleIndex);
mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
- locale));
+ locales.get(bestLocaleIndex)));
}
mConfigurationSeq++;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 0acc2a0..8ffc6f3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -258,6 +258,11 @@
if (app.thread == null) {
throw new RemoteException();
}
+ if (app.inFullBackup) {
+ skipReceiverLocked(r);
+ return;
+ }
+
r.receiver = app.thread.asBinder();
r.curApp = app;
app.curReceiver = r;
@@ -341,13 +346,17 @@
}
if (r != null) {
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
+ skipReceiverLocked(r);
}
}
+ private void skipReceiverLocked(BroadcastRecord r) {
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+ }
+
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
@@ -641,9 +650,17 @@
try {
if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
"Delivering to " + filter + " : " + r);
- performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
- new Intent(r.intent), r.resultCode, r.resultData,
- r.resultExtras, r.ordered, r.initialSticky, r.userId);
+ if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
+ // Skip delivery if full backup in progress
+ // If it's an ordered broadcast, we need to continue to the next receiver.
+ if (ordered) {
+ skipReceiverLocked(r);
+ }
+ } else {
+ performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
+ new Intent(r.intent), r.resultCode, r.resultData,
+ r.resultExtras, r.ordered, r.initialSticky, r.userId);
+ }
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 059acbd..da18f327 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -192,6 +192,9 @@
// app that installed the package.
ComponentName errorReportReceiver;
+ // Process is currently hosting a backup agent for backup or restore
+ public boolean inFullBackup;
+
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 595d086..67f96d8 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -938,6 +938,17 @@
boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
IProgressListener listener) {
synchronized (mService) {
+ // TODO Move this block outside of synchronized if it causes lock contention
+ if (!StorageManager.isUserKeyUnlocked(userId)) {
+ final UserInfo userInfo = getUserInfo(userId);
+ final IMountService mountService = getMountService();
+ try {
+ // We always want to unlock user storage, even user is not started yet
+ mountService.unlockUserKey(userId, userInfo.serialNumber, token, secret);
+ } catch (RemoteException | RuntimeException e) {
+ Slog.w(TAG, "Failed to unlock: " + e.getMessage());
+ }
+ }
// Bail if user isn't actually running, otherwise register the given
// listener to watch for unlock progress
final UserState uss = mStartedUsers.get(userId);
@@ -948,19 +959,6 @@
uss.mUnlockProgress.addListener(listener);
}
- // TODO Move this block outside of synchronized if it causes lock contention
- if (!StorageManager.isUserKeyUnlocked(userId)) {
- final UserInfo userInfo = getUserInfo(userId);
- final IMountService mountService = getMountService();
- try {
- mountService.unlockUserKey(userId, userInfo.serialNumber, token, secret);
- } catch (RemoteException | RuntimeException e) {
- Slog.w(TAG, "Failed to unlock: " + e.getMessage());
- notifyFinished(userId, listener);
- return false;
- }
- }
-
finishUserUnlocking(uss);
// We just unlocked a user, so let's now attempt to unlock any
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 15a63ec..e3dcf140 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -36,6 +36,7 @@
import com.android.server.notification.NotificationManagerService.DumpFilter;
import java.io.PrintWriter;
+import java.util.Calendar;
import java.util.TimeZone;
/**
@@ -237,6 +238,14 @@
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Slog.d(TAG, "onReceive " + intent.getAction());
+ if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
+ for (Uri conditionId : mSubscriptions.keySet()) {
+ final ScheduleCalendar cal = mSubscriptions.get(conditionId);
+ if (cal != null) {
+ cal.setTimeZone(Calendar.getInstance().getTimeZone());
+ }
+ }
+ }
evaluateSubscriptions();
}
};
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2f72607..5fdb1e8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5421,7 +5421,7 @@
// If the service process is killed, then ask it to clean up after itself
final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
SYSUI_SCREENSHOT_ERROR_RECEIVER);
- Intent errorIntent = new Intent();
+ Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT);
errorIntent.setComponent(errorComponent);
errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
Intent.FLAG_RECEIVER_FOREGROUND);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b773a4e..f57e83aa 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -451,6 +451,16 @@
destroySurfaces();
}
+ void markSavedSurfaceExiting() {
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ final WindowState w = allAppWindows.get(i);
+ if (w.isAnimatingInvisibleWithSavedSurface()) {
+ w.mAnimatingExit = true;
+ w.mWinAnimator.mAnimating = true;
+ }
+ }
+ }
+
void restoreSavedSurfaces() {
if (!canRestoreSurfaces()) {
clearVisibleBeforeClientHidden();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index aaed8ca..d363712 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2277,6 +2277,19 @@
}
}
+ private void setupWindowForRemoveOnExit(WindowState win) {
+ win.mRemoveOnExit = true;
+ win.setDisplayLayoutNeeded();
+ // Request a focus update as this window's input channel is already gone. Otherwise
+ // we could have no focused window in input manager.
+ final boolean focusChanged = updateFocusedWindowLocked(
+ UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
+ mWindowPlacerLocked.performSurfacePlacement();
+ if (focusChanged) {
+ mInputMonitor.updateInputWindowsLw(false /*force*/);
+ }
+ }
+
public void removeWindow(Session session, IWindow client) {
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
@@ -2358,14 +2371,7 @@
// Do not set mAnimatingExit to true here, it will cause the surface to be hidden
// immediately after the enter animation is done. If the app is not yet drawn then
// it will show up as a flicker.
- win.mRemoveOnExit = true;
- // Request a focus update as this window's input channel is already gone. Otherwise
- // we could have no focused window in input manager.
- final boolean focusChanged = updateFocusedWindowLocked(
- UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
- if (focusChanged) {
- mInputMonitor.updateInputWindowsLw(false /*force*/);
- }
+ setupWindowForRemoveOnExit(win);
Binder.restoreCallingIdentity(origId);
return;
}
@@ -2417,17 +2423,10 @@
// The exit animation is running or should run... wait for it!
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Not removing " + win + " due to exit animation ");
- win.mRemoveOnExit = true;
- win.setDisplayLayoutNeeded();
- final boolean focusChanged = updateFocusedWindowLocked(
- UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
- mWindowPlacerLocked.performSurfacePlacement();
+ setupWindowForRemoveOnExit(win);
if (appToken != null) {
appToken.updateReportedVisibilityLocked();
}
- if (focusChanged) {
- mInputMonitor.updateInputWindowsLw(false /*force*/);
- }
Binder.restoreCallingIdentity(origId);
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c15afb3..be27c82 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2111,7 +2111,9 @@
void clearHasSavedSurface() {
mSurfaceSaved = false;
mAnimatingWithSavedSurface = false;
- mWasVisibleBeforeClientHidden = false;
+ if (mWasVisibleBeforeClientHidden) {
+ mAppToken.destroySavedSurfaces();
+ }
}
boolean clearAnimatingWithSavedSurface() {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 308b24d..4148cd0 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1301,6 +1301,14 @@
appsCount = mService.mClosingApps.size();
for (int i = 0; i < appsCount; i++) {
AppWindowToken wtoken = mService.mClosingApps.valueAt(i);
+
+ // If we still have some windows animating with saved surfaces that's
+ // either invisible or already removed, mark them exiting so that they
+ // are disposed of after the exit animation. These are not supposed to
+ // be shown, or are delayed removal until app is actually drawn (in which
+ // case the window will be removed after the animation).
+ wtoken.markSavedSurfaceExiting();
+
final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
appAnimator.clearThumbnail();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c1913de..1af9ccb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3810,14 +3810,13 @@
// If caller has PO (or DO) it can change the password, so see if that's the case first.
ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid);
+ final boolean preN = getTargetSdk(admin.info.getPackageName(),
+ userHandle) <= android.os.Build.VERSION_CODES.M;
if (admin == null) {
// Otherwise, make sure the caller has any active admin with the right policy.
admin = getActiveAdminForCallerLocked(null,
DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
- final boolean preN = getTargetSdk(admin.info.getPackageName(), userHandle)
- <= android.os.Build.VERSION_CODES.M;
-
// As of N, password resetting to empty/null is not allowed anymore.
// TODO Should we allow DO/PO to set an empty password?
if (TextUtils.isEmpty(password)) {
@@ -3838,6 +3837,30 @@
}
}
}
+ // Do not allow to reset password when current user has a managed profile
+ if (!isManagedProfile(userHandle)) {
+ for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
+ if (userInfo.isManagedProfile()) {
+ if (!preN) {
+ throw new IllegalStateException(
+ "Cannot reset password on user has managed profile");
+ } else {
+ Slog.e(LOG_TAG, "Cannot reset password on user has managed profile");
+ return false;
+ }
+ }
+ }
+ }
+ // Do not allow to reset password when user is locked
+ if (!mUserManager.isUserUnlocked(userHandle)) {
+ if (!preN) {
+ throw new IllegalStateException("Cannot reset password when user is locked");
+ } else {
+ Slog.e(LOG_TAG, "Cannot reset password when user is locked");
+ return false;
+ }
+ }
+
quality = getPasswordQuality(null, userHandle, /* parent */ false);
if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e306d89..eed4351 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -176,7 +176,7 @@
* visual content.
*/
private static final int DEFAULT_SYSTEM_THEME =
- com.android.internal.R.style.Theme_Material_Light_DarkActionBar;
+ com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar;
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index cbd0415..1afd90d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -23,6 +23,7 @@
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.bars.AppCompatActionBar;
import com.android.layoutlib.bridge.bars.BridgeActionBar;
import com.android.layoutlib.bridge.bars.Config;
@@ -232,8 +233,10 @@
private BridgeActionBar createActionBar(@NonNull BridgeContext context,
@NonNull SessionParams params) {
+ boolean isMenu = "menu".equals(params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG));
+
BridgeActionBar actionBar;
- if (mBuilder.isThemeAppCompat()) {
+ if (mBuilder.isThemeAppCompat() && !isMenu) {
actionBar = new AppCompatActionBar(context, params);
} else {
actionBar = new FrameworkActionBar(context, params);