OSDN Git Service

Merge "Fix a translate issue with saveLayer" into nyc-dev
authorJohn Reck <jreck@google.com>
Wed, 25 May 2016 16:27:30 +0000 (16:27 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Wed, 25 May 2016 16:27:31 +0000 (16:27 +0000)
126 files changed:
api/current.txt
api/removed.txt
api/system-current.txt
api/system-removed.txt
api/test-current.txt
api/test-removed.txt
cmds/uiautomator/api/current.txt
cmds/uiautomator/cmds/uiautomator/uiautomator
cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
cmds/uiautomator/library/Android.mk
cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
core/java/android/animation/AnimatorSet.java
core/java/android/animation/ValueAnimator.java
core/java/android/app/ActivityThread.java
core/java/android/app/Notification.java
core/java/android/app/ResourcesManager.java
core/java/android/app/admin/DevicePolicyManager.java
core/java/android/bluetooth/BluetoothAdapter.java
core/java/android/content/res/Configuration.java
core/java/android/content/res/ResourcesImpl.java
core/java/android/net/metrics/DefaultNetworkEvent.java
core/java/android/os/LocaleList.java
core/java/android/os/RecoverySystem.java
core/java/android/view/NotificationHeaderView.java
core/java/android/view/inputmethod/InputMethodManager.java
core/java/android/widget/CalendarView.java
core/java/android/widget/CalendarViewLegacyDelegate.java
core/java/android/widget/CalendarViewMaterialDelegate.java
core/java/android/widget/DatePicker.java
core/java/android/widget/DatePickerCalendarDelegate.java
core/java/android/widget/DatePickerSpinnerDelegate.java
core/java/android/widget/DayPickerPagerAdapter.java
core/java/android/widget/DayPickerView.java
core/java/android/widget/Editor.java
core/java/android/widget/SimpleMonthView.java
core/java/android/widget/YearPickerView.java
core/java/com/android/internal/view/menu/CascadingMenuPopup.java
core/java/com/android/internal/widget/LockPatternUtils.java
core/java/com/android/server/BootReceiver.java
core/jni/android_media_AudioRecord.cpp
core/jni/android_media_AudioTrack.cpp
core/res/AndroidManifest.xml
core/res/res/values/arrays.xml
core/res/res/values/dimens.xml
core/res/res/values/strings.xml
core/res/res/values/symbols.xml
core/tests/coretests/apks/locales/Android.mk [new file with mode: 0644]
core/tests/coretests/apks/locales/AndroidManifest.xml [new file with mode: 0644]
core/tests/coretests/apks/locales/res/values-pl/values.xml [new file with mode: 0644]
core/tests/coretests/apks/locales/res/values/values.xml [new file with mode: 0644]
core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java [new file with mode: 0644]
docs/html-intl/intl/es/preview/setup-sdk.jd
docs/html-intl/intl/in/preview/setup-sdk.jd
docs/html-intl/intl/ja/preview/setup-sdk.jd
docs/html-intl/intl/ko/preview/setup-sdk.jd
docs/html-intl/intl/pt-br/preview/setup-sdk.jd
docs/html-intl/intl/ru/preview/setup-sdk.jd
docs/html-intl/intl/vi/preview/setup-sdk.jd
docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
docs/html/images/testing/test_framework.png [deleted file]
docs/html/jd_extras_en.js
docs/html/preview/setup-sdk.jd
graphics/java/android/graphics/drawable/LayerDrawable.java
location/java/android/location/GpsStatus.java
media/java/android/media/AudioManager.java
media/java/android/media/AudioRecord.java
media/java/android/media/AudioTrack.java
media/java/android/media/MediaCodecInfo.java
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java [new file with mode: 0644]
packages/Shell/src/com/android/shell/BugreportProgressService.java
packages/Shell/src/com/android/shell/BugreportStorageProvider.java
packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
packages/SystemUI/res/layout/notification_children_divider.xml
packages/SystemUI/res/layout/status_bar_expanded.xml
packages/SystemUI/res/values/dimens.xml
packages/SystemUI/res/values/values_tv.xml
packages/SystemUI/src/com/android/systemui/ExpandHelper.java
packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
services/core/java/com/android/server/AlarmManagerService.java
services/core/java/com/android/server/AppOpsService.java
services/core/java/com/android/server/BluetoothManagerService.java
services/core/java/com/android/server/ConnectivityService.java
services/core/java/com/android/server/LockSettingsService.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/BroadcastQueue.java
services/core/java/com/android/server/am/ProcessRecord.java
services/core/java/com/android/server/am/UserController.java
services/core/java/com/android/server/notification/NotificationUsageStats.java
services/core/java/com/android/server/notification/RateEstimator.java
services/core/java/com/android/server/policy/PhoneWindowManager.java
services/core/java/com/android/server/wm/WindowStateAnimator.java
services/core/jni/com_android_server_location_GnssLocationProvider.cpp
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
services/java/com/android/server/SystemServer.java
services/tests/servicestests/src/com/android/server/notification/RateEstimatorTest.java
services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java

index 7d40c5f..70ea1c5 100644 (file)
@@ -19776,6 +19776,7 @@ package android.media {
     method public void stop() throws java.lang.IllegalStateException;
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int READ_BLOCKING = 0; // 0x0
     field public static final int READ_NON_BLOCKING = 1; // 0x1
@@ -19898,6 +19899,7 @@ package android.media {
     method public int write(java.nio.ByteBuffer, int, int, long);
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int MODE_STATIC = 0; // 0x0
     field public static final int MODE_STREAM = 1; // 0x1
@@ -20555,6 +20557,7 @@ package android.media {
     field public static final int HEVCProfileMain10HDR10 = 4096; // 0x1000
     field public static final int MPEG2LevelH14 = 2; // 0x2
     field public static final int MPEG2LevelHL = 3; // 0x3
+    field public static final int MPEG2LevelHP = 4; // 0x4
     field public static final int MPEG2LevelLL = 0; // 0x0
     field public static final int MPEG2LevelML = 1; // 0x1
     field public static final int MPEG2Profile422 = 2; // 0x2
@@ -20568,9 +20571,11 @@ package android.media {
     field public static final int MPEG4Level1 = 4; // 0x4
     field public static final int MPEG4Level2 = 8; // 0x8
     field public static final int MPEG4Level3 = 16; // 0x10
+    field public static final int MPEG4Level3b = 24; // 0x18
     field public static final int MPEG4Level4 = 32; // 0x20
     field public static final int MPEG4Level4a = 64; // 0x40
     field public static final int MPEG4Level5 = 128; // 0x80
+    field public static final int MPEG4Level6 = 256; // 0x100
     field public static final int MPEG4ProfileAdvancedCoding = 4096; // 0x1000
     field public static final int MPEG4ProfileAdvancedCore = 8192; // 0x2000
     field public static final int MPEG4ProfileAdvancedRealTime = 1024; // 0x400
index 73a439c..42c64ea 100644 (file)
@@ -196,10 +196,6 @@ package android.media {
     ctor public AudioFormat();
   }
 
-  public static abstract class AudioManager.AudioRecordingCallback {
-    method public deprecated void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
-  }
-
 }
 
 package android.media.tv {
index b92058a..a6f612d 100644 (file)
@@ -21292,6 +21292,7 @@ package android.media {
     method public void stop() throws java.lang.IllegalStateException;
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int READ_BLOCKING = 0; // 0x0
     field public static final int READ_NON_BLOCKING = 1; // 0x1
@@ -21416,6 +21417,7 @@ package android.media {
     method public int write(java.nio.ByteBuffer, int, int, long);
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int MODE_STATIC = 0; // 0x0
     field public static final int MODE_STREAM = 1; // 0x1
@@ -22073,6 +22075,7 @@ package android.media {
     field public static final int HEVCProfileMain10HDR10 = 4096; // 0x1000
     field public static final int MPEG2LevelH14 = 2; // 0x2
     field public static final int MPEG2LevelHL = 3; // 0x3
+    field public static final int MPEG2LevelHP = 4; // 0x4
     field public static final int MPEG2LevelLL = 0; // 0x0
     field public static final int MPEG2LevelML = 1; // 0x1
     field public static final int MPEG2Profile422 = 2; // 0x2
@@ -22086,9 +22089,11 @@ package android.media {
     field public static final int MPEG4Level1 = 4; // 0x4
     field public static final int MPEG4Level2 = 8; // 0x8
     field public static final int MPEG4Level3 = 16; // 0x10
+    field public static final int MPEG4Level3b = 24; // 0x18
     field public static final int MPEG4Level4 = 32; // 0x20
     field public static final int MPEG4Level4a = 64; // 0x40
     field public static final int MPEG4Level5 = 128; // 0x80
+    field public static final int MPEG4Level6 = 256; // 0x100
     field public static final int MPEG4ProfileAdvancedCoding = 4096; // 0x1000
     field public static final int MPEG4ProfileAdvancedCore = 8192; // 0x2000
     field public static final int MPEG4ProfileAdvancedRealTime = 1024; // 0x400
index 0a758f8..4873605 100644 (file)
@@ -194,10 +194,6 @@ package android.media {
     ctor public AudioFormat();
   }
 
-  public static abstract class AudioManager.AudioRecordingCallback {
-    method public deprecated void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
-  }
-
 }
 
 package android.media.tv {
index a51886a..37bc7af 100644 (file)
@@ -19845,6 +19845,7 @@ package android.media {
     method public void stop() throws java.lang.IllegalStateException;
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int READ_BLOCKING = 0; // 0x0
     field public static final int READ_NON_BLOCKING = 1; // 0x1
@@ -19967,6 +19968,7 @@ package android.media {
     method public int write(java.nio.ByteBuffer, int, int, long);
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int MODE_STATIC = 0; // 0x0
     field public static final int MODE_STREAM = 1; // 0x1
@@ -20624,6 +20626,7 @@ package android.media {
     field public static final int HEVCProfileMain10HDR10 = 4096; // 0x1000
     field public static final int MPEG2LevelH14 = 2; // 0x2
     field public static final int MPEG2LevelHL = 3; // 0x3
+    field public static final int MPEG2LevelHP = 4; // 0x4
     field public static final int MPEG2LevelLL = 0; // 0x0
     field public static final int MPEG2LevelML = 1; // 0x1
     field public static final int MPEG2Profile422 = 2; // 0x2
@@ -20637,9 +20640,11 @@ package android.media {
     field public static final int MPEG4Level1 = 4; // 0x4
     field public static final int MPEG4Level2 = 8; // 0x8
     field public static final int MPEG4Level3 = 16; // 0x10
+    field public static final int MPEG4Level3b = 24; // 0x18
     field public static final int MPEG4Level4 = 32; // 0x20
     field public static final int MPEG4Level4a = 64; // 0x40
     field public static final int MPEG4Level5 = 128; // 0x80
+    field public static final int MPEG4Level6 = 256; // 0x100
     field public static final int MPEG4ProfileAdvancedCoding = 4096; // 0x1000
     field public static final int MPEG4ProfileAdvancedCore = 8192; // 0x2000
     field public static final int MPEG4ProfileAdvancedRealTime = 1024; // 0x400
index 73a439c..42c64ea 100644 (file)
@@ -196,10 +196,6 @@ package android.media {
     ctor public AudioFormat();
   }
 
-  public static abstract class AudioManager.AudioRecordingCallback {
-    method public deprecated void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
-  }
-
 }
 
 package android.media.tv {
index 7eeecf5..634ca4d 100644 (file)
@@ -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 @@ package com.android.uiautomator.core {
     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 @@ package com.android.uiautomator.core {
     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 @@ package com.android.uiautomator.core {
     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 @@ package com.android.uiautomator.core {
     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 @@ package com.android.uiautomator.core {
     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 @@ package com.android.uiautomator.core {
     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.core {
 
 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();
index fe2c735..86a1dba 100755 (executable)
@@ -67,6 +67,11 @@ jars=
 
 # 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
index f0c60d2..6d75844 100644 (file)
@@ -22,7 +22,10 @@ import android.os.Bundle;
  * 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 {
 
     /**
index ae763f2..af42653 100644 (file)
@@ -28,7 +28,10 @@ import junit.framework.TestListener;
 /**
  * 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
index b5f21c9..7c9aede 100644 (file)
@@ -26,7 +26,10 @@ import com.android.uiautomator.core.UiDevice;
 
 /**
  * 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;
index 62fb0e4..ea77fbc 100644 (file)
@@ -67,7 +67,6 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
 # 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 @@ $(eval $(call check-api, \
     $(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 @@ $(eval $(call check-api, \
     $(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_src_files :=
 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 :=
index 249f404..255c49a 100644 (file)
@@ -25,7 +25,10 @@ package com.android.uiautomator.core;
  * 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;
index e15beb2..06bacb0 100644 (file)
@@ -19,7 +19,10 @@ package com.android.uiautomator.core;
  * 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 {
 
     /**
index a930eb4..b1b432b 100644 (file)
@@ -42,7 +42,10 @@ import java.util.concurrent.TimeoutException;
  * 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();
 
index 2118da8..751bbe8 100644 (file)
@@ -31,7 +31,10 @@ import android.view.accessibility.AccessibilityNodeInfo;
  * 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();
     /**
index fc0891b..f3e2698 100644 (file)
@@ -20,7 +20,10 @@ package com.android.uiautomator.core;
  * 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;
index a8d20c3..6673dac 100644 (file)
@@ -24,7 +24,10 @@ import android.view.accessibility.AccessibilityNodeInfo;
  * 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();
 
index 482a74d..9ba425e 100644 (file)
@@ -27,7 +27,10 @@ import java.util.regex.Pattern;
  * 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;
index 5403e30..030fb0a 100644 (file)
@@ -22,7 +22,10 @@ package com.android.uiautomator.core;
  * 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 {
 
     /**
index f0c60d2..6d75844 100644 (file)
@@ -22,7 +22,10 @@ import android.os.Bundle;
  * 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 {
 
     /**
index e7d961b..3d5476d 100644 (file)
@@ -36,7 +36,10 @@ import java.util.List;
  * {@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";
index 8ff38bb..7841d29 100644 (file)
@@ -465,20 +465,26 @@ public final class AnimatorSet extends Animator {
 
     /**
      * 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();
index 0a9b5de..4edf249 100644 (file)
@@ -708,12 +708,18 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
 
     /**
      * 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;
     }
 
index dff0769..30753c1 100644 (file)
@@ -62,6 +62,7 @@ import android.os.DropBoxManager;
 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.text.DateFormat;
 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 @@ public final class ActivityThread {
                     + config);
 
             mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
+            updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+                    mResourcesManager.getConfiguration().getLocales());
 
             if (mConfiguration == null) {
                 mConfiguration = new Configuration();
@@ -4989,6 +4993,24 @@ public final class ActivityThread {
         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 @@ public final class ActivityThread {
          */
         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 @@ public final class ActivityThread {
         }
 
         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 @@ public final class ActivityThread {
                     // 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 ||
index 4a8203b..0cc44e7 100644 (file)
@@ -58,6 +58,7 @@ import android.util.SparseArray;
 import android.view.Gravity;
 import android.view.NotificationHeaderView;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ProgressBar;
 import android.widget.RemoteViews;
 
@@ -3202,11 +3203,14 @@ public class Notification implements Parcelable
 
             bindNotificationHeader(contentView);
             bindLargeIcon(contentView);
+            boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
             if (title != null) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title, title);
+                contentView.setViewLayoutWidth(R.id.title, showProgress
+                        ? ViewGroup.LayoutParams.WRAP_CONTENT
+                        : ViewGroup.LayoutParams.MATCH_PARENT);
             }
-            boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
             if (text != null) {
                 int textId = showProgress ? com.android.internal.R.id.text_line_1
                         : com.android.internal.R.id.text;
index b4e9db8..25a8b66 100644 (file)
@@ -67,10 +67,6 @@ public class ResourcesManager {
                 }
             };
 
-    private String[] mSystemLocales = null;
-    private final HashSet<String> mNonSystemLocales = new HashSet<>();
-    private boolean mHasNonSystemLocales = false;
-
     /**
      * The global compatibility settings.
      */
@@ -479,12 +475,7 @@ public class ResourcesManager {
      */
     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 @@ public class ResourcesManager {
         // 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 @@ public class ResourcesManager {
         }
     }
 
-    /* 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 @@ public class ResourcesManager {
                         | 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 @@ public class ResourcesManager {
                 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 @@ public class ResourcesManager {
                         if (tmpConfig == null) {
                             tmpConfig = new Configuration();
                         }
-                        tmpConfig.setTo(localeAdjustedConfig);
+                        tmpConfig.setTo(config);
                         if (!isDefaultDisplay) {
                             dm = getDisplayMetrics(displayId);
                             applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
@@ -840,7 +774,7 @@ public class ResourcesManager {
                         }
                         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());
index a672d80..63afdd8 100644 (file)
@@ -2191,7 +2191,8 @@ public class DevicePolicyManager {
      * <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 @@ public class DevicePolicyManager {
      *         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");
index 4c86578..9390bcd 100644 (file)
@@ -2045,12 +2045,15 @@ public final class BluetoothAdapter {
             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 ){
index c1aac85..f6445e6 100644 (file)
@@ -1446,7 +1446,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
      *
      * @return The locale list.
      */
-    public LocaleList getLocales() {
+    public @NonNull LocaleList getLocales() {
         fixUpLocaleList();
         return mLocaleList;
     }
index dada612..32a2795 100644 (file)
@@ -330,18 +330,43 @@ public class ResourcesImpl {
                 // 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 @@ public class ResourcesImpl {
                 }
 
                 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,
index 6138b83..f8b5992 100644 (file)
@@ -68,8 +68,28 @@ public final class DefaultNetworkEvent extends IpConnectivityEvent implements Pa
 
     @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
index 8136796..2dc3beb 100644 (file)
@@ -148,17 +148,6 @@ public final class LocaleList implements Parcelable {
     }
 
     /**
-     * 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 @@ public final class LocaleList implements Parcelable {
     }
 
     /**
+     * {@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 @@ public final class LocaleList implements Parcelable {
      * 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
index 403e06c..4abbf0e 100644 (file)
@@ -25,8 +25,10 @@ import android.text.TextUtils;
 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.Locale;
 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 @@ public class RecoverySystem {
     /** 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 @@ public class RecoverySystem {
         }
     }
 
+    // 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 @@ public class RecoverySystem {
             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()) {
index 37da869..6e6baea 100644 (file)
@@ -18,7 +18,11 @@ package android.view;
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Outline;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
@@ -47,6 +51,18 @@ public class NotificationHeaderView extends ViewGroup {
     private int mOriginalNotificationColor;
     private boolean mExpanded;
     private boolean mShowWorkBadgeAtEnd;
+    private Drawable mBackground;
+    private int mHeaderBackgroundHeight;
+
+    ViewOutlineProvider mProvider = new ViewOutlineProvider() {
+        @Override
+        public void getOutline(View view, Outline outline) {
+            if (mBackground != null) {
+                outline.setRect(0, 0, getWidth(), mHeaderBackgroundHeight);
+                outline.setAlpha(1f);
+            }
+        }
+    };
 
     public NotificationHeaderView(Context context) {
         this(context, null);
@@ -66,6 +82,8 @@ public class NotificationHeaderView extends ViewGroup {
                 com.android.internal.R.dimen.notification_header_shrink_min_width);
         mContentEndMargin = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin_end);
+        mHeaderBackgroundHeight = getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_header_background_height);
     }
 
     @Override
@@ -165,6 +183,43 @@ public class NotificationHeaderView extends ViewGroup {
         return new ViewGroup.MarginLayoutParams(getContext(), attrs);
     }
 
+    /**
+     * Set a {@link Drawable} to be displayed as a background on the header.
+     */
+    public void setHeaderBackgroundDrawable(Drawable drawable) {
+        if (drawable != null) {
+            setWillNotDraw(false);
+            mBackground = drawable;
+            mBackground.setCallback(this);
+            setOutlineProvider(mProvider);
+        } else {
+            setWillNotDraw(true);
+            mBackground = null;
+            setOutlineProvider(null);
+        }
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mBackground != null) {
+            mBackground.setBounds(0, 0, getWidth(), mHeaderBackgroundHeight);
+            mBackground.draw(canvas);
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || who == mBackground;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        if (mBackground != null && mBackground.isStateful()) {
+            mBackground.setState(getDrawableState());
+        }
+    }
+
     private void updateTouchListener() {
         if (mExpandClickListener != null) {
             mTouchListener.bindTouchRects();
index 1618758..4013b30 100644 (file)
@@ -1277,6 +1277,28 @@ public final class InputMethodManager {
                             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 {
index 66896ab..ac44033 100644 (file)
@@ -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.Context;
 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
index f540479..2b91713 100644 (file)
@@ -27,6 +27,7 @@ import android.graphics.Canvas;
 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.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
-import java.util.Calendar;
 import java.util.Locale;
 
 import libcore.icu.LocaleData;
index 0ed75d5..07b2000 100644 (file)
@@ -18,11 +18,10 @@ package android.widget;
 
 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;
 
index 0c5edc5..8613f99 100644 (file)
 
 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.util.SparseArray;
 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.
index 0a23b34..2c80f28 100755 (executable)
@@ -26,6 +26,7 @@ import android.content.res.Resources;
 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.view.accessibility.AccessibilityEvent;
 import android.widget.DayPickerView.OnDaySelectedListener;
 import android.widget.YearPickerView.OnYearSelectedListener;
 
-import java.util.Calendar;
 import java.util.Locale;
 
 /**
index d8a3c56..17af014 100644 (file)
@@ -19,6 +19,7 @@ package android.widget;
 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.DateFormatSymbols;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
-import java.util.Calendar;
 import java.util.Locale;
 
 import libcore.icu.ICU;
index 97936e7..9f02b44 100644 (file)
@@ -25,14 +25,13 @@ import android.annotation.Nullable;
 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.
  */
index dc772fb..ab89c1f 100644 (file)
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
 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.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
 
-import java.util.Calendar;
 import java.util.Locale;
 
 import libcore.icu.LocaleData;
index 622567c..15bba3a 100644 (file)
@@ -76,6 +76,7 @@ import android.util.SparseArray;
 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 @@ public class Editor {
         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 @@ public class Editor {
         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 @@ public class Editor {
         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 @@ public class Editor {
         }
 
         @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 @@ public class Editor {
 
         @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 @@ public class Editor {
         }
 
         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() {
index 97d834a..588425f 100644 (file)
@@ -32,6 +32,7 @@ import android.graphics.Rect;
 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;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
 import java.text.NumberFormat;
-import java.util.Calendar;
 import java.util.Locale;
 
 import libcore.icu.LocaleData;
index 96624d2..a3f5a67 100644 (file)
 
 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.
  */
index 07f3801..ddf3a76 100644 (file)
@@ -231,6 +231,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
         popupWindow.setAnchorView(mAnchorView);
         popupWindow.setDropDownGravity(mDropDownGravity);
         popupWindow.setModal(true);
+        popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
         return popupWindow;
     }
 
index a5485da..70941b6 100644 (file)
@@ -141,6 +141,9 @@ public class LockPatternUtils {
     // 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;
index 6d6c162..fbc51cd 100644 (file)
@@ -127,7 +127,7 @@ public class BootReceiver extends BroadcastReceiver {
             .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);
         }
index 1bc4285..1fc08ed 100644 (file)
@@ -479,17 +479,13 @@ void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint
 
 static inline
 jint interpretReadSizeError(ssize_t readSize) {
-    ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
-    switch (readSize) {
-    case WOULD_BLOCK:
+    if (readSize == WOULD_BLOCK) {
         return (jint)0;
-    case BAD_VALUE:
-        return (jint)AUDIO_JAVA_BAD_VALUE;
-    default:
-        // may be possible for other errors such as
-        // NO_INIT to happen if restoreRecord_l fails.
-    case INVALID_OPERATION:
-        return (jint)AUDIO_JAVA_INVALID_OPERATION;
+    } else if (readSize == NO_INIT) {
+        return AUDIO_JAVA_DEAD_OBJECT;
+    } else {
+        ALOGE("Error %zd during AudioRecord native read", readSize);
+        return nativeToJavaStatus(readSize);
     }
 }
 
index 024c21d..982a1f8 100644 (file)
@@ -606,6 +606,18 @@ void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint
     env->ReleaseFloatArrayElements(array, elems, mode);
 }
 
+static inline
+jint interpretWriteSizeError(ssize_t writeSize) {
+    if (writeSize == WOULD_BLOCK) {
+        return (jint)0;
+    } else if (writeSize == NO_INIT) {
+        return AUDIO_JAVA_DEAD_OBJECT;
+    } else {
+        ALOGE("Error %zd during AudioTrack native read", writeSize);
+        return nativeToJavaStatus(writeSize);
+    }
+}
+
 // ----------------------------------------------------------------------------
 template <typename T>
 static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
@@ -628,11 +640,10 @@ static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T
         memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
         written = sizeInBytes;
     }
-    if (written > 0) {
+    if (written >= 0) {
         return written / sizeof(T);
     }
-    // for compatibility, error codes pass through unchanged
-    return written;
+    return interpretWriteSizeError(written);
 }
 
 // ----------------------------------------------------------------------------
index c2ff4ef..6c289dc 100644 (file)
     <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" />
     <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" />
     <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" />
index 30e23f5..e190bd1 100644 (file)
     <!-- 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
index 573b066..37fb816 100644 (file)
     <!-- height of the content margin on the bottom -->
     <dimen name="notification_content_margin_bottom">16dp</dimen>
 
+       <!-- The height of the background for a notification header on a group -->
+    <dimen name="notification_header_background_height">45.5dp</dimen>
+
     <!-- Height of a small notification in the status bar -->
     <dimen name="notification_min_height">92dp</dimen>
 
index 45ee778..2232942 100644 (file)
     <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>
index e99d57f..f2c3c42 100644 (file)
   <java-symbol type="dimen" name="notification_content_picture_margin" />
   <java-symbol type="dimen" name="notification_content_margin_top" />
   <java-symbol type="dimen" name="notification_content_margin_bottom" />
+  <java-symbol type="dimen" name="notification_header_background_height" />
   <java-symbol type="string" name="importance_from_user" />
   <java-symbol type="string" name="importance_from_person" />
 
diff --git a/core/tests/coretests/apks/locales/Android.mk b/core/tests/coretests/apks/locales/Android.mk
new file mode 100644 (file)
index 0000000..9cb13dd
--- /dev/null
@@ -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 (file)
index 0000000..8a1f575
--- /dev/null
@@ -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 (file)
index 0000000..22237fa
--- /dev/null
@@ -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 (file)
index 0000000..cb990d2
--- /dev/null
@@ -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 (file)
index 0000000..e85666e
--- /dev/null
@@ -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));
+    }
+}
index 01c715b..769121d 100644 (file)
@@ -221,4 +221,4 @@ android {
 y <a href="{@docRoot}preview/api-overview.html">API y funciones de Android N
 </a>.</li>
 </ul>
-
+<div class="end-content-title"></div>
index a842931..c6e43f1 100644 (file)
@@ -221,4 +221,4 @@ android {
 dan <a href="{@docRoot}preview/api-overview.html">Android N API
 dan Fiturnya</a>.</li>
 </ul>
-
+<div class="end-content-title"></div>
index adb9edf..91f17c1 100644 (file)
@@ -136,7 +136,7 @@ API 참조와 API 차이점 보고서가 포함되어 있습니다.
 
 <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 @@ SDK</strong> 옵션 목록에서
 및 기능</a>에서 Android N 플랫폼에
 대해 자세히 알아봅니다.</li>
 </ul>
-
+<div class="end-content-title"></div>
index 4159c25..9035e50 100644 (file)
@@ -79,7 +79,7 @@ instalação existente, pule para a etapa 4):</p>
 
 <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 @@ Android N Preview SDK no Android Studio da seguinte maneira:</p>
 <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 @@ agora mesmo.</p>
   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 @@ android {
 
 <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>
index 3838700..582fe9f 100644 (file)
@@ -79,7 +79,7 @@ check updates for</strong> и выберите
 
 <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 @@ API.
 
 <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 @@ SDK</strong> и выберите
 и в описании <a href="{@docRoot}preview/api-overview.html">API-интерфейсов и возможностей Android N</a>.
 </li>
 </ul>
-
+<div class="end-content-title"></div>
index 2e3a18b..6f6cd20 100644 (file)
@@ -98,7 +98,7 @@ bản cài đặt có sẵn thì hãy chuyển đến bước 4):</p>
 
   <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 @@ android {
 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/images/testing/test_framework.png b/docs/html/images/testing/test_framework.png
deleted file mode 100644 (file)
index fbc5fc2..0000000
Binary files a/docs/html/images/testing/test_framework.png and /dev/null differ
index c85ef7e..3aab5bd 100644 (file)
@@ -237,7 +237,7 @@ METADATA['en'].extras = METADATA['en'].extras.concat([
     "title":"Test Your App",
     "summary":"This document describes key concepts related to Android app testing. It assumes you have a basic knowledge of the JUnit testing framework. Android testing is based on JUnit. In general, a JUnit test is a method whose statements test a part of the application\u2026",
     "url":"studio/test/index.html",
-    "image":"images/testing/test_framework.png",
+    "image":"studio/images/test/test-framework.png",
     "type":"studio",
     "keywords": [],
     "tags": [],
index 28fc406..ebeae74 100644 (file)
@@ -189,4 +189,4 @@ href="{@docRoot}preview/download.html">Test on an Android N Device</a>.</li>
 and <a href="{@docRoot}preview/api-overview.html">Android N APIs
 and Features</a>.</li>
 </ul>
-
+<div class="end-content-title"></div>
index c67b008..1864206 100644 (file)
@@ -104,6 +104,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
     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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
       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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
     }
 
     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;
index 5561b22..b601cde 100644 (file)
@@ -34,6 +34,7 @@ public final class GpsStatus {
     private static final int NUM_SATELLITES = 255;
     private static final int GLONASS_SVID_OFFSET = 64;
     private static final int BEIDOU_SVID_OFFSET = 200;
+    private static final int SBAS_SVID_OFFSET = -87;
 
     /* These package private values are modified by the LocationManager class */
     private int mTimeToFirstFix;
@@ -166,9 +167,10 @@ public final class GpsStatus {
                 prn += GLONASS_SVID_OFFSET;
             } else if (constellationType == GnssStatus.CONSTELLATION_BEIDOU) {
                 prn += BEIDOU_SVID_OFFSET;
+            } else if (constellationType == GnssStatus.CONSTELLATION_SBAS) {
+                prn += SBAS_SVID_OFFSET;
             } else if ((constellationType != GnssStatus.CONSTELLATION_GPS) &&
-                    (constellationType != GnssStatus.CONSTELLATION_QZSS) &&
-                    (constellationType != GnssStatus.CONSTELLATION_SBAS)) {
+                    (constellationType != GnssStatus.CONSTELLATION_QZSS)) {
                 continue;
             }
             if (prn > 0 && prn <= NUM_SATELLITES) {
index 7178313..4bdc70e 100644 (file)
@@ -2747,13 +2747,6 @@ public class AudioManager {
      */
     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()}.
index 4504e69..8efd599 100644 (file)
@@ -94,6 +94,11 @@ public class AudioRecord implements AudioRouting
      * Denotes a failure due to the improper use of a method.
      */
     public  static final int ERROR_INVALID_OPERATION               = AudioSystem.INVALID_OPERATION;
+    /**
+     * An error code indicating that the object reporting it is no longer valid and needs to
+     * be recreated.
+     */
+    public  static final int ERROR_DEAD_OBJECT                     = AudioSystem.DEAD_OBJECT;
 
     // Error codes:
     // to keep in sync with frameworks/base/core/jni/android_media_AudioRecord.cpp
@@ -1046,10 +1051,16 @@ public class AudioRecord implements AudioRouting
      * @param audioData the array to which the recorded audio data is written.
      * @param offsetInBytes index in audioData from which the data is written expressed in bytes.
      * @param sizeInBytes the number of requested bytes.
-     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of bytes will not exceed sizeInBytes.
+     * @return zero or the positive number of bytes that were read, or one of the following
+     *    error codes. The number of bytes will not exceed sizeInBytes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {
         return read(audioData, offsetInBytes, sizeInBytes, READ_BLOCKING);
@@ -1070,11 +1081,17 @@ public class AudioRecord implements AudioRouting
      *     is read.
      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
-     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of bytes will be a multiple of the frame size in bytes
+     * @return zero or the positive number of bytes that were read, or one of the following
+     *    error codes. The number of bytes will be a multiple of the frame size in bytes
      *    not to exceed sizeInBytes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
             @ReadMode int readMode) {
@@ -1106,10 +1123,17 @@ public class AudioRecord implements AudioRouting
      *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInShorts the number of requested shorts.
      *        Must not be negative, or cause the data access to go out of bounds of the array.
-     * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
+     * @return zero or the positive number of shorts that were read, or one of the following
+     *    error codes. The number of shorts will be a multiple of the channel count not to exceed
+     *    sizeInShorts.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
         return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING);
@@ -1129,10 +1153,17 @@ public class AudioRecord implements AudioRouting
      *     is read.
      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
-     * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
+     * @return zero or the positive number of shorts that were read, or one of the following
+     *    error codes. The number of shorts will be a multiple of the channel count not to exceed
+     *    sizeInShorts.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
             @ReadMode int readMode) {
@@ -1169,10 +1200,17 @@ public class AudioRecord implements AudioRouting
      *     is read.
      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
-     * @return the number of floats that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of floats will be a multiple of the channel count not to exceed sizeInFloats.
+     * @return zero or the positive number of floats that were read, or one of the following
+     *    error codes. The number of floats will be a multiple of the channel count not to exceed
+     *    sizeInFloats.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
             @ReadMode int readMode) {
@@ -1213,11 +1251,17 @@ public class AudioRecord implements AudioRouting
      * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
      *    that the number of bytes requested be a multiple of the frame size (sample size in
      *    bytes multiplied by the channel count).
-     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of bytes will not exceed sizeInBytes.
-     *    The number of bytes read will be truncated to be a multiple of the frame size.
+     * @return zero or the positive number of bytes that were read, or one of the following
+     *    error codes. The number of bytes will not exceed sizeInBytes and will be truncated to be
+     *    a multiple of the frame size.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) {
         return read(audioBuffer, sizeInBytes, READ_BLOCKING);
@@ -1240,11 +1284,17 @@ public class AudioRecord implements AudioRouting
      *     is read.
      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
-     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of bytes will not exceed sizeInBytes.
-     *    The number of bytes read will be truncated to be a multiple of the frame size.
+     * @return zero or the positive number of bytes that were read, or one of the following
+     *    error codes. The number of bytes will not exceed sizeInBytes and will be truncated to be
+     *    a multiple of the frame size.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode) {
         if (mState != STATE_INITIALIZED) {
index 12e88a2..9a81668 100644 (file)
@@ -156,7 +156,6 @@ public class AudioTrack extends PlayerBase
     /**
      * An error code indicating that the object reporting it is no longer valid and needs to
      * be recreated.
-     * @hide
      */
     public  static final int ERROR_DEAD_OBJECT                     = AudioSystem.DEAD_OBJECT;
     /**
@@ -1840,17 +1839,17 @@ public class AudioTrack extends PlayerBase
      *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInBytes the number of bytes to write in audioData after the offset.
      *    Must not be negative, or cause the data access to go out of bounds of the array.
-     * @return zero or the positive number of bytes that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of bytes will be a multiple of the frame size in bytes
+     * @return zero or the positive number of bytes that were written, or one of the following
+     *    error codes. The number of bytes will be a multiple of the frame size in bytes
      *    not to exceed sizeInBytes.
-     *
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      * This is equivalent to {@link #write(byte[], int, int, int)} with <code>writeMode</code>
      * set to  {@link #WRITE_BLOCKING}.
      */
@@ -1888,16 +1887,17 @@ public class AudioTrack extends PlayerBase
      *         to the audio sink.
      *     <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
-     * @return zero or the positive number of bytes that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of bytes will be a multiple of the frame size in bytes
+     * @return zero or the positive number of bytes that were written, or one of the following
+     *    error codes. The number of bytes will be a multiple of the frame size in bytes
      *    not to exceed sizeInBytes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
             @WriteMode int writeMode) {
@@ -1950,16 +1950,17 @@ public class AudioTrack extends PlayerBase
      *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInShorts the number of shorts to read in audioData after the offset.
      *    Must not be negative, or cause the data access to go out of bounds of the array.
-     * @return zero or the positive number of shorts that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
-     *
+     * @return zero or the positive number of shorts that were written, or one of the following
+     *    error codes. The number of shorts will be a multiple of the channel count not to
+     *    exceed sizeInShorts.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      * This is equivalent to {@link #write(short[], int, int, int)} with <code>writeMode</code>
      * set to  {@link #WRITE_BLOCKING}.
      */
@@ -1995,15 +1996,17 @@ public class AudioTrack extends PlayerBase
      *         to the audio sink.
      *     <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
-     * @return zero or the positive number of shorts that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
+     * @return zero or the positive number of shorts that were written, or one of the following
+     *    error codes. The number of shorts will be a multiple of the channel count not to
+     *    exceed sizeInShorts.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
             @WriteMode int writeMode) {
@@ -2074,15 +2077,17 @@ public class AudioTrack extends PlayerBase
      *         to the audio sink.
      *     <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
-     * @return zero or the positive number of floats that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of floats will be a multiple of the channel count not to exceed sizeInFloats.
+     * @return zero or the positive number of floats that were written, or one of the following
+     *    error codes. The number of floats will be a multiple of the channel count not to
+     *    exceed sizeInFloats.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
             @WriteMode int writeMode) {
@@ -2154,12 +2159,16 @@ public class AudioTrack extends PlayerBase
      *         to the audio sink.
      *     <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
-     * @return zero or the positive number of bytes that were written, or
-     *     {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}, or
-     *     {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *     needs to be recreated.
-     *     The dead object error code is not returned if some data was successfully transferred.
-     *     In this case, the error is returned at the next write().
+     * @return zero or the positive number of bytes that were written, or one of the following
+     *    error codes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull ByteBuffer audioData, int sizeInBytes,
             @WriteMode int writeMode) {
@@ -2223,12 +2232,16 @@ public class AudioTrack extends PlayerBase
      *     <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
      * @param timestamp The timestamp of the first decodable audio frame in the provided audioData.
-     * @return zero or a positive number of bytes that were written, or
-     *     {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}, or
-     *     {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *     needs to be recreated.
-     *     The dead object error code is not returned if some data was successfully transferred.
-     *     In this case, the error is returned at the next write().
+     * @return zero or the positive number of bytes that were written, or one of the following
+     *    error codes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull ByteBuffer audioData, int sizeInBytes,
             @WriteMode int writeMode, long timestamp) {
index 08c1842..87c6d88 100644 (file)
@@ -2636,12 +2636,10 @@ public final class MediaCodecInfo {
         public static final int MPEG4Level1      = 0x04;
         public static final int MPEG4Level2      = 0x08;
         public static final int MPEG4Level3      = 0x10;
-        /** @hide */
         public static final int MPEG4Level3b     = 0x18;
         public static final int MPEG4Level4      = 0x20;
         public static final int MPEG4Level4a     = 0x40;
         public static final int MPEG4Level5      = 0x80;
-        /** @hide */
         public static final int MPEG4Level6      = 0x100;
 
         // from OMX_VIDEO_MPEG2PROFILETYPE
@@ -2657,7 +2655,6 @@ public final class MediaCodecInfo {
         public static final int MPEG2LevelML     = 0x01;
         public static final int MPEG2LevelH14    = 0x02;
         public static final int MPEG2LevelHL     = 0x03;
-        /** @hide */
         public static final int MPEG2LevelHP     = 0x04;
 
         // from OMX_AUDIO_AACPROFILETYPE
index e718742..423c3b0 100644 (file)
@@ -38,6 +38,7 @@ import android.hardware.camera2.CameraMetadata;
 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.getPreviewS
 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 @@ public class Camera2SurfaceViewTestCase extends
     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 @@ public class Camera2SurfaceViewTestCase extends
         mReaderSurface = null;
     }
 
+
     /**
      * Open a camera device and get the StaticMetadata for a given camera id.
      *
@@ -603,8 +608,13 @@ public class Camera2SurfaceViewTestCase extends
         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 (file)
index 0000000..11327ca
--- /dev/null
@@ -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);
+    }
+
+}
index 474e3e6..6bc4df7 100644 (file)
@@ -815,6 +815,14 @@ public class BugreportProgressService extends Service {
      */
     private void onBugreportFinished(int id, Intent intent) {
         final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
+        // Since BugreportProvider and BugreportProgressService aren't tightly coupled,
+        // we need to make sure they are explicitly tied to a single unique notification URI
+        // so that the service can alert the provider of changes it has done (ie. new bug
+        // reports)
+        // See { @link Cursor#setNotificationUri } and {@link ContentResolver#notifyChanges }
+        final Uri notificationUri = BugreportStorageProvider.getNotificationUri();
+        mContext.getContentResolver().notifyChange(notificationUri, null, false);
+
         if (bugreportFile == null) {
             // Should never happen, dumpstate always set the file.
             Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
index 0f92fa6..8b07599 100644 (file)
@@ -19,9 +19,11 @@ package com.android.shell;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.database.MatrixCursor.RowBuilder;
+import android.net.Uri;
 import android.os.CancellationSignal;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
 import android.provider.DocumentsProvider;
@@ -32,6 +34,7 @@ import java.io.File;
 import java.io.FileNotFoundException;
 
 public class BugreportStorageProvider extends DocumentsProvider {
+    private static final String AUTHORITY = "com.android.shell.documents";
     private static final String DOC_ID_ROOT = "bugreport";
 
     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
@@ -103,6 +106,7 @@ public class BugreportStorageProvider extends DocumentsProvider {
                 for (File file : files) {
                     addFileRow(result, file);
                 }
+                result.setNotificationUri(getContext().getContentResolver(), getNotificationUri());
             }
         }
         return result;
@@ -130,6 +134,12 @@ public class BugreportStorageProvider extends DocumentsProvider {
         }
     }
 
+    // This is used by BugreportProgressService so that the notification uri shared by
+    // BugreportProgressService and BugreportStorageProvider are guaranteed the same and unique
+    protected static Uri getNotificationUri() {
+      return DocumentsContract.buildChildDocumentsUri(AUTHORITY, DOC_ID_ROOT);
+    }
+
     private static String[] resolveRootProjection(String[] projection) {
         return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
     }
index 201f47d..36df924 100644 (file)
@@ -19,6 +19,7 @@
         android:layout_height="wrap_content"
         android:focusable="true"
         android:focusableInTouchMode="true"
+        android:contentDescription="@string/status_bar_recent_inspect_item_title"
         android:layout_gravity="center"
         android:layout_centerInParent="true"
         android:clipToPadding="false"
@@ -49,6 +50,7 @@
             android:layout_gravity="center_horizontal"
             android:layout_marginTop="@dimen/recents_tv_dismiss_icon_top_margin"
             android:layout_marginBottom="@dimen/recents_tv_dismiss_icon_bottom_margin"
+            android:contentDescription="@string/status_bar_accessibility_dismiss_recents"
             android:alpha="@integer/dismiss_unselected_alpha"
             android:src="@drawable/recents_tv_dismiss_icon" />
     <TextView
index dad7cea..76315b8 100644 (file)
@@ -20,4 +20,4 @@
     android:id="@+id/notification_more_divider"
     android:layout_width="match_parent"
     android:layout_height="@dimen/notification_divider_height"
-    android:background="#61000000" />
+    android:background="#FF616161" />
index 658571e..2c8a559 100644 (file)
@@ -53,8 +53,7 @@
             android:layout_width="@dimen/notification_panel_width"
             android:layout_height="match_parent"
             android:layout_gravity="@integer/notification_panel_layout_gravity"
-            android:layout_marginBottom="@dimen/close_handle_underlap"
-            android:importantForAccessibility="no" />
+            android:layout_marginBottom="@dimen/close_handle_underlap" />
 
         <ViewStub
             android:id="@+id/keyguard_user_switcher"
index b8f576b..cbb3c0a 100644 (file)
     <!-- The height of the divider between the individual notifications. -->
     <dimen name="notification_divider_height">0.5dp</dimen>
 
+    <!-- The height of a notification header -->
+    <dimen name="notification_header_height">53dp</dimen>
+
     <!-- The height of the divider between the individual notifications when the notification wants it to be increased. This is currently the case for notification groups -->
     <dimen name="notification_divider_height_increased">6dp</dimen>
 
index dc0483f..9259415 100644 (file)
@@ -16,5 +16,5 @@ limitations under the License.
 <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>
index b6d8095..7a5e322 100644 (file)
@@ -45,6 +45,7 @@ public class ExpandHelper implements Gefingerpoken {
         void setUserLockedChild(View v, boolean userLocked);
         void expansionStateChanged(boolean isExpanding);
         int getMaxExpandHeight(ExpandableView view);
+        void setExpansionCancelled(View view);
     }
 
     private static final String TAG = "ExpandHelper";
@@ -558,6 +559,8 @@ public class ExpandHelper implements Gefingerpoken {
                 public void onAnimationEnd(Animator animation) {
                     if (!mCancelled) {
                         mCallback.setUserExpandedChild(scaledView, expand);
+                    } else {
+                        mCallback.setExpansionCancelled(scaledView);
                     }
                     mCallback.setUserLockedChild(scaledView, false);
                     mScaleAnimation.removeListener(this);
index c3efe64..8218599 100644 (file)
@@ -104,6 +104,7 @@ public class ViewFocusAnimator implements View.OnFocusChangeListener {
 
         mTargetView.getDismissIconView().setAlpha(mDismissIconAlpha * level);
         mTargetView.getThumbnailView().setZ(z);
+        mTargetView.getDismissIconView().setZ(z);
     }
 
     public float getFocusProgress() {
index 2eede99..bd85748 100644 (file)
@@ -38,6 +38,8 @@ import android.widget.LinearLayout;
 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 @@ public class TaskCardView extends LinearLayout {
         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);
     }
 
index e66dc74..63933d4 100644 (file)
@@ -189,6 +189,7 @@ public abstract class BaseStatusBar extends SystemUI implements
 
     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 @@ public abstract class BaseStatusBar extends SystemUI implements
         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 @@ public abstract class BaseStatusBar extends SystemUI implements
                 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 @@ public abstract class BaseStatusBar extends SystemUI implements
 
     /**
      * 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 @@ public abstract class BaseStatusBar extends SystemUI implements
         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();
index b4ce9cd..c53ab59 100644 (file)
@@ -22,7 +22,6 @@ import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.Notification;
 import android.content.Context;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.AnimationDrawable;
@@ -31,6 +30,8 @@ import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Property;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.NotificationHeaderView;
@@ -39,13 +40,11 @@ import android.view.ViewStub;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.Chronometer;
 import android.widget.ImageView;
-import android.widget.RemoteViews;
 
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
@@ -65,6 +64,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     private int mMaxHeadsUpHeight;
     private int mNotificationMinHeight;
     private int mNotificationMaxHeight;
+    private int mIncreasedPaddingBetweenElements;
 
     /** Does this row contain layouts that can adapt to row expansion */
     private boolean mExpandable;
@@ -97,7 +97,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
      */
     private boolean mOnKeyguard;
 
-    private AnimatorSet mTranslateAnim;
+    private Animator mTranslateAnim;
     private ArrayList<View> mTranslateableViews;
     private NotificationContentView mPublicLayout;
     private NotificationContentView mPrivateLayout;
@@ -115,8 +115,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     private String mAppName;
     private boolean mIsHeadsUp;
     private boolean mLastChronometerRunning = true;
-    private NotificationHeaderView mNotificationHeader;
-    private NotificationViewWrapper mNotificationHeaderWrapper;
     private ViewStub mChildrenContainerStub;
     private NotificationGroupManager mGroupManager;
     private boolean mChildrenExpanded;
@@ -128,13 +126,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     private boolean mIsPinned;
     private FalsingManager mFalsingManager;
     private HeadsUpManager mHeadsUpManager;
-    private NotificationHeaderUtil mHeaderUtil = new NotificationHeaderUtil(this);
 
     private boolean mJustClicked;
     private boolean mIconAnimationRunning;
     private boolean mShowNoBackground;
     private ExpandableNotificationRow mNotificationParent;
     private OnExpandClickListener mOnExpandClickListener;
+    private boolean mGroupExpansionChanging;
+
     private OnClickListener mExpandClickListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -142,6 +141,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
                 mGroupManager.toggleGroupExpansion(mStatusBarNotification);
                 mOnExpandClickListener.onExpandClicked(mEntry,
                         mGroupManager.isGroupExpanded(mStatusBarNotification));
+                mGroupExpansionChanging = true;
+                updateBackgroundForGroupState();
             } else {
                 boolean nowExpanded;
                 if (isPinned()) {
@@ -160,6 +161,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     private boolean mDismissed;
     private boolean mKeepInParent;
     private boolean mRemoved;
+    private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
+            new FloatProperty<ExpandableNotificationRow>("translate") {
+                @Override
+                public void setValue(ExpandableNotificationRow object, float value) {
+                    object.setTranslation(value);
+                }
+
+                @Override
+                public Float get(ExpandableNotificationRow object) {
+                    return object.getTranslation();
+                }
+    };
+
+    public boolean isGroupExpansionChanging() {
+        if (isChildInGroup()) {
+            return mNotificationParent.isGroupExpansionChanging();
+        }
+        return mGroupExpansionChanging;
+    }
+
+    public void setGroupExpansionChanging(boolean changing) {
+        mGroupExpansionChanging = changing;
+    }
 
     public NotificationContentView getPrivateLayout() {
         return mPrivateLayout;
@@ -172,8 +196,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     public void setIconAnimationRunning(boolean running) {
         setIconAnimationRunning(running, mPublicLayout);
         setIconAnimationRunning(running, mPrivateLayout);
-        setIconAnimationRunningForChild(running, mNotificationHeader);
         if (mIsSummaryWithChildren) {
+            setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
             List<ExpandableNotificationRow> notificationChildren =
                     mChildrenContainer.getNotificationChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
@@ -235,7 +259,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         updateNotificationColor();
         updateClearability();
         if (mIsSummaryWithChildren) {
-            recreateNotificationHeader();
+            mChildrenContainer.recreateNotificationHeader(mExpandClickListener, mEntry.notification);
             mChildrenContainer.onNotificationUpdated();
         }
         if (mIconAnimationRunning) {
@@ -329,9 +353,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         if (mChildrenContainer != null) {
             mChildrenContainer.removeNotification(row);
         }
-        if (!row.isRemoved()) {
-            mHeaderUtil.restoreNotificationHeader(row);
-        }
         onChildrenCountChanged();
         row.setIsChildInGroup(false, null);
     }
@@ -352,7 +373,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         boolean childInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
         mNotificationParent = childInGroup ? parent : null;
         mPrivateLayout.setIsChildInGroup(childInGroup);
-        updateNoBackgroundState();
+        updateBackgroundForGroupState();
+        if (mNotificationParent != null) {
+            mNotificationParent.updateBackgroundForGroupState();
+        }
     }
 
     @Override
@@ -544,15 +568,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     }
 
     public NotificationHeaderView getNotificationHeader() {
-        if (mNotificationHeader != null) {
-            return mNotificationHeader;
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getHeaderView();
         }
         return mPrivateLayout.getNotificationHeader();
     }
 
     private NotificationHeaderView getVisibleNotificationHeader() {
-        if (mNotificationHeader != null) {
-            return mNotificationHeader;
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getHeaderView();
         }
         return getShowingLayout().getVisibleNotificationHeader();
     }
@@ -568,11 +592,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     public void reInflateViews() {
         initDimens();
         if (mIsSummaryWithChildren) {
-            removeView(mNotificationHeader);
-            mNotificationHeader = null;
-            recreateNotificationHeader();
             if (mChildrenContainer != null) {
-                mChildrenContainer.reInflateViews();
+                mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
             }
         }
         if (mGuts != null) {
@@ -646,9 +667,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
                 continue;
             }
             mChildrenContainer.removeNotification(row);
-            if (!row.isRemoved()) {
-                mHeaderUtil.restoreNotificationHeader(row);
-            }
             row.setIsChildInGroup(false, null);
         }
         onChildrenCountChanged();
@@ -684,8 +702,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         return mRemoved;
     }
 
-    public void setRemoved(boolean removed) {
-        mRemoved = removed;
+    public void setRemoved() {
+        mRemoved = true;
+
+        mPrivateLayout.setRemoved();
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setRemoved();
+        }
     }
 
     public NotificationChildrenContainer getChildrenContainer() {
@@ -709,6 +732,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         mMaxHeadsUpHeightLegacy = getFontScaledHeight(
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
+        mIncreasedPaddingBetweenElements = getResources()
+                .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
     }
 
     /**
@@ -804,17 +829,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         mTranslateableViews.remove(mGutsStub);
     }
 
-    private void setTranslationForOutline(float translationX) {
-        setOutlineRect(false, translationX, getTop(), getRight() + translationX, getBottom());
-    }
-
     public void resetTranslation() {
         if (mTranslateableViews != null) {
             for (int i = 0; i < mTranslateableViews.size(); i++) {
                 mTranslateableViews.get(i).setTranslationX(0);
             }
-            setTranslationForOutline(0);
         }
+        invalidateOutline();
         if (mSettingsIconRow != null) {
             mSettingsIconRow.resetState();
         }
@@ -824,8 +845,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         if (mTranslateAnim != null) {
             mTranslateAnim.cancel();
         }
-        mTranslateAnim = (AnimatorSet) getTranslateViewAnimator(leftTarget,
-                null /* updateListener */);
+        mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
         if (mTranslateAnim != null) {
             mTranslateAnim.start();
         }
@@ -843,7 +863,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
                 mTranslateableViews.get(i).setTranslationX(translationX);
             }
         }
-        setTranslationForOutline(translationX);
+        invalidateOutline();
         if (mSettingsIconRow != null) {
             mSettingsIconRow.updateSettingsIcons(translationX, getMeasuredWidth());
         }
@@ -867,48 +887,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
             // No translation if guts are exposed.
             return null;
         }
-        AnimatorSet set = new AnimatorSet();
-        if (mTranslateableViews != null) {
-            for (int i = 0; i < mTranslateableViews.size(); i++) {
-                final View animView = mTranslateableViews.get(i);
-                final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(
-                        animView, "translationX", leftTarget);
-                if (i == 0) {
-                    translateAnim.addUpdateListener(new AnimatorUpdateListener() {
-                        @Override
-                        public void onAnimationUpdate(ValueAnimator animation) {
-                            setTranslationForOutline((float) animation.getAnimatedValue());
-                            if (mSettingsIconRow != null) {
-                                mSettingsIconRow.updateSettingsIcons(
-                                        (float) animation.getAnimatedValue(), getMeasuredWidth());
-                            }
-                        }
-                    });
-                    if (listener != null) {
-                        translateAnim.addUpdateListener(listener);
-                    }
-                    translateAnim.addListener(new AnimatorListenerAdapter() {
-                        boolean cancelled = false;
+        final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
+                leftTarget);
+        if (listener != null) {
+            translateAnim.addUpdateListener(listener);
+        }
+        translateAnim.addListener(new AnimatorListenerAdapter() {
+            boolean cancelled = false;
 
-                        @Override
-                        public void onAnimationCancel(Animator anim) {
-                            cancelled = true;
-                        }
+            @Override
+            public void onAnimationCancel(Animator anim) {
+                cancelled = true;
+            }
 
-                        @Override
-                        public void onAnimationEnd(Animator anim) {
-                            if (!cancelled && mSettingsIconRow != null && leftTarget == 0) {
-                                mSettingsIconRow.resetState();
-                                mTranslateAnim = null;
-                            }
-                        }
-                    });
+            @Override
+            public void onAnimationEnd(Animator anim) {
+                if (!cancelled && mSettingsIconRow != null && leftTarget == 0) {
+                    mSettingsIconRow.resetState();
+                    mTranslateAnim = null;
                 }
-                set.play(translateAnim);
             }
-        }
-        mTranslateAnim = set;
-        return set;
+        });
+        mTranslateAnim = translateAnim;
+        return translateAnim;
     }
 
     public float getSpaceForGear() {
@@ -937,9 +938,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         if (mChildrenContainer != null) {
             mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
                     : INVISIBLE);
-        }
-        if (mNotificationHeader != null) {
-            mNotificationHeader.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
+            mChildrenContainer.updateHeaderVisibility(!mShowingPublic && mIsSummaryWithChildren
+                    ? VISIBLE
                     : INVISIBLE);
         }
         // The limits might have changed if the view suddenly became a group or vice versa
@@ -969,7 +969,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         }
         if (mIsSummaryWithChildren) {
             mChildrenContainer.setDark(dark, fade, delay);
-            mNotificationHeaderWrapper.setDark(dark, fade, delay);
         }
     }
 
@@ -1044,6 +1043,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         mPrivateLayout.setUserExpanding(userLocked);
         if (mIsSummaryWithChildren) {
             mChildrenContainer.setUserLocked(userLocked);
+            if (userLocked) {
+                updateBackgroundForGroupState();
+            }
         }
     }
 
@@ -1123,30 +1125,28 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         }
     }
 
-    private boolean isGroupExpanded() {
+    public boolean isGroupExpanded() {
         return mGroupManager.isGroupExpanded(mStatusBarNotification);
     }
 
-    /**
-     * @return whether this view has a header on the top of the content
-     */
-    private boolean hasNotificationHeader() {
-        return mIsSummaryWithChildren;
-    }
-
     private void onChildrenCountChanged() {
         mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
-                && mChildrenContainer != null && mChildrenContainer.getChildCount() > 0;
-        if (mIsSummaryWithChildren) {
-            if (mNotificationHeader == null) {
-                recreateNotificationHeader();
-            }
+                && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
+        if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
+            mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
+                    mEntry.notification);
         }
         mPrivateLayout.updateExpandButtons(isExpandable());
         updateChildrenHeaderAppearance();
         updateChildrenVisibility();
     }
 
+    public void updateChildrenHeaderAppearance() {
+        if (mChildrenContainer != null) {
+            mChildrenContainer.updateChildrenHeaderAppearance();
+        }
+    }
+
     /**
      * Check whether the view state is currently expanded. This is given by the system in {@link
      * #setSystemExpanded(boolean)} and can be overridden by user expansion or
@@ -1251,8 +1251,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     }
 
     private void animateShowingPublic(long delay, long duration) {
-        View[] privateViews = mIsSummaryWithChildren ?
-                new View[] {mChildrenContainer, mNotificationHeader}
+        View[] privateViews = mIsSummaryWithChildren
+                ? new View[] {mChildrenContainer}
                 : new View[] {mPrivateLayout};
         View[] publicViews = new View[] {mPublicLayout};
         View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
@@ -1302,9 +1302,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
 
     public void setChildrenExpanded(boolean expanded, boolean animate) {
         mChildrenExpanded = expanded;
-        if (mNotificationHeader != null) {
-            mNotificationHeader.setExpanded(expanded);
-        }
         if (mChildrenContainer != null) {
             mChildrenContainer.setChildrenExpanded(expanded);
         }
@@ -1349,6 +1346,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     }
 
     @Override
+    public int getExtraBottomPadding() {
+        if (mIsSummaryWithChildren && isGroupExpanded()) {
+            return mIncreasedPaddingBetweenElements;
+        }
+        return 0;
+    }
+
+    @Override
     public void setActualHeight(int height, boolean notifyListeners) {
         super.setActualHeight(height, notifyListeners);
         if (mGuts != null && mGuts.areGutsExposed()) {
@@ -1406,33 +1411,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
         }
     }
 
-    private void recreateNotificationHeader() {
-        final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
-                getStatusBarNotification().getNotification());
-        final RemoteViews header = builder.makeNotificationHeader();
-        if (mNotificationHeader == null) {
-            mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
-            final View expandButton = mNotificationHeader.findViewById(
-                    com.android.internal.R.id.expand_button);
-            expandButton.setVisibility(VISIBLE);
-            mNotificationHeader.setOnClickListener(mExpandClickListener);
-            mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
-                    mNotificationHeader, this);
-            addView(mNotificationHeader, indexOfChild(mChildrenContainer) + 1);
-            mTranslateableViews.add(mNotificationHeader);
-        } else {
-            header.reapply(getContext(), mNotificationHeader);
-            mNotificationHeaderWrapper.notifyContentUpdated(mEntry.notification);
-        }
-        updateChildrenHeaderAppearance();
-    }
-
-    public void updateChildrenHeaderAppearance() {
-        if (mIsSummaryWithChildren) {
-            mHeaderUtil.updateChildrenHeaderAppearance();
-        }
-    }
-
     public boolean isMaxExpandHeightInitialized() {
         return mMaxExpandHeight != 0;
     }
@@ -1451,19 +1429,50 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
     @Override
     protected void updateBackgroundTint() {
         super.updateBackgroundTint();
-        updateNoBackgroundState();
+        updateBackgroundForGroupState();
         if (mIsSummaryWithChildren) {
             List<ExpandableNotificationRow> notificationChildren =
                     mChildrenContainer.getNotificationChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
                 ExpandableNotificationRow child = notificationChildren.get(i);
-                child.updateNoBackgroundState();
+                child.updateBackgroundForGroupState();
             }
         }
     }
 
-    private void updateNoBackgroundState() {
-        mShowNoBackground = isChildInGroup() && hasSameBgColor(mNotificationParent);
+    /**
+     * Called when a group has finished animating from collapsed or expanded state.
+     */
+    public void onFinishedExpansionChange() {
+        mGroupExpansionChanging = false;
+        updateBackgroundForGroupState();
+    }
+
+    /**
+     * Updates the parent and children backgrounds in a group based on the expansion state.
+     */
+    public void updateBackgroundForGroupState() {
+        if (mIsSummaryWithChildren) {
+            // Only when the group has finished expanding do we hide its background.
+            mShowNoBackground = isGroupExpanded() && !isGroupExpansionChanging() && !isUserLocked();
+            mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
+            List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < children.size(); i++) {
+                children.get(i).updateBackgroundForGroupState();
+            }
+        } else if (isChildInGroup()) {
+            final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
+            // Only show a background if the group is expanded OR if it is expanding / collapsing
+            // and has a custom background color
+            final boolean showBackground = isGroupExpanded()
+                    || ((mNotificationParent.isGroupExpansionChanging()
+                            || mNotificationParent.isUserLocked()) && childColor != 0);
+            mShowNoBackground = !showBackground;
+        } else {
+            // Only children or parents ever need no background.
+            mShowNoBackground = false;
+        }
+        updateOutline();
         updateBackground();
     }
 
index f98e87d..9d9f3b9 100644 (file)
@@ -33,22 +33,25 @@ public abstract class ExpandableOutlineView extends ExpandableView {
     private boolean mCustomOutline;
     private float mOutlineAlpha = -1f;
 
+    ViewOutlineProvider mProvider = new ViewOutlineProvider() {
+        @Override
+        public void getOutline(View view, Outline outline) {
+            int translation = (int) getTranslation();
+            if (!mCustomOutline) {
+                outline.setRect(translation,
+                        mClipTopAmount,
+                        getWidth() + translation,
+                        Math.max(getActualHeight(), mClipTopAmount));
+            } else {
+                outline.setRect(mOutlineRect);
+            }
+            outline.setAlpha(mOutlineAlpha);
+        }
+    };
+
     public ExpandableOutlineView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                if (!mCustomOutline) {
-                    outline.setRect(0,
-                            mClipTopAmount,
-                            getWidth(),
-                            Math.max(getActualHeight(), mClipTopAmount));
-                } else {
-                    outline.setRect(mOutlineRect);
-                }
-                outline.setAlpha(mOutlineAlpha);
-            }
-        });
+        setOutlineProvider(mProvider);
     }
 
     @Override
@@ -87,17 +90,30 @@ public abstract class ExpandableOutlineView extends ExpandableView {
 
     @Override
     public int getOutlineTranslation() {
-        return mCustomOutline ? mOutlineRect.left : 0;
+        return mCustomOutline ? mOutlineRect.left : (int) getTranslation();
     }
 
-    protected void setOutlineRect(float left, float top, float right, float bottom) {
-        setOutlineRect(true, left, top, right, bottom);
+    public void updateOutline() {
+        if (mCustomOutline) {
+            return;
+        }
+        boolean hasOutline = true;
+        if (isChildInGroup()) {
+            hasOutline = isGroupExpanded() && !isGroupExpansionChanging();
+        } else if (isSummaryWithChildren()) {
+            hasOutline = !isGroupExpanded() || isGroupExpansionChanging();
+        }
+        setOutlineProvider(hasOutline ? mProvider : null);
     }
 
-    protected void setOutlineRect(boolean clipToOutline, float left, float top, float right,
-            float bottom) {
+    public boolean isOutlineShowing() {
+        ViewOutlineProvider op = getOutlineProvider();
+        return op != null;
+    }
+
+    protected void setOutlineRect(float left, float top, float right, float bottom) {
         mCustomOutline = true;
-        setClipToOutline(clipToOutline);
+        setClipToOutline(true);
 
         mOutlineRect.set((int) left, (int) top, (int) right, (int) bottom);
 
index 280663b..2c302ed 100644 (file)
@@ -323,7 +323,7 @@ public abstract class ExpandableView extends FrameLayout {
             if (top >= getActualHeight()) {
                 top = getActualHeight() - 1;
             }
-            mClipRect.set(0, top, getWidth(), getActualHeight());
+            mClipRect.set(0, top, getWidth(), getActualHeight() + getExtraBottomPadding());
             setClipBounds(mClipRect);
         } else {
             setClipBounds(null);
@@ -411,6 +411,28 @@ public abstract class ExpandableView extends FrameLayout {
     }
 
     /**
+     * @return padding used to alter how much of the view is clipped.
+     */
+    public int getExtraBottomPadding() {
+        return 0;
+    }
+
+    /**
+     * @return true if the group's expansion state is changing, false otherwise.
+     */
+    public boolean isGroupExpansionChanging() {
+        return false;
+    }
+
+    public boolean isGroupExpanded() {
+        return false;
+    }
+
+    public boolean isChildInGroup() {
+        return false;
+    }
+
+    /**
      * A listener notifying when {@link #getActualHeight} changes.
      */
     public interface OnHeightChangedListener {
index 5a8d4b3..32b61cd 100644 (file)
@@ -71,7 +71,6 @@ public class NotificationContentView extends FrameLayout {
     private HybridGroupManager mHybridGroupManager;
     private int mClipTopAmount;
     private int mContentHeight;
-    private int mUnrestrictedContentHeight;
     private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
     private boolean mDark;
     private boolean mAnimate;
@@ -371,8 +370,7 @@ public class NotificationContentView extends FrameLayout {
     }
 
     public void setContentHeight(int contentHeight) {
-        mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());;
-        mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
+        mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
         selectLayout(mAnimate /* animate */, false /* force */);
 
         int minHeightHint = getMinContentHeightHint();
@@ -591,7 +589,21 @@ public class NotificationContentView extends FrameLayout {
         mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
     }
 
-    private int getBackgroundColor(int visibleType) {
+    public int getVisibleType() {
+        return mVisibleType;
+    }
+
+    public int getBackgroundColorForExpansionState() {
+        // When expanding or user locked we want the new type, when collapsing we want
+        // the original type
+        final int visibleType = (mContainingNotification.isGroupExpanded()
+                || mContainingNotification.isUserLocked())
+                        ? calculateVisibleType()
+                        : getVisibleType();
+        return getBackgroundColor(visibleType);
+    }
+
+    public int getBackgroundColor(int visibleType) {
         NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
         int customBackgroundColor = 0;
         if (currentVisibleWrapper != null) {
@@ -699,7 +711,7 @@ public class NotificationContentView extends FrameLayout {
     /**
      * @return one of the static enum types in this view, calculated form the current state
      */
-    private int calculateVisibleType() {
+    public int calculateVisibleType() {
         if (mUserExpanding) {
             int height = !mIsChildInGroup || isGroupExpanded()
                     || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
@@ -1041,4 +1053,13 @@ public class NotificationContentView extends FrameLayout {
     public HybridNotificationView getSingleLineView() {
         return mSingleLineView;
     }
+
+    public void setRemoved() {
+        if (mExpandedRemoteInput != null) {
+            mExpandedRemoteInput.setRemoved();
+        }
+        if (mHeadsUpRemoteInput != null) {
+            mHeadsUpRemoteInput.setRemoved();
+        }
+    }
 }
index 7575dc8..7f95d48 100644 (file)
@@ -90,7 +90,7 @@ public class NotificationHeaderUtil {
     private final ArrayList<HeaderProcessor> mComparators = new ArrayList<>();
     private final HashSet<Integer> mDividers = new HashSet<>();
 
-    NotificationHeaderUtil(ExpandableNotificationRow row) {
+    public NotificationHeaderUtil(ExpandableNotificationRow row) {
         mRow = row;
         // To hide the icons if they are the same and the color is the same
         mComparators.add(new HeaderProcessor(mRow,
index 073994f..31fa3bf 100644 (file)
@@ -263,6 +263,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
      * 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
             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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
                 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
     }
 
     @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 class PhoneStatusBar extends BaseStatusBar implements DemoMode,
 
     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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
                 || (state == StatusBarState.SHADE && isGoingToNotificationShade()))) {
             clearNotificationEffects();
         }
+        if (state == StatusBarState.KEYGUARD) {
+            removeRemoteInputEntriesKeptUntilCollapsed();
+        }
         mState = state;
         mGroupManager.setStatusBarState(state);
         mFalsingManager.setStatusBarState(state);
@@ -4335,6 +4390,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         if (expandView instanceof ExpandableNotificationRow) {
             row = (ExpandableNotificationRow) expandView;
             row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
+            // Indicate that the group expansion is changing at this time -- this way the group
+            // and children backgrounds / divider animations will look correct.
+            row.setGroupExpansionChanging(true);
         }
         boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
                 || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
index 095265a..2f522f0 100644 (file)
@@ -73,6 +73,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
 
     private ScrollContainer mScrollContainer;
     private View mScrollContainerChild;
+    private boolean mRemoved;
 
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -171,7 +172,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
     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 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
         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 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
         }
 
         @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);
 
index e919d5c..cb0b848 100644 (file)
 
 package com.android.systemui.statusbar.stack;
 
+import android.app.Notification;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.drawable.ColorDrawable;
+import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
+import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.RemoteViews;
 import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.ViewInvertHelper;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationHeaderUtil;
 import com.android.systemui.statusbar.notification.HybridGroupManager;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 import java.util.ArrayList;
@@ -51,7 +58,7 @@ public class NotificationChildrenContainer extends ViewGroup {
     private int mChildPadding;
     private int mDividerHeight;
     private int mMaxNotificationHeight;
-    private int mNotificationHeaderHeight;
+    private int mNotificationHeaderMargin;
     private int mNotificatonTopPadding;
     private float mCollapsedBottompadding;
     private ViewInvertHelper mOverflowInvertHelper;
@@ -63,6 +70,12 @@ public class NotificationChildrenContainer extends ViewGroup {
     private boolean mUserLocked;
     private int mActualHeight;
     private boolean mNeverAppliedGroupState;
+    private int mHeaderHeight;
+
+    private NotificationHeaderView mNotificationHeader;
+    private NotificationViewWrapper mNotificationHeaderWrapper;
+    private NotificationHeaderUtil mHeaderUtil;
+    private ViewState mHeaderViewState;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -88,9 +101,10 @@ public class NotificationChildrenContainer extends ViewGroup {
                 R.dimen.notification_children_padding);
         mDividerHeight = Math.max(1, getResources().getDimensionPixelSize(
                 R.dimen.notification_divider_height));
+        mHeaderHeight = getResources().getDimensionPixelSize(R.dimen.notification_header_height);
         mMaxNotificationHeight = getResources().getDimensionPixelSize(
                 R.dimen.notification_max_height);
-        mNotificationHeaderHeight = getResources().getDimensionPixelSize(
+        mNotificationHeaderMargin = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin_top);
         mNotificatonTopPadding = getResources().getDimensionPixelSize(
                 R.dimen.notification_children_container_top_padding);
@@ -112,6 +126,10 @@ public class NotificationChildrenContainer extends ViewGroup {
             mOverflowNumber.layout(getWidth() - mOverflowNumber.getMeasuredWidth(), 0, getWidth(),
                     mOverflowNumber.getMeasuredHeight());
         }
+        if (mNotificationHeader != null) {
+            mNotificationHeader.layout(0, 0, mNotificationHeader.getMeasuredWidth(),
+                    mNotificationHeader.getMeasuredHeight());
+        }
     }
 
     @Override
@@ -131,7 +149,7 @@ public class NotificationChildrenContainer extends ViewGroup {
                     newHeightSpec);
         }
         int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
-        int height = mNotificationHeaderHeight + mNotificatonTopPadding;
+        int height = mNotificationHeaderMargin + mNotificatonTopPadding;
         int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
         int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
         int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
@@ -155,6 +173,12 @@ public class NotificationChildrenContainer extends ViewGroup {
         if (heightMode != MeasureSpec.UNSPECIFIED) {
             height = Math.min(height, size);
         }
+
+        if (mNotificationHeader != null) {
+            int headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
+            mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
+        }
+
         setMeasuredDimension(width, height);
     }
 
@@ -201,6 +225,41 @@ public class NotificationChildrenContainer extends ViewGroup {
         row.setSystemChildExpanded(false);
         row.setUserLocked(false);
         updateGroupOverflow();
+        if (!row.isRemoved()) {
+            mHeaderUtil.restoreNotificationHeader(row);
+        }
+    }
+
+    /**
+     * @return The number of notification children in the container.
+     */
+    public int getNotificationChildCount() {
+        return mChildren.size();
+    }
+
+    public void recreateNotificationHeader(OnClickListener listener, StatusBarNotification notification) {
+        final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
+                mNotificationParent.getStatusBarNotification().getNotification());
+        final RemoteViews header = builder.makeNotificationHeader();
+        if (mNotificationHeader == null) {
+            mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
+            final View expandButton = mNotificationHeader.findViewById(
+                    com.android.internal.R.id.expand_button);
+            expandButton.setVisibility(VISIBLE);
+            mNotificationHeader.setOnClickListener(listener);
+            mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
+                    mNotificationHeader, mNotificationParent);
+            addView(mNotificationHeader, 0);
+            invalidate();
+        } else {
+            header.reapply(getContext(), mNotificationHeader);
+            mNotificationHeaderWrapper.notifyContentUpdated(notification);
+        }
+        updateChildrenHeaderAppearance();
+    }
+
+    public void updateChildrenHeaderAppearance() {
+        mHeaderUtil.updateChildrenHeaderAppearance();
     }
 
     public void updateGroupOverflow() {
@@ -210,7 +269,7 @@ public class NotificationChildrenContainer extends ViewGroup {
             mOverflowNumber = mHybridGroupManager.bindOverflowNumber(
                     mOverflowNumber, childCount - maxAllowedVisibleChildren);
             if (mOverflowInvertHelper == null) {
-                mOverflowInvertHelper= new ViewInvertHelper(mOverflowNumber,
+                mOverflowInvertHelper = new ViewInvertHelper(mOverflowNumber,
                         NotificationPanelView.DOZE_ANIMATION_DURATION);
             }
             if (mGroupOverFlowState == null) {
@@ -300,7 +359,7 @@ public class NotificationChildrenContainer extends ViewGroup {
      *         in @param maxAllowedVisibleChildren
      */
     private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
-        int intrinsicHeight = mNotificationHeaderHeight;
+        int intrinsicHeight = mNotificationHeaderMargin;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         boolean firstChild = true;
@@ -353,7 +412,7 @@ public class NotificationChildrenContainer extends ViewGroup {
      */
     public void getState(StackScrollState resultState, StackViewState parentState) {
         int childCount = mChildren.size();
-        int yPosition = mNotificationHeaderHeight;
+        int yPosition = mNotificationHeaderMargin;
         boolean firstChild = true;
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
         int lastVisibleIndex = maxAllowedVisibleChildren - 1;
@@ -363,12 +422,16 @@ public class NotificationChildrenContainer extends ViewGroup {
             expandFactor = getGroupExpandFraction();
             firstOverflowIndex = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
         }
+
+        boolean childrenExpanded = !mNotificationParent.isGroupExpansionChanging()
+                && mChildrenExpanded;
+        int parentHeight = parentState.height;
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             if (!firstChild) {
                 if (mUserLocked) {
-                     yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
-                             expandFactor);
+                    yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
+                            expandFactor);
                 } else {
                     yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
                 }
@@ -383,11 +446,28 @@ public class NotificationChildrenContainer extends ViewGroup {
                 }
                 firstChild = false;
             }
+
             StackViewState childState = resultState.getViewStateForView(child);
             int intrinsicHeight = child.getIntrinsicHeight();
+            if (childrenExpanded) {
+                // When a group is expanded and moving into bottom stack, the bottom visible child
+                // adjusts its height to move into it. Children after it are hidden.
+                if (updateChildStateForExpandedGroup(child, parentHeight, childState, yPosition)) {
+                    // Clipping might be deactivated if the view is transforming, however, clipping
+                    // the child into the bottom stack should take precedent over this.
+                    childState.isBottomClipped = true;
+                }
+            } else {
+                childState.hidden = false;
+                childState.height = intrinsicHeight;
+                childState.isBottomClipped = false;
+            }
             childState.yTranslation = yPosition;
-            childState.zTranslation = 0;
-            childState.height = intrinsicHeight;
+            // When the group is expanded, the children cast the shadows rather than the parent
+            // so use the parent's elevation here.
+            childState.zTranslation = childrenExpanded
+                    ? mNotificationParent.getTranslationZ()
+                    : 0;
             childState.dimmed = parentState.dimmed;
             childState.dark = parentState.dark;
             childState.hideSensitive = parentState.hideSensitive;
@@ -422,10 +502,44 @@ public class NotificationChildrenContainer extends ViewGroup {
                     mGroupOverFlowState.alpha = mirrorView.getAlpha();
                 }
             } else {
-                mGroupOverFlowState.yTranslation += mNotificationHeaderHeight;
+                mGroupOverFlowState.yTranslation += mNotificationHeaderMargin;
                 mGroupOverFlowState.alpha = 0.0f;
             }
         }
+        if (mNotificationHeader != null) {
+            if (mHeaderViewState == null) {
+                mHeaderViewState = new ViewState();
+            }
+            mHeaderViewState.initFrom(mNotificationHeader);
+            mHeaderViewState.zTranslation = childrenExpanded
+                    ? mNotificationParent.getTranslationZ()
+                    : 0;
+        }
+    }
+
+    /**
+     * When moving into the bottom stack, the bottom visible child in an expanded group adjusts its
+     * height, children in the group after this are gone.
+     *
+     * @param child the child who's height to adjust.
+     * @param parentHeight the height of the parent.
+     * @param childState the state to update.
+     * @param yPosition the yPosition of the view.
+     * @return true if children after this one should be hidden.
+     */
+    private boolean updateChildStateForExpandedGroup(ExpandableNotificationRow child,
+            int parentHeight, StackViewState childState, int yPosition) {
+        final int top = yPosition + child.getClipTopAmount();
+        final int intrinsicHeight = child.getIntrinsicHeight();
+        final int bottom = top + intrinsicHeight;
+        int newHeight = intrinsicHeight;
+        if (bottom >= parentHeight) {
+            // Child is either clipped or gone
+            newHeight = Math.max((parentHeight - top), 0);
+        }
+        childState.hidden = newHeight == 0;
+        childState.height = newHeight;
+        return childState.height != intrinsicHeight && !childState.hidden;
     }
 
     private int getMaxAllowedVisibleChildren() {
@@ -450,6 +564,8 @@ public class NotificationChildrenContainer extends ViewGroup {
         if (mUserLocked) {
             expandFraction = getGroupExpandFraction();
         }
+        final boolean dividersVisible = mUserLocked
+                || mNotificationParent.isGroupExpansionChanging();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
@@ -464,6 +580,7 @@ public class NotificationChildrenContainer extends ViewGroup {
                 alpha = NotificationUtils.interpolate(0, 0.5f,
                         Math.min(viewState.alpha, expandFraction));
             }
+            tmpState.hidden = !dividersVisible;
             tmpState.alpha = alpha;
             state.applyViewState(divider, tmpState);
             // There is no fake shadow to be drawn on the children
@@ -473,6 +590,9 @@ public class NotificationChildrenContainer extends ViewGroup {
             state.applyViewState(mOverflowNumber, mGroupOverFlowState);
             mNeverAppliedGroupState = false;
         }
+        if (mNotificationHeader != null) {
+            state.applyViewState(mNotificationHeader, mHeaderViewState);
+        }
     }
 
     /**
@@ -491,6 +611,8 @@ public class NotificationChildrenContainer extends ViewGroup {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
         float expandFraction = getGroupExpandFraction();
+        final boolean dividersVisible = mUserLocked
+                || mNotificationParent.isGroupExpansionChanging();
         for (int i = childCount - 1; i >= 0; i--) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
@@ -505,6 +627,7 @@ public class NotificationChildrenContainer extends ViewGroup {
                 alpha = NotificationUtils.interpolate(0, 0.5f,
                         Math.min(viewState.alpha, expandFraction));
             }
+            tmpState.hidden = !dividersVisible;
             tmpState.alpha = alpha;
             stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration);
             // There is no fake shadow to be drawn on the children
@@ -521,6 +644,9 @@ public class NotificationChildrenContainer extends ViewGroup {
             stateAnimator.startViewAnimations(mOverflowNumber, mGroupOverFlowState,
                     baseDelay, duration);
         }
+        if (mNotificationHeader != null) {
+            state.applyViewState(mNotificationHeader, mHeaderViewState);
+        }
     }
 
     public ExpandableNotificationRow getViewAtPosition(float y) {
@@ -541,14 +667,45 @@ public class NotificationChildrenContainer extends ViewGroup {
     public void setChildrenExpanded(boolean childrenExpanded) {
         mChildrenExpanded = childrenExpanded;
         updateExpansionStates();
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setExpanded(childrenExpanded);
+        }
     }
 
     public void setNotificationParent(ExpandableNotificationRow parent) {
         mNotificationParent = parent;
+        mHeaderUtil = new NotificationHeaderUtil(mNotificationParent);
+    }
+
+    public NotificationHeaderView getHeaderView() {
+        return mNotificationHeader;
+    }
+
+    public void updateHeaderVisibility(int visiblity) {
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setVisibility(visiblity);
+        }
+    }
+
+    /**
+     * Called when a groups expansion changes to adjust the background of the header view.
+     *
+     * @param expanded whether the group is expanded.
+     */
+    public void updateHeaderForExpansion(boolean expanded) {
+        if (mNotificationHeader != null) {
+            if (expanded) {
+                ColorDrawable cd = new ColorDrawable();
+                cd.setColor(mNotificationParent.calculateBgColor());
+                mNotificationHeader.setHeaderBackgroundDrawable(cd);
+            } else {
+                mNotificationHeader.setHeaderBackgroundDrawable(null);
+            }
+        }
     }
 
     public int getMaxContentHeight() {
-        int maxContentHeight = mNotificationHeaderHeight + mNotificatonTopPadding;
+        int maxContentHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
@@ -601,7 +758,7 @@ public class NotificationChildrenContainer extends ViewGroup {
     }
 
     private int getVisibleChildrenExpandHeight() {
-        int intrinsicHeight = mNotificationHeaderHeight + mNotificatonTopPadding + mDividerHeight;
+        int intrinsicHeight = mNotificationHeaderMargin + mNotificatonTopPadding + mDividerHeight;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
@@ -628,7 +785,7 @@ public class NotificationChildrenContainer extends ViewGroup {
     }
 
     private int getMinHeight(int maxAllowedVisibleChildren) {
-        int minExpandHeight = mNotificationHeaderHeight;
+        int minExpandHeight = mNotificationHeaderMargin;
         int visibleChildren = 0;
         boolean firstChild = true;
         int childCount = mChildren.size();
@@ -653,9 +810,13 @@ public class NotificationChildrenContainer extends ViewGroup {
         if (mOverflowNumber != null) {
             mOverflowInvertHelper.setInverted(dark, fade, delay);
         }
+        mNotificationHeaderWrapper.setDark(dark, fade, delay);
     }
 
-    public void reInflateViews() {
+    public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
+        removeView(mNotificationHeader);
+        mNotificationHeader = null;
+        recreateNotificationHeader(listener, notification);
         initDimens();
         for (int i = 0; i < mDividers.size(); i++) {
             View prevDivider = mDividers.get(i);
@@ -685,4 +846,12 @@ public class NotificationChildrenContainer extends ViewGroup {
         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();
+        }
+    }
 }
index f7a2d2b..19f4074 100644 (file)
@@ -34,6 +34,7 @@ import android.graphics.PointF;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -47,6 +48,8 @@ import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.OverScroller;
@@ -336,6 +339,7 @@ public class NotificationStackScrollLayout extends ViewGroup
     private boolean mFadingOut;
     private boolean mParentFadingOut;
     private boolean mGroupExpandedForMeasure;
+    private boolean mScrollable;
     private View mForcedScroll;
     private float mBackgroundFadeAmount = 1.0f;
     private static final Property<NotificationStackScrollLayout, Float> BACKGROUND_FADE =
@@ -443,7 +447,6 @@ public class NotificationStackScrollLayout extends ViewGroup
 
     private void initView(Context context) {
         mScroller = new OverScroller(getContext());
-        setFocusable(true);
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
         setClipChildren(false);
         final ViewConfiguration configuration = ViewConfiguration.get(context);
@@ -730,6 +733,7 @@ public class NotificationStackScrollLayout extends ViewGroup
         mQsContainer = qsContainer;
     }
 
+    @Override
     public void onChildDismissed(View v) {
         ExpandableNotificationRow row = (ExpandableNotificationRow) v;
         if (!row.isDismissed()) {
@@ -817,6 +821,7 @@ public class NotificationStackScrollLayout extends ViewGroup
         return true; // Don't fade out the notification
     }
 
+    @Override
     public void onBeginDrag(View v) {
         mFalsingManager.onNotificatonStartDismissing();
         setSwipingInProgress(true);
@@ -845,6 +850,7 @@ public class NotificationStackScrollLayout extends ViewGroup
         return false;
     }
 
+    @Override
     public void onDragCancelled(View v) {
         mFalsingManager.onNotificatonStopDismissing();
         setSwipingInProgress(false);
@@ -903,11 +909,13 @@ public class NotificationStackScrollLayout extends ViewGroup
         return closestChild;
     }
 
+    @Override
     public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
         getLocationOnScreen(mTempInt2);
         return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
     }
 
+    @Override
     public ExpandableView getChildAtPosition(float touchX, float touchY) {
         // find the view under the pointer, accounting for GONE views
         final int count = getChildCount();
@@ -944,12 +952,14 @@ public class NotificationStackScrollLayout extends ViewGroup
         return null;
     }
 
+    @Override
     public boolean canChildBeExpanded(View v) {
         return v instanceof ExpandableNotificationRow
                 && ((ExpandableNotificationRow) v).isExpandable()
                 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
     }
 
+    @Override
     public void setUserExpandedChild(View v, boolean userExpanded) {
         if (v instanceof ExpandableNotificationRow) {
             ((ExpandableNotificationRow) v).setUserExpanded(userExpanded,
@@ -957,6 +967,14 @@ public class NotificationStackScrollLayout extends ViewGroup
         }
     }
 
+    @Override
+    public void setExpansionCancelled(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
+        }
+    }
+
+    @Override
     public void setUserLockedChild(View v, boolean userLocked) {
         if (v instanceof ExpandableNotificationRow) {
             ((ExpandableNotificationRow) v).setUserLocked(userLocked);
@@ -1067,6 +1085,7 @@ public class NotificationStackScrollLayout extends ViewGroup
         return mScrollingEnabled;
     }
 
+    @Override
     public boolean canChildBeDismissed(View v) {
         return StackScrollAlgorithm.canChildBeDismissed(v);
     }
@@ -1728,6 +1747,15 @@ public class NotificationStackScrollLayout extends ViewGroup
             }
         }
         mContentHeight = height + mTopPadding;
+        updateScrollability();
+    }
+
+    private void updateScrollability() {
+        boolean scrollable = getScrollRange() > 0;
+        if (scrollable != mScrollable) {
+            mScrollable = scrollable;
+            setFocusable(scrollable);
+        }
     }
 
     private void updateBackground() {
@@ -1915,7 +1943,8 @@ public class NotificationStackScrollLayout extends ViewGroup
                 // we're ending up at the same location as we are now, lets just skip the animation
                 bottom = finalBottom;
             } else {
-                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight());
+                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
+                        - lastView.getExtraBottomPadding());
                 bottom = Math.min(bottom, getHeight());
             }
         } else {
@@ -2482,9 +2511,9 @@ public class NotificationStackScrollLayout extends ViewGroup
             mNeedsAnimation = false;
         }
         if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
+            setAnimationRunning(true);
             mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
                     mGoToFullShadeDelay);
-            setAnimationRunning(true);
             mAnimationEvents.clear();
             updateBackground();
             updateViewShadows();
@@ -2929,16 +2958,6 @@ public class NotificationStackScrollLayout extends ViewGroup
         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();
@@ -3112,6 +3131,7 @@ public class NotificationStackScrollLayout extends ViewGroup
             mListener.onChildLocationsChanged(this);
         }
         runAnimationFinishedRunnables();
+        setAnimationRunning(false);
         updateBackground();
         updateViewShadows();
     }
@@ -3141,8 +3161,9 @@ public class NotificationStackScrollLayout extends ViewGroup
                 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
             } else {
                 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
-                        expandableView.getTranslationY();
-                expandableView.setFakeShadowIntensity(diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
+                        expandableView.getTranslationY() - previous.getExtraBottomPadding();
+                expandableView.setFakeShadowIntensity(
+                        diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
                         previous.getOutlineAlpha(), (int) yLocation,
                         previous.getOutlineTranslation());
             }
@@ -3227,6 +3248,7 @@ public class NotificationStackScrollLayout extends ViewGroup
             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);
@@ -3513,6 +3535,12 @@ public class NotificationStackScrollLayout extends ViewGroup
         if (!mGroupExpandedForMeasure) {
             onHeightChanged(changedRow, false /* needsAnimation */);
         }
+        runAfterAnimationFinished(new Runnable() {
+            @Override
+            public void run() {
+                changedRow.onFinishedExpansionChange();
+            }
+        });
     }
 
     @Override
@@ -3520,6 +3548,68 @@ public class NotificationStackScrollLayout extends ViewGroup
         mPhoneStatusBar.requestNotificationUpdate();
     }
 
+    /** @hide */
+    @Override
+    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEventInternal(event);
+        event.setScrollable(mScrollable);
+        event.setScrollX(mScrollX);
+        event.setScrollY(mOwnScrollY);
+        event.setMaxScrollX(mScrollX);
+        event.setMaxScrollY(getScrollRange());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfoInternal(info);
+        final int scrollRange = getScrollRange();
+        if (scrollRange > 0) {
+            info.setScrollable(true);
+            if (mScrollY > 0) {
+                info.addAction(
+                        AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
+            }
+            if (mScrollY < scrollRange) {
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
+            }
+        }
+    }
+
+    /** @hide */
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+        if (!isEnabled()) {
+            return false;
+        }
+        int direction = -1;
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollDown:
+                direction = 1;
+                // fall through
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollUp:
+                final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
+                        - mBottomStackPeekSize - mBottomStackSlowDownHeight;
+                final int targetScrollY = Math.max(0,
+                        Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
+                if (targetScrollY != mOwnScrollY) {
+                    mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
+                    postInvalidateOnAnimation();
+                    return true;
+                }
+                break;
+        }
+        return false;
+    }
+
     @Override
     public void onGroupsChanged() {
         mPhoneStatusBar.requestNotificationUpdate();
index 1ccc908..d6c5506 100644 (file)
@@ -173,6 +173,9 @@ public class StackScrollState {
         }
         if (view instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            if (state.isBottomClipped) {
+                row.setClipToActualHeight(true);
+            }
             row.applyChildrenState(this);
         }
         return true;
index 6f451ff..0f94227 100644 (file)
@@ -223,7 +223,7 @@ public class StackStateAnimator {
         startViewAnimations(child, viewState, delay, duration);
 
         // start height animation
-        if (heightChanging && child.getActualHeight() != 0) {
+        if (heightChanging) {
             startHeightAnimation(child, viewState, duration, delay);
         }
 
@@ -270,7 +270,8 @@ public class StackStateAnimator {
     public void startViewAnimations(View child, ViewState viewState, long delay, long duration) {
         boolean wasVisible = child.getVisibility() == View.VISIBLE;
         final float alpha = viewState.alpha;
-        if (!wasVisible && alpha != 0 && !viewState.gone && !viewState.hidden) {
+        if (!wasVisible && (alpha != 0 || child.getAlpha() != 0)
+                && !viewState.gone && !viewState.hidden) {
             child.setVisibility(View.VISIBLE);
         }
         boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
@@ -477,11 +478,27 @@ public class StackStateAnimator {
         animator.addListener(getGlobalAnimationFinishedListener());
         // remove the tag when the animation is finished
         animator.addListener(new AnimatorListenerAdapter() {
+            boolean mWasCancelled;
+
             @Override
             public void onAnimationEnd(Animator animation) {
                 child.setTag(TAG_ANIMATOR_HEIGHT, null);
                 child.setTag(TAG_START_HEIGHT, null);
                 child.setTag(TAG_END_HEIGHT, null);
+                if (!mWasCancelled && child instanceof ExpandableNotificationRow) {
+                    ((ExpandableNotificationRow) child).setGroupExpansionChanging(
+                            false /* isExpansionChanging */);
+                }
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mWasCancelled = false;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCancelled = true;
             }
         });
         startAnimator(animator);
index 1114e03..ecdee4e 100644 (file)
@@ -57,6 +57,11 @@ public class StackViewState extends ViewState {
      */
     public int location;
 
+    /**
+     * Whether a child in a group is being clipped at the bottom.
+     */
+    public boolean isBottomClipped;
+
     @Override
     public void copyFrom(ViewState viewState) {
         super.copyFrom(viewState);
@@ -71,6 +76,7 @@ public class StackViewState extends ViewState {
             clipTopAmount = svs.clipTopAmount;
             notGoneIndex = svs.notGoneIndex;
             location = svs.location;
+            isBottomClipped = svs.isBottomClipped;
         }
     }
 }
index 3f95427..de2c58c 100644 (file)
@@ -44,4 +44,19 @@ public class AppWidgetService extends SystemService {
             mImpl.setSafeMode(isSafeMode());
         }
     }
+
+    @Override
+    public void onUnlockUser(int userHandle) {
+        mImpl.onUserUnlocked(userHandle);
+    }
+
+    @Override
+    public void onStopUser(int userHandle) {
+        mImpl.onUserStopped(userHandle);
+    }
+
+    @Override
+    public void onSwitchUser(int userHandle) {
+        mImpl.reloadWidgetsMaskedStateForGroup(userHandle);
+    }
 }
index ac2dda3..4e0ddd6 100644 (file)
@@ -158,12 +158,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
 
             if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                 onConfigurationChanged();
-            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
-                onUserUnlocked(userId);
-            } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
-                onUserStopped(userId);
-            } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                reloadWidgetsMaskedStateForGroup(userId);
             } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
                     || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
                 synchronized (mLock) {
@@ -281,13 +275,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
                 sdFilter, null, null);
 
-        IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
-        userFilter.addAction(Intent.ACTION_USER_STOPPED);
-        userFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                userFilter, null, null);
-
         IntentFilter offModeFilter = new IntentFilter();
         offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
         offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
@@ -454,7 +441,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
      * due to user not being available and package suspension.
      * userId must be the group parent.
      */
-    private void reloadWidgetsMaskedStateForGroup(int userId) {
+    void reloadWidgetsMaskedStateForGroup(int userId) {
         if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
             return;
         }
@@ -2525,7 +2512,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
         }
     }
 
-    private void onUserUnlocked(int userId) {
+    void onUserUnlocked(int userId) {
         if (isProfileWithLockedParent(userId)) {
             return;
         }
@@ -3072,7 +3059,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
         return new AtomicFile(settingsFile);
     }
 
-    private void onUserStopped(int userId) {
+    void onUserStopped(int userId) {
         synchronized (mLock) {
             boolean crossProfileWidgetsChanged = false;
 
index 1edc9b1..8ad6e6a 100644 (file)
@@ -125,6 +125,8 @@ class AlarmManagerService extends SystemService {
     long mNativeData;
     private long mNextWakeup;
     private long mNextNonWakeup;
+    private long mLastWakeupSet;
+    private long mLastWakeup;
     int mBroadcastRefCount = 0;
     PowerManager.WakeLock mWakeLock;
     boolean mLastWakeLockUnimportantForLogging;
@@ -1400,6 +1402,9 @@ class AlarmManagerService extends SystemService {
                     pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
             pw.print("  Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
                     pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
+            pw.print("  Last wakeup: "); TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw);
+            pw.print(" set at "); TimeUtils.formatDuration(mLastWakeupSet, nowELAPSED, pw);
+            pw.println();
             pw.print("  Num time change events: "); pw.println(mNumTimeChanged);
             pw.println("  mDeviceIdleUserWhitelist=" + Arrays.toString(mDeviceIdleUserWhitelist));
 
@@ -1838,6 +1843,7 @@ class AlarmManagerService extends SystemService {
             final Batch firstBatch = mAlarmBatches.get(0);
             if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
                 mNextWakeup = firstWakeup.start;
+                mLastWakeupSet = SystemClock.elapsedRealtime();
                 setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
             }
             if (firstBatch != firstWakeup) {
@@ -2436,6 +2442,7 @@ class AlarmManagerService extends SystemService {
             while (true)
             {
                 int result = waitForAlarm(mNativeData);
+                mLastWakeup = SystemClock.elapsedRealtime();
 
                 triggerList.clear();
 
@@ -2536,6 +2543,11 @@ class AlarmManagerService extends SystemService {
                             deliverAlarmsLocked(triggerList, nowELAPSED);
                         }
                     }
+
+                } else {
+                    // Just in case -- even though no wakeup flag was set, make sure
+                    // we have updated the kernel to the next alarm time.
+                    rescheduleKernelAlarmsLocked();
                 }
             }
         }
index dab7d70..b5b0cd8 100644 (file)
@@ -2180,8 +2180,9 @@ public class AppOpsService extends IAppOpsService.Stub {
         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);
             }
         }
     }
index f30a126..1f88be5 100644 (file)
@@ -1457,14 +1457,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
                                                     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 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
 
         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;
index c096fa5..2f2d8a4 100644 (file)
@@ -2245,6 +2245,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
             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 @@ public class ConnectivityService extends IConnectivityManager.Stub
             }
             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).
@@ -4066,7 +4067,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
             synchronized(mRulesLock) {
                 uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
             }
-            if ((uidRules & RULE_ALLOW_ALL) == 0) {
+            if (mRestrictBackground && (uidRules & RULE_ALLOW_METERED) == 0
+                    && (uidRules & RULE_TEMPORARY_ALLOW_METERED) == 0) {
                 // we could silently fail or we can filter the available nets to only give
                 // them those they have access to.  Chose the more useful option.
                 networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -4583,7 +4585,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
         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 {
@@ -4595,7 +4597,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
         updateTcpBufferSizes(newNetwork);
         setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
-        logDefaultNetworkEvent(newNetwork, prevNetwork);
     }
 
     // Handles a network appearing or improving its score.
@@ -4746,7 +4747,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
         }
         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
index 4477e8b..1d8bb6b 100644 (file)
@@ -43,6 +43,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
 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 @@ public class LockSettingsService extends ILockSettings.Stub {
     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 class LockSettingsService extends ILockSettings.Stub {
 
     public LockSettingsService(Context context) {
         mContext = context;
+        mHandler = new Handler();
         mStrongAuth = new LockSettingsStrongAuth(context);
         // Open the database
 
@@ -367,10 +368,20 @@ public class LockSettingsService extends ILockSettings.Stub {
         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 @@ public class LockSettingsService extends ILockSettings.Stub {
         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 @@ public class LockSettingsService extends ILockSettings.Stub {
             }
         };
 
-        // 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 @@ public class LockSettingsService extends ILockSettings.Stub {
                     // 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 @@ public class LockSettingsService extends ILockSettings.Stub {
             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 @@ public class LockSettingsService extends ILockSettings.Stub {
 
             // 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 @@ public class LockSettingsService extends ILockSettings.Stub {
         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
index 7a59703..a8a8553 100644 (file)
@@ -201,6 +201,7 @@ import android.service.voice.VoiceInteractionManagerInternal;
 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 @@ public final class ActivityManagerService extends ActivityManagerNative
                 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 @@ public final class ActivityManagerService extends ActivityManagerNative
                     || 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 @@ public final class ActivityManagerService extends ActivityManagerNative
                 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++;
index 0acc2a0..8ffc6f3 100644 (file)
@@ -258,6 +258,11 @@ public final class BroadcastQueue {
         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 @@ public final class BroadcastQueue {
         }
 
         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 @@ public final class BroadcastQueue {
         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;
             }
index 059acbd..da18f32 100644 (file)
@@ -192,6 +192,9 @@ final class ProcessRecord {
     // 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();
 
index 595d086..67f96d8 100644 (file)
@@ -938,28 +938,26 @@ final class UserController {
     boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
             IProgressListener listener) {
         synchronized (mService) {
-            // Bail if user isn't actually running, otherwise register the given
-            // listener to watch for unlock progress
-            final UserState uss = mStartedUsers.get(userId);
-            if (uss == null) {
-                notifyFinished(userId, listener);
-                return false;
-            } else {
-                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 {
+                    // 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());
-                    notifyFinished(userId, listener);
-                    return false;
                 }
             }
+            // Bail if user isn't actually running, otherwise register the given
+            // listener to watch for unlock progress
+            final UserState uss = mStartedUsers.get(userId);
+            if (uss == null) {
+                notifyFinished(userId, listener);
+                return false;
+            } else {
+                uss.mUnlockProgress.addListener(listener);
+            }
 
             finishUserUnlocking(uss);
 
index 00d7a7b..7b573da 100644 (file)
@@ -384,7 +384,7 @@ public class NotificationUsageStats {
             noisyImportance = new ImportanceHistogram(context, "note_imp_noisy_");
             quietImportance = new ImportanceHistogram(context, "note_imp_quiet_");
             finalImportance = new ImportanceHistogram(context, "note_importance_");
-            enqueueRate = new RateEstimator(mCreated);
+            enqueueRate = new RateEstimator();
         }
 
         public AggregatedStats getPrevious() {
index 4dc30a4..c17db4a 100644 (file)
@@ -25,30 +25,43 @@ package com.android.server.notification;
 public class RateEstimator {
     private static final double RATE_ALPHA = 0.8;
     private static final double MINIMUM_DT = 0.0005;
-    private long mLastEventTime;
-    private float mInterarrivalTime;
+    private Long mLastEventTime;
+    private Float mInterarrivalTime;
 
-    public RateEstimator(long now) {
-        mLastEventTime = now;
-    }
+    public RateEstimator() {}
 
-    /** Update the estimate to account for an event that jsut happened. */
+    /** Update the estimate to account for an event that just happened. */
     public float update(long now) {
-        mInterarrivalTime = (float) getInterarrivalEstimate(now);
+        float rate;
+        if (mLastEventTime == null) {
+            // No last event time, rate is zero.
+            rate = 0f;
+        } else {
+            // Calculate the new inter-arrival time based on last event time.
+            mInterarrivalTime = (float) getInterarrivalEstimate(now);
+            rate = (float) (1.0 / mInterarrivalTime);
+        }
         mLastEventTime = now;
-        return (float) (1.0 / mInterarrivalTime);
+        return rate;
     }
 
     /** @return the estimated rate if there were a new event right now. */
     public float getRate(long now) {
+        if (mLastEventTime == null) {
+            return 0f;
+        }
         return (float) (1.0 / getInterarrivalEstimate(now));
     }
 
     /** @return the average inter-arrival time if there were a new event right now. */
     private double getInterarrivalEstimate(long now) {
-        // a*iat_old + (1-a)*(t_now-t_last)
         double dt = ((double) (now - mLastEventTime)) / 1000.0;
         dt = Math.max(dt, MINIMUM_DT);
+        if (mInterarrivalTime == null) {
+            // No last inter-arrival time, return the new value directly.
+            return dt;
+        }
+        // a*iat_old + (1-a)*(t_now-t_last)
         return (RATE_ALPHA * mInterarrivalTime + (1.0 - RATE_ALPHA) * dt);
     }
 }
index 2f72607..5fdb1e8 100644 (file)
@@ -5421,7 +5421,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
         // 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);
index aea395d..42eddd5 100644 (file)
@@ -1334,7 +1334,17 @@ class WindowStateAnimator {
             final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
             if (wallpaperTarget != null) {
                 final Task task = wallpaperTarget.getTask();
-                if (task != null && !task.isFullscreen()) {
+                final WindowStateAnimator winAnimator = wallpaperTarget.mWinAnimator;
+                // We can only crop the wallpaper using final crop with stack bounds if the target
+                // is not animating, or if it's animating with clip mode STACK_CLIP_AFTER_ANIM.
+                // If it's animating with mode STACK_CLIP_NONE, we shouldn't crop either the task
+                // itself or the wallpaper. If it's animating with STACK_CLIP_BEFORE_ANIM, the crop
+                // is before the transform on the task itself.
+                final boolean useFinalCropOnWallpaper = !winAnimator.isAnimationSet()
+                        || winAnimator.resolveStackClip() == STACK_CLIP_AFTER_ANIM;
+                if (task != null && !task.isFullscreen()
+                        && task.cropWindowsToStackBounds()
+                        && useFinalCropOnWallpaper){
                     final TaskStack stack = task.mStack;
                     if (stack != null && !stack.isFullscreen()) {
                         stack.getDimBounds(mTmpStackBounds);
@@ -1459,7 +1469,7 @@ class WindowStateAnimator {
             // We need to ensure for each surface, that we disable transformation matrix
             // scaling in the same transaction which we resize the surface in.
             // As we are in SCALING_MODE_SCALE_TO_WINDOW, SurfaceFlinger will
-            // then take over the scaling until the new buffer arrives, and things 
+            // then take over the scaling until the new buffer arrives, and things
             // will be seamless.
             mForceScaleUntilResize = true;
         } else {
index e29452a..e8d4c58 100644 (file)
@@ -72,15 +72,18 @@ static const GnssConfigurationInterface* sGnssConfigurationInterface = NULL;
 #define GPS_MAX_SATELLITE_COUNT 32
 #define GNSS_MAX_SATELLITE_COUNT 64
 
-// Let these through, with ID remapped by offset
+// Let these through, with ID remapped down to 1, 2... by offset
 #define GLONASS_SVID_OFFSET 64
 #define GLONASS_SVID_COUNT 24
 #define BEIDOU_SVID_OFFSET 200
 #define BEIDOU_SVID_COUNT 35
 
+// Let these through, with ID remapped up (33->120 ... 64->151, etc.)
+#define SBAS_SVID_MIN 33
+#define SBAS_SVID_MAX 64
+#define SBAS_SVID_ADD 87
+
 // Let these through, with no ID remapping
-#define SBAS_SVID_MIN 120
-#define SBAS_SVID_MAX 151
 #define QZSS_SVID_MIN 193
 #define QZSS_SVID_MAX 200
 
@@ -159,6 +162,7 @@ static void sv_status_callback(GpsSvStatus* sv_status)
             info.svid -= BEIDOU_SVID_OFFSET;
         } else if (info.svid >= SBAS_SVID_MIN && info.svid <= SBAS_SVID_MAX) {
             info.constellation = GNSS_CONSTELLATION_SBAS;
+            info.svid += SBAS_SVID_ADD;
         } else if (info.svid >= QZSS_SVID_MIN && info.svid <= QZSS_SVID_MAX) {
             info.constellation = GNSS_CONSTELLATION_QZSS;
         } else {
index c1913de..1af9ccb 100644 (file)
@@ -3810,14 +3810,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             // 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                     }
                 }
             }
+            // 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;
index e306d89..eed4351 100644 (file)
@@ -176,7 +176,7 @@ public final class SystemServer {
      * 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;
index cc0920f..b5698d5 100644 (file)
@@ -26,18 +26,18 @@ public class RateEstimatorTest extends AndroidTestCase {
     @Override
     public void setUp() {
         mTestStartTime = 1225731600000L;
-        mEstimator = new RateEstimator(mTestStartTime);
+        mEstimator = new RateEstimator();
     }
 
     @SmallTest
     public void testRunningTimeBackwardDoesntExplodeUpdate() throws Exception {
-        final float rate = mEstimator.update(mTestStartTime - 1000L);
-        assertFalse(Float.isInfinite(rate));
-        assertFalse(Float.isNaN(rate));
+        assertUpdateTime(mTestStartTime);
+        assertUpdateTime(mTestStartTime - 1000L);
     }
 
     @SmallTest
     public void testRunningTimeBackwardDoesntExplodeGet() throws Exception {
+        assertUpdateTime(mTestStartTime);
         final float rate = mEstimator.getRate(mTestStartTime - 1000L);
         assertFalse(Float.isInfinite(rate));
         assertFalse(Float.isNaN(rate));
@@ -45,13 +45,14 @@ public class RateEstimatorTest extends AndroidTestCase {
 
     @SmallTest
     public void testInstantaneousEventsDontExplodeUpdate() throws Exception {
-        final float rate = mEstimator.update(mTestStartTime);
-        assertFalse(Float.isInfinite(rate));
-        assertFalse(Float.isNaN(rate));
+        assertUpdateTime(mTestStartTime);
+        assertUpdateTime(mTestStartTime);
     }
 
     @SmallTest
     public void testInstantaneousEventsDontExplodeGet() throws Exception {
+        assertUpdateTime(mTestStartTime);
+        assertUpdateTime(mTestStartTime);
         final float rate = mEstimator.getRate(mTestStartTime);
         assertFalse(Float.isInfinite(rate));
         assertFalse(Float.isNaN(rate));
@@ -59,6 +60,7 @@ public class RateEstimatorTest extends AndroidTestCase {
 
     @SmallTest
     public void testCompactBurstIsEstimatedUnderTwoPercent() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 1, 5); // five events at 1000Hz
         final float rate = mEstimator.getRate(nextEventTime);
@@ -67,6 +69,7 @@ public class RateEstimatorTest extends AndroidTestCase {
 
     @SmallTest
     public void testSustained1000HzBurstIsEstimatedOverNinetyPercent() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 1, 100); // one hundred events at 1000Hz
         final float rate = mEstimator.getRate(nextEventTime);
@@ -75,6 +78,7 @@ public class RateEstimatorTest extends AndroidTestCase {
 
     @SmallTest
     public void testSustained100HzBurstIsEstimatedOverNinetyPercent() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 10, 100); // one hundred events at 100Hz
         final float rate = mEstimator.getRate(nextEventTime);
@@ -84,6 +88,7 @@ public class RateEstimatorTest extends AndroidTestCase {
 
     @SmallTest
     public void testRecoverQuicklyAfterSustainedBurst() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 10, 1000); // one hundred events at 100Hz
         final float rate = mEstimator.getRate(nextEventTime + 5000L); // two seconds later
@@ -92,12 +97,19 @@ public class RateEstimatorTest extends AndroidTestCase {
 
     @SmallTest
     public void testEstimateShouldNotOvershoot() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 1, 1000); // one thousand events at 1000Hz
         final float rate = mEstimator.getRate(nextEventTime);
         assertLessThan("Rate", rate, 1000f);
     }
 
+    @SmallTest
+    public void testGetRateWithoutUpdate() throws Exception {
+        final float rate = mEstimator.getRate(mTestStartTime);
+        assertLessThan("Rate", rate, 0.1f);
+    }
+
     private void assertLessThan(String label, float a, float b)  {
         assertTrue(String.format("%s was %f, but should be less than %f", label, a, b), a < b);
     }
@@ -115,4 +127,10 @@ public class RateEstimatorTest extends AndroidTestCase {
         }
         return time;
     }
+
+    private void assertUpdateTime(long time) {
+        final float rate = mEstimator.update(time);
+        assertFalse(Float.isInfinite(rate));
+        assertFalse(Float.isNaN(rate));
+    }
 }
index 57d68fd..51f5899 100644 (file)
@@ -1101,7 +1101,7 @@ public class VoiceInteractionManagerService extends SystemService {
                 if (hit && doit) {
                     // The user is force stopping our current interactor/recognizer.
                     // Clear the current settings and restore default state.
-                    synchronized (VoiceInteractionManagerService.this) {
+                    synchronized (VoiceInteractionManagerServiceStub.this) {
                         unloadAllKeyphraseModels();
                         if (mImpl != null) {
                             mImpl.shutdownLocked();
@@ -1126,7 +1126,7 @@ public class VoiceInteractionManagerService extends SystemService {
                 int userHandle = getChangingUserId();
                 if (DEBUG) Slog.d(TAG, "onSomePackagesChanged user=" + userHandle);
 
-                synchronized (VoiceInteractionManagerService.this) {
+                synchronized (VoiceInteractionManagerServiceStub.this) {
                     ComponentName curInteractor = getCurInteractor(userHandle);
                     ComponentName curRecognizer = getCurRecognizer(userHandle);
                     if (curRecognizer == null) {
index cbd0415..1afd90d 100644 (file)
@@ -23,6 +23,7 @@ import com.android.ide.common.rendering.api.SessionParams;
 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 @@ class Layout extends RelativeLayout {
 
     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);