Merge "Fix issue #28931042: wtf in system server" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 56390c2..70ea1c5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5048,7 +5048,7 @@
public static class Notification.BigPictureStyle extends android.app.Notification.Style {
ctor public Notification.BigPictureStyle();
- ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.BigPictureStyle(android.app.Notification.Builder);
method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon);
method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap);
@@ -5058,7 +5058,7 @@
public static class Notification.BigTextStyle extends android.app.Notification.Style {
ctor public Notification.BigTextStyle();
- ctor public Notification.BigTextStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.BigTextStyle(android.app.Notification.Builder);
method public android.app.Notification.BigTextStyle bigText(java.lang.CharSequence);
method public android.app.Notification.BigTextStyle setBigContentTitle(java.lang.CharSequence);
method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
@@ -5159,12 +5159,10 @@
public static class Notification.DecoratedCustomViewStyle extends android.app.Notification.Style {
ctor public Notification.DecoratedCustomViewStyle();
- ctor public Notification.DecoratedCustomViewStyle(android.app.Notification.Builder);
}
public static class Notification.DecoratedMediaCustomViewStyle extends android.app.Notification.MediaStyle {
ctor public Notification.DecoratedMediaCustomViewStyle();
- ctor public Notification.DecoratedMediaCustomViewStyle(android.app.Notification.Builder);
}
public static abstract interface Notification.Extender {
@@ -5173,7 +5171,7 @@
public static class Notification.InboxStyle extends android.app.Notification.Style {
ctor public Notification.InboxStyle();
- ctor public Notification.InboxStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.InboxStyle(android.app.Notification.Builder);
method public android.app.Notification.InboxStyle addLine(java.lang.CharSequence);
method public android.app.Notification.InboxStyle setBigContentTitle(java.lang.CharSequence);
method public android.app.Notification.InboxStyle setSummaryText(java.lang.CharSequence);
@@ -5181,7 +5179,7 @@
public static class Notification.MediaStyle extends android.app.Notification.Style {
ctor public Notification.MediaStyle();
- ctor public Notification.MediaStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.MediaStyle(android.app.Notification.Builder);
method public android.app.Notification.MediaStyle setMediaSession(android.media.session.MediaSession.Token);
method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
}
@@ -19585,7 +19583,7 @@
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
- method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
+ method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -19731,7 +19729,7 @@
public static abstract class AudioManager.AudioRecordingCallback {
ctor public AudioManager.AudioRecordingCallback();
- method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
+ method public void onRecordingConfigChanged(java.util.List<android.media.AudioRecordingConfiguration>);
}
public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19778,6 +19776,7 @@
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
@@ -19900,6 +19899,7 @@
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
@@ -20557,6 +20557,7 @@
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
@@ -20570,9 +20571,11 @@
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
@@ -34633,8 +34636,8 @@
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
- method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
- method public final void requestUnbind() throws android.os.RemoteException;
+ method public static void requestRebind(android.content.ComponentName);
+ method public final void requestUnbind();
method public final void setNotificationsShown(java.lang.String[]);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index 27188f0..a6f612d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5195,7 +5195,7 @@
public static class Notification.BigPictureStyle extends android.app.Notification.Style {
ctor public Notification.BigPictureStyle();
- ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.BigPictureStyle(android.app.Notification.Builder);
method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon);
method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap);
@@ -5205,7 +5205,7 @@
public static class Notification.BigTextStyle extends android.app.Notification.Style {
ctor public Notification.BigTextStyle();
- ctor public Notification.BigTextStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.BigTextStyle(android.app.Notification.Builder);
method public android.app.Notification.BigTextStyle bigText(java.lang.CharSequence);
method public android.app.Notification.BigTextStyle setBigContentTitle(java.lang.CharSequence);
method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
@@ -5306,12 +5306,10 @@
public static class Notification.DecoratedCustomViewStyle extends android.app.Notification.Style {
ctor public Notification.DecoratedCustomViewStyle();
- ctor public Notification.DecoratedCustomViewStyle(android.app.Notification.Builder);
}
public static class Notification.DecoratedMediaCustomViewStyle extends android.app.Notification.MediaStyle {
ctor public Notification.DecoratedMediaCustomViewStyle();
- ctor public Notification.DecoratedMediaCustomViewStyle(android.app.Notification.Builder);
}
public static abstract interface Notification.Extender {
@@ -5320,7 +5318,7 @@
public static class Notification.InboxStyle extends android.app.Notification.Style {
ctor public Notification.InboxStyle();
- ctor public Notification.InboxStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.InboxStyle(android.app.Notification.Builder);
method public android.app.Notification.InboxStyle addLine(java.lang.CharSequence);
method public android.app.Notification.InboxStyle setBigContentTitle(java.lang.CharSequence);
method public android.app.Notification.InboxStyle setSummaryText(java.lang.CharSequence);
@@ -5328,7 +5326,7 @@
public static class Notification.MediaStyle extends android.app.Notification.Style {
ctor public Notification.MediaStyle();
- ctor public Notification.MediaStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.MediaStyle(android.app.Notification.Builder);
method public android.app.Notification.MediaStyle setMediaSession(android.media.session.MediaSession.Token);
method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
}
@@ -21092,7 +21090,7 @@
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
- method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
+ method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -21246,7 +21244,7 @@
public static abstract class AudioManager.AudioRecordingCallback {
ctor public AudioManager.AudioRecordingCallback();
- method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
+ method public void onRecordingConfigChanged(java.util.List<android.media.AudioRecordingConfiguration>);
}
public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -21294,6 +21292,7 @@
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
@@ -21418,6 +21417,7 @@
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
@@ -22075,6 +22075,7 @@
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
@@ -22088,9 +22089,11 @@
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
@@ -37359,8 +37362,8 @@
method public void registerAsSystemService(android.content.Context, android.content.ComponentName, int) throws android.os.RemoteException;
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
- method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
- method public final void requestUnbind() throws android.os.RemoteException;
+ method public static void requestRebind(android.content.ComponentName);
+ method public final void requestUnbind();
method public final void setNotificationsShown(java.lang.String[]);
method public final void setOnNotificationPostedTrim(int);
method public void unregisterAsSystemService() throws android.os.RemoteException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 15a2d30..37bc7af 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5049,7 +5049,7 @@
public static class Notification.BigPictureStyle extends android.app.Notification.Style {
ctor public Notification.BigPictureStyle();
- ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.BigPictureStyle(android.app.Notification.Builder);
method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon);
method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap);
@@ -5059,7 +5059,7 @@
public static class Notification.BigTextStyle extends android.app.Notification.Style {
ctor public Notification.BigTextStyle();
- ctor public Notification.BigTextStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.BigTextStyle(android.app.Notification.Builder);
method public android.app.Notification.BigTextStyle bigText(java.lang.CharSequence);
method public android.app.Notification.BigTextStyle setBigContentTitle(java.lang.CharSequence);
method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
@@ -5160,12 +5160,10 @@
public static class Notification.DecoratedCustomViewStyle extends android.app.Notification.Style {
ctor public Notification.DecoratedCustomViewStyle();
- ctor public Notification.DecoratedCustomViewStyle(android.app.Notification.Builder);
}
public static class Notification.DecoratedMediaCustomViewStyle extends android.app.Notification.MediaStyle {
ctor public Notification.DecoratedMediaCustomViewStyle();
- ctor public Notification.DecoratedMediaCustomViewStyle(android.app.Notification.Builder);
}
public static abstract interface Notification.Extender {
@@ -5174,7 +5172,7 @@
public static class Notification.InboxStyle extends android.app.Notification.Style {
ctor public Notification.InboxStyle();
- ctor public Notification.InboxStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.InboxStyle(android.app.Notification.Builder);
method public android.app.Notification.InboxStyle addLine(java.lang.CharSequence);
method public android.app.Notification.InboxStyle setBigContentTitle(java.lang.CharSequence);
method public android.app.Notification.InboxStyle setSummaryText(java.lang.CharSequence);
@@ -5182,7 +5180,7 @@
public static class Notification.MediaStyle extends android.app.Notification.Style {
ctor public Notification.MediaStyle();
- ctor public Notification.MediaStyle(android.app.Notification.Builder);
+ ctor public deprecated Notification.MediaStyle(android.app.Notification.Builder);
method public android.app.Notification.MediaStyle setMediaSession(android.media.session.MediaSession.Token);
method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
}
@@ -19654,7 +19652,7 @@
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
- method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
+ method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -19800,7 +19798,7 @@
public static abstract class AudioManager.AudioRecordingCallback {
ctor public AudioManager.AudioRecordingCallback();
- method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
+ method public void onRecordingConfigChanged(java.util.List<android.media.AudioRecordingConfiguration>);
}
public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19847,6 +19845,7 @@
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
@@ -19969,6 +19968,7 @@
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
@@ -20626,6 +20626,7 @@
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
@@ -20639,9 +20640,11 @@
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
@@ -34710,8 +34713,8 @@
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
- method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
- method public final void requestUnbind() throws android.os.RemoteException;
+ method public static void requestRebind(android.content.ComponentName);
+ method public final void requestUnbind();
method public final void setNotificationsShown(java.lang.String[]);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
diff --git a/cmds/uiautomator/api/current.txt b/cmds/uiautomator/api/current.txt
index 7eeecf5..634ca4d 100644
--- a/cmds/uiautomator/api/current.txt
+++ b/cmds/uiautomator/api/current.txt
@@ -1,6 +1,6 @@
package com.android.uiautomator.core {
- public final class Configurator {
+ public final deprecated class Configurator {
method public long getActionAcknowledgmentTimeout();
method public static com.android.uiautomator.core.Configurator getInstance();
method public long getKeyInjectionDelay();
@@ -14,7 +14,7 @@
method public com.android.uiautomator.core.Configurator setWaitForSelectorTimeout(long);
}
- public class UiCollection extends com.android.uiautomator.core.UiObject {
+ public deprecated class UiCollection extends com.android.uiautomator.core.UiObject {
ctor public UiCollection(com.android.uiautomator.core.UiSelector);
method public com.android.uiautomator.core.UiObject getChildByDescription(com.android.uiautomator.core.UiSelector, java.lang.String) throws com.android.uiautomator.core.UiObjectNotFoundException;
method public com.android.uiautomator.core.UiObject getChildByInstance(com.android.uiautomator.core.UiSelector, int) throws com.android.uiautomator.core.UiObjectNotFoundException;
@@ -22,7 +22,7 @@
method public int getChildCount(com.android.uiautomator.core.UiSelector);
}
- public class UiDevice {
+ public deprecated class UiDevice {
method public void clearLastTraversedText();
method public boolean click(int, int);
method public boolean drag(int, int, int, int, int);
@@ -77,7 +77,7 @@
method public void wakeUp() throws android.os.RemoteException;
}
- public class UiObject {
+ public deprecated class UiObject {
ctor public UiObject(com.android.uiautomator.core.UiSelector);
method public void clearTextField() throws com.android.uiautomator.core.UiObjectNotFoundException;
method public boolean click() throws com.android.uiautomator.core.UiObjectNotFoundException;
@@ -130,13 +130,13 @@
field protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500L; // 0x157cL
}
- public class UiObjectNotFoundException extends java.lang.Exception {
+ public deprecated class UiObjectNotFoundException extends java.lang.Exception {
ctor public UiObjectNotFoundException(java.lang.String);
ctor public UiObjectNotFoundException(java.lang.String, java.lang.Throwable);
ctor public UiObjectNotFoundException(java.lang.Throwable);
}
- public class UiScrollable extends com.android.uiautomator.core.UiCollection {
+ public deprecated class UiScrollable extends com.android.uiautomator.core.UiCollection {
ctor public UiScrollable(com.android.uiautomator.core.UiSelector);
method protected boolean exists(com.android.uiautomator.core.UiSelector);
method public boolean flingBackward() throws com.android.uiautomator.core.UiObjectNotFoundException;
@@ -165,7 +165,7 @@
method public com.android.uiautomator.core.UiScrollable setSwipeDeadZonePercentage(double);
}
- public class UiSelector {
+ public deprecated class UiSelector {
ctor public UiSelector();
method public com.android.uiautomator.core.UiSelector checkable(boolean);
method public com.android.uiautomator.core.UiSelector checked(boolean);
@@ -198,7 +198,7 @@
method public com.android.uiautomator.core.UiSelector textStartsWith(java.lang.String);
}
- public abstract interface UiWatcher {
+ public abstract deprecated interface UiWatcher {
method public abstract boolean checkForCondition();
}
@@ -206,11 +206,11 @@
package com.android.uiautomator.testrunner {
- public abstract interface IAutomationSupport {
+ public abstract deprecated interface IAutomationSupport {
method public abstract void sendStatus(int, android.os.Bundle);
}
- public class UiAutomatorTestCase extends junit.framework.TestCase {
+ public deprecated class UiAutomatorTestCase extends junit.framework.TestCase {
ctor public UiAutomatorTestCase();
method public com.android.uiautomator.testrunner.IAutomationSupport getAutomationSupport();
method public android.os.Bundle getParams();
diff --git a/cmds/uiautomator/cmds/uiautomator/uiautomator b/cmds/uiautomator/cmds/uiautomator/uiautomator
index fe2c735..86a1dba 100755
--- a/cmds/uiautomator/cmds/uiautomator/uiautomator
+++ b/cmds/uiautomator/cmds/uiautomator/uiautomator
@@ -67,6 +67,11 @@
# special case pre-processing for 'runtest' command
if [ "${cmd}" == "runtest" ]; then
+ # Print deprecation warning
+ echo "Warning: This version of UI Automator is deprecated. New tests should be written using"
+ echo "UI Automator 2.0 which is available as part of the Android Testing Support Library."
+ echo "See https://developer.android.com/training/testing/ui-testing/uiautomator-testing.html"
+ echo "for more details."
# first parse the jar paths
while [ true ]; do
if [ -z "${1}" ] && [ -z "${jars}" ]; then
diff --git a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
index f0c60d2..6d75844 100644
--- a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
+++ b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
@@ -22,7 +22,10 @@
* Provides auxiliary support for running test cases
*
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public interface IAutomationSupport {
/**
diff --git a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
index ae763f2..af42653 100644
--- a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
+++ b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
@@ -28,7 +28,10 @@
/**
* Test runner for {@link UiAutomatorTestCase}s. Such tests are executed
* on the device and have access to an applications context.
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiAutomatorInstrumentationTestRunner extends InstrumentationTestRunner {
@Override
diff --git a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index b5f21c9..7c9aede 100644
--- a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -26,7 +26,10 @@
/**
* UI Automator test case that is executed on the device.
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiAutomatorTestCase extends InstrumentationTestCase {
private Bundle mParams;
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index 62fb0e4..ea77fbc 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -67,7 +67,6 @@
# Make sure to run droiddoc first to generate the stub source files.
$(full_classes_compiled_jar) : $(uiautomator_stubs_stamp)
$(built_dex_intermediate) : $(uiautomator_stubs_stamp)
-uiautomator_stubs_jar := $(full_classes_compiled_jar)
###############################################
# API check
@@ -92,7 +91,7 @@
$(uiautomator_internal_removed_api_file), \
$(checkapi_last_error_level_flags), \
cat $(LOCAL_PATH)/apicheck_msg_last.txt, \
- $(uiautomator_stubs_jar), \
+ uiautomator.core, \
$(uiautomator_stubs_stamp)))
checkapi_current_error_level_flags := \
@@ -111,7 +110,7 @@
$(uiautomator_internal_removed_api_file), \
$(checkapi_current_error_level_flags), \
cat $(LOCAL_PATH)/apicheck_msg_current.txt, \
- $(uiautomator_stubs_jar), \
+ uiautomator.core, \
$(uiautomator_stubs_stamp)))
.PHONY: update-uiautomator-api
@@ -128,7 +127,6 @@
uiautomator.core_java_libraries :=
uiautomator_stubs_stamp :=
uiautomator_internal_api_file :=
-uiautomator_stubs_jar :=
uiautomator_api_dir :=
checkapi_last_error_level_flags :=
checkapi_current_error_level_flags :=
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
index 249f404..255c49a 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
@@ -25,7 +25,10 @@
* tests with the modified parameters, make sure to also restore
* the original parameter values, otherwise this will impact other tests cases.
* @since API Level 18
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public final class Configurator {
private long mWaitForIdleTimeout = 10 * 1000;
private long mWaitForSelector = 10 * 1000;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
index e15beb2..06bacb0 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
@@ -19,7 +19,10 @@
* Used to enumerate a container's UI elements for the purpose of counting,
* or targeting a sub elements by a child's text or description.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiCollection extends UiObject {
/**
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
index a930eb4..b1b432b 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
@@ -42,7 +42,10 @@
* You can also use this class to simulate user actions on the device,
* such as pressing the d-pad or pressing the Home and Menu buttons.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiDevice {
private static final String LOG_TAG = UiDevice.class.getSimpleName();
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
index 2118da8..751bbe8 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
@@ -31,7 +31,10 @@
* its constructor. Once you create an instance of a UiObject, it can
* be reused for different views that match the selector criteria.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiObject {
private static final String LOG_TAG = UiObject.class.getSimpleName();
/**
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
index fc0891b..f3e2698 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
@@ -20,7 +20,10 @@
* Generated in test runs when a {@link UiSelector} selector could not be matched
* to any UI element displayed.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiObjectNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
index a8d20c3..6673dac 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
@@ -24,7 +24,10 @@
* for items in scrollable layout elements. This class can be used with
* horizontally or vertically scrollable controls.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiScrollable extends UiCollection {
private static final String LOG_TAG = UiScrollable.class.getSimpleName();
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
index 482a74d..9ba425e 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
@@ -27,7 +27,10 @@
* information. You can also target an element by its location in a layout
* hierarchy.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiSelector {
static final int SELECTOR_NIL = 0;
static final int SELECTOR_TEXT = 1;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
index 5403e30..030fb0a 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
@@ -22,7 +22,10 @@
* to locate its selector yet. Only during this time, the watchers are invoked to check if there is
* something else unexpected on the screen.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public interface UiWatcher {
/**
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
index f0c60d2..6d75844 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
@@ -22,7 +22,10 @@
* Provides auxiliary support for running test cases
*
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public interface IAutomationSupport {
/**
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index e7d961b..3d5476d 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -36,7 +36,10 @@
* {@link UiDevice} instance
* {@link Bundle} for command line parameters.
* @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
*/
+@Deprecated
public class UiAutomatorTestCase extends TestCase {
private static final String DISABLE_IME = "disable_ime";
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6ba6b0f..cd9a05b 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -7015,6 +7015,7 @@
data.writeInt(0);
}
mRemote.transact(SEND_INTENT_SENDER_TRANSACTION, data, reply, 0);
+ reply.readException();
final int res = reply.readInt();
data.recycle();
reply.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dff0769..30753c1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -62,6 +62,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
@@ -130,6 +131,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
@@ -4684,6 +4686,8 @@
+ config);
mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
+ updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+ mResourcesManager.getConfiguration().getLocales());
if (mConfiguration == null) {
mConfiguration = new Configuration();
@@ -4989,6 +4993,24 @@
return insInfo.nativeLibraryDir;
}
+ /**
+ * The LocaleList set for the app's resources may have been shuffled so that the preferred
+ * Locale is at position 0. We must find the index of this preferred Locale in the
+ * original LocaleList.
+ */
+ private void updateLocaleListFromAppContext(Context context, LocaleList newLocaleList) {
+ final Locale bestLocale = context.getResources().getConfiguration().getLocales().get(0);
+ final int newLocaleListSize = newLocaleList.size();
+ for (int i = 0; i < newLocaleListSize; i++) {
+ if (bestLocale.equals(newLocaleList.get(i))) {
+ LocaleList.setDefault(newLocaleList, i);
+ return;
+ }
+ }
+ throw new AssertionError("chosen locale " + bestLocale + " must be present in LocaleList: "
+ + newLocaleList.toLanguageTags());
+ }
+
private void handleBindApplication(AppBindData data) {
// Register the UI Thread as a sensitive thread to the runtime.
VMRuntime.registerSensitiveThread();
@@ -5047,6 +5069,24 @@
*/
TimeZone.setDefault(null);
+ /*
+ * Set the LocaleList. This may change once we create the App Context.
+ */
+ LocaleList.setDefault(data.config.getLocales());
+
+ synchronized (mResourcesManager) {
+ /*
+ * Update the system configuration since its preloaded and might not
+ * reflect configuration changes. The configuration object passed
+ * in AppBindData can be safely assumed to be up to date
+ */
+ mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+ mCurDefaultDisplayDpi = data.config.densityDpi;
+
+ // This calls mResourcesManager so keep it within the synchronized block.
+ applyCompatConfiguration(mCurDefaultDisplayDpi);
+ }
+
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
/**
@@ -5174,25 +5214,8 @@
}
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
- synchronized (mResourcesManager) {
- /*
- * Initialize the default locales in this process for the reasons we set the time zone.
- *
- * We do this through ResourcesManager, since we need to do locale negotiation.
- */
- mResourcesManager.setDefaultLocalesLocked(data.config.getLocales());
-
- /*
- * Update the system configuration since its preloaded and might not
- * reflect configuration changes. The configuration object passed
- * in AppBindData can be safely assumed to be up to date
- */
- mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
- mCurDefaultDisplayDpi = data.config.densityDpi;
-
- // This calls mResourcesManager so keep it within the synchronized block.
- applyCompatConfiguration(mCurDefaultDisplayDpi);
- }
+ updateLocaleListFromAppContext(appContext,
+ mResourcesManager.getConfiguration().getLocales());
if (!Process.isIsolated() && !"android".equals(appContext.getPackageName())) {
// This cache location probably points at credential-encrypted
@@ -5895,6 +5918,9 @@
// immediately, because upon returning the view
// hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
+ updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+ mResourcesManager.getConfiguration().getLocales());
+
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bd94a17..0cc44e7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -58,6 +58,7 @@
import android.view.Gravity;
import android.view.NotificationHeaderView;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
@@ -3106,6 +3107,11 @@
}
private Drawable getProfileBadgeDrawable() {
+ if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
+ // This user can never be a badged profile,
+ // and also includes USER_ALL system notifications.
+ return null;
+ }
// Note: This assumes that the current user can read the profile badge of the
// originating user.
return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
@@ -3197,11 +3203,14 @@
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;
@@ -4082,6 +4091,10 @@
public BigPictureStyle() {
}
+ /**
+ * @deprecated use {@code BigPictureStyle()}.
+ */
+ @Deprecated
public BigPictureStyle(Builder builder) {
setBuilder(builder);
}
@@ -4238,6 +4251,10 @@
public BigTextStyle() {
}
+ /**
+ * @deprecated use {@code BigTextStyle()}.
+ */
+ @Deprecated
public BigTextStyle(Builder builder) {
setBuilder(builder);
}
@@ -4780,6 +4797,10 @@
public InboxStyle() {
}
+ /**
+ * @deprecated use {@code InboxStyle()}.
+ */
+ @Deprecated
public InboxStyle(Builder builder) {
setBuilder(builder);
}
@@ -4952,6 +4973,10 @@
public MediaStyle() {
}
+ /**
+ * @deprecated use {@code MediaStyle()}.
+ */
+ @Deprecated
public MediaStyle(Builder builder) {
setBuilder(builder);
}
@@ -5159,10 +5184,6 @@
public DecoratedCustomViewStyle() {
}
- public DecoratedCustomViewStyle(Builder builder) {
- setBuilder(builder);
- }
-
/**
* @hide
*/
@@ -5279,10 +5300,6 @@
public DecoratedMediaCustomViewStyle() {
}
- public DecoratedMediaCustomViewStyle(Builder builder) {
- setBuilder(builder);
- }
-
/**
* @hide
*/
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index b4e9db8..25a8b66 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -67,10 +67,6 @@
}
};
- private String[] mSystemLocales = null;
- private final HashSet<String> mNonSystemLocales = new HashSet<>();
- private boolean mHasNonSystemLocales = false;
-
/**
* The global compatibility settings.
*/
@@ -479,12 +475,7 @@
*/
private Resources getOrCreateResources(@Nullable IBinder activityToken,
@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
- final boolean findSystemLocales;
- final boolean hasNonSystemLocales;
synchronized (this) {
- findSystemLocales = (mSystemLocales == null || mSystemLocales.length == 0);
- hasNonSystemLocales = mHasNonSystemLocales;
-
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
@@ -538,24 +529,7 @@
// If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
ResourcesImpl resourcesImpl = createResourcesImpl(key);
- final String[] systemLocales = findSystemLocales
- ? AssetManager.getSystem().getLocales() : null;
- final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
-
- // Avoid checking for non-pseudo-locales if we already know there were some from a previous
- // Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
- // since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
- // able to affect mHasNonSystemLocales.
- final boolean isPseudoLocalesOnly = hasNonSystemLocales ||
- LocaleList.isPseudoLocalesOnly(nonSystemLocales);
-
synchronized (this) {
- if (mSystemLocales == null || mSystemLocales.length == 0) {
- mSystemLocales = systemLocales;
- }
- mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
- mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
-
ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
if (existingResourcesImpl != null) {
if (DEBUG) {
@@ -745,23 +719,6 @@
}
}
- /* package */ void setDefaultLocalesLocked(@NonNull LocaleList locales) {
- if (mSystemLocales == null) {
- throw new RuntimeException("ResourcesManager is not ready to negotiate locales.");
- }
- final int bestLocale;
- if (mHasNonSystemLocales) {
- bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mNonSystemLocales);
- } else {
- // We fallback to system locales if there was no locale specifically supported by the
- // assets. This is to properly support apps that only rely on the shared system assets
- // and don't need assets of their own.
- bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mSystemLocales);
- }
- // set it for Java, this also affects newly created Resources
- LocaleList.setDefault(locales, bestLocale);
- }
-
public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
try {
@@ -786,30 +743,7 @@
| ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
}
- Configuration localeAdjustedConfig = config;
- final LocaleList configLocales = config.getLocales();
- if (!configLocales.isEmpty()) {
- setDefaultLocalesLocked(configLocales);
- final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
- if (adjustedLocales
- != configLocales) { // has the same result as .equals() in this case
- // The first locale in the list was not chosen. So we create a modified
- // configuration with the adjusted locales (which moves the chosen locale to the
- // front).
- localeAdjustedConfig = new Configuration();
- localeAdjustedConfig.setTo(config);
- localeAdjustedConfig.setLocales(adjustedLocales);
- // Also adjust the locale list in mResConfiguration, so that the Resources
- // created later would have the same locale list.
- if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
- mResConfiguration.setLocales(adjustedLocales);
- changes |= ActivityInfo.CONFIG_LOCALE;
- }
- }
- }
-
- Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics,
- compat);
+ Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
ApplicationPackageManager.configurationChanged();
//Slog.i(TAG, "Configuration changed in " + currentPackageName());
@@ -821,7 +755,7 @@
ResourcesImpl r = mResourceImpls.valueAt(i).get();
if (r != null) {
if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
- + r + " config to: " + localeAdjustedConfig);
+ + r + " config to: " + config);
int displayId = key.mDisplayId;
boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
DisplayMetrics dm = defaultDisplayMetrics;
@@ -830,7 +764,7 @@
if (tmpConfig == null) {
tmpConfig = new Configuration();
}
- tmpConfig.setTo(localeAdjustedConfig);
+ tmpConfig.setTo(config);
if (!isDefaultDisplay) {
dm = getDisplayMetrics(displayId);
applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
@@ -840,7 +774,7 @@
}
r.updateConfiguration(tmpConfig, dm, compat);
} else {
- r.updateConfiguration(localeAdjustedConfig, dm, compat);
+ r.updateConfiguration(config, dm, compat);
}
//Slog.i(TAG, "Updated app resources " + v.getKey()
// + " " + r + ": " + r.getConfiguration());
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index cd8f126..bb5f7a1 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -19,6 +19,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
@@ -150,7 +151,7 @@
// We add padding to the AppWidgetHostView if necessary
Rect padding = getDefaultPaddingForWidget(mContext, info.provider, null);
setPadding(padding.left, padding.top, padding.right, padding.bottom);
- setContentDescription(info.label);
+ updateContentDescription(info);
}
}
@@ -459,6 +460,7 @@
}
applyContent(content, recycled, exception);
+ updateContentDescription(mInfo);
}
private void applyContent(View content, boolean recycled, Exception exception) {
@@ -493,6 +495,22 @@
}
}
+ private void updateContentDescription(AppWidgetProviderInfo info) {
+ if (info != null) {
+ LauncherApps launcherApps = getContext().getSystemService(LauncherApps.class);
+ ApplicationInfo appInfo = launcherApps.getApplicationInfo(
+ info.provider.getPackageName(), 0, info.getProfile());
+ if (appInfo != null &&
+ (appInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0) {
+ setContentDescription(
+ Resources.getSystem().getString(
+ com.android.internal.R.string.suspended_widget_accessibility, info.label));
+ } else {
+ setContentDescription(info.label);
+ }
+ }
+ }
+
private void inflateAsync(RemoteViews remoteViews) {
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d036d96..4c86578 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -57,6 +57,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
@@ -483,6 +484,8 @@
private final IBluetoothManager mManagerService;
private IBluetooth mService;
+ private final ReentrantReadWriteLock mServiceLock =
+ new ReentrantReadWriteLock();
private final Object mLock = new Object();
private final Map<LeScanCallback, ScanCallback> mLeScanClients;
@@ -517,8 +520,13 @@
throw new IllegalArgumentException("bluetooth manager service is null");
}
try {
+ mServiceLock.writeLock().lock();
mService = managerService.registerAdapter(mManagerCallback);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
mManagerService = managerService;
mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
mToken = new Binder();
@@ -605,10 +613,14 @@
@RequiresPermission(Manifest.permission.BLUETOOTH)
public boolean isEnabled() {
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.isEnabled();
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isEnabled();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+
return false;
}
@@ -639,12 +651,12 @@
* or OFF if BT is in BLE_ON state
*/
private void notifyUserAction(boolean enable) {
- if (mService == null) {
- Log.e(TAG, "mService is null");
- return;
- }
-
try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ Log.e(TAG, "mService is null");
+ return;
+ }
if (enable) {
mService.onLeServiceUp(); //NA:TODO implementation pending
} else {
@@ -652,6 +664,8 @@
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
}
@@ -783,26 +797,28 @@
@RequiresPermission(Manifest.permission.BLUETOOTH)
@AdapterState
public int getState() {
+ int state = BluetoothAdapter.STATE_OFF;
+
try {
- synchronized(mManagerCallback) {
- if (mService != null)
- {
- int state= mService.getState();
- if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
- //consider all internal states as OFF
- if (state == BluetoothAdapter.STATE_BLE_ON
- || state == BluetoothAdapter.STATE_BLE_TURNING_ON
- || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
- if (VDBG) Log.d(TAG, "Consider internal state as OFF");
- state = BluetoothAdapter.STATE_OFF;
- }
- return state;
- }
- // TODO(BT) there might be a small gap during STATE_TURNING_ON that
- // mService is null, handle that case
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ state = mService.getState();
}
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return STATE_OFF;
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+
+ // Consider all internal states as OFF
+ if (state == BluetoothAdapter.STATE_BLE_ON
+ || state == BluetoothAdapter.STATE_BLE_TURNING_ON
+ || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
+ if (VDBG) Log.d(TAG, "Consider internal state as OFF");
+ state = BluetoothAdapter.STATE_OFF;
+ }
+ if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
+ return state;
}
/**
@@ -825,19 +841,21 @@
@RequiresPermission(Manifest.permission.BLUETOOTH)
@AdapterState
public int getLeState() {
+ int state = BluetoothAdapter.STATE_OFF;
+
try {
- synchronized(mManagerCallback) {
- if (mService != null)
- {
- int state= mService.getState();
- if (VDBG) Log.d(TAG,"getLeState() returning " + state);
- return state;
- }
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ state = mService.getState();
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
- return BluetoothAdapter.STATE_OFF;
+
+ if (VDBG) Log.d(TAG,"getLeState() returning " + state);
+ return state;
}
boolean getLeAccess() {
@@ -879,16 +897,21 @@
*/
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean enable() {
- int state = STATE_OFF;
- if (isEnabled() == true){
+ int state = BluetoothAdapter.STATE_OFF;
+ if (isEnabled() == true) {
if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
return true;
}
- //Use service interface to get the exact state
- if (mService != null) {
- try {
- state = mService.getState();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ // Use service interface to get the exact state
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ state = mService.getState();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
if (state == BluetoothAdapter.STATE_BLE_ON) {
@@ -993,10 +1016,13 @@
*/
public boolean configHciSnoopLog(boolean enable) {
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.configHciSnoopLog(enable);
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.configHciSnoopLog(enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return false;
}
@@ -1012,12 +1038,16 @@
*/
public boolean factoryReset() {
try {
+ mServiceLock.readLock().lock();
if (mService != null) {
return mService.factoryReset();
- } else {
- SystemProperties.set("persist.bluetooth.factoryreset", "true");
}
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ SystemProperties.set("persist.bluetooth.factoryreset", "true");
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return false;
}
@@ -1032,10 +1062,13 @@
public ParcelUuid[] getUuids() {
if (getState() != STATE_ON) return null;
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.getUuids();
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.getUuids();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return null;
}
@@ -1058,10 +1091,13 @@
public boolean setName(String name) {
if (getState() != STATE_ON) return false;
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.setName(name);
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.setName(name);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return false;
}
@@ -1086,10 +1122,13 @@
public int getScanMode() {
if (getState() != STATE_ON) return SCAN_MODE_NONE;
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.getScanMode();
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.getScanMode();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return SCAN_MODE_NONE;
}
@@ -1124,10 +1163,13 @@
public boolean setScanMode(@ScanMode int mode, int duration) {
if (getState() != STATE_ON) return false;
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.setScanMode(mode, duration);
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.setScanMode(mode, duration);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return false;
}
@@ -1142,10 +1184,13 @@
public int getDiscoverableTimeout() {
if (getState() != STATE_ON) return -1;
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.getDiscoverableTimeout();
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.getDiscoverableTimeout();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return -1;
}
@@ -1153,10 +1198,13 @@
public void setDiscoverableTimeout(int timeout) {
if (getState() != STATE_ON) return;
try {
- synchronized(mManagerCallback) {
- if (mService != null) mService.setDiscoverableTimeout(timeout);
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) mService.setDiscoverableTimeout(timeout);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
}
/**
@@ -1193,10 +1241,13 @@
public boolean startDiscovery() {
if (getState() != STATE_ON) return false;
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.startDiscovery();
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.startDiscovery();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return false;
}
@@ -1221,10 +1272,13 @@
public boolean cancelDiscovery() {
if (getState() != STATE_ON) return false;
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.cancelDiscovery();
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.cancelDiscovery();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return false;
}
@@ -1251,10 +1305,13 @@
public boolean isDiscovering() {
if (getState() != STATE_ON) return false;
try {
- synchronized(mManagerCallback) {
- if (mService != null ) return mService.isDiscovering();
- }
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isDiscovering();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return false;
}
@@ -1266,9 +1323,12 @@
public boolean isMultipleAdvertisementSupported() {
if (getState() != STATE_ON) return false;
try {
- return mService.isMultiAdvertisementSupported();
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isMultiAdvertisementSupported();
} catch (RemoteException e) {
Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
return false;
}
@@ -1301,9 +1361,12 @@
public boolean isPeripheralModeSupported() {
if (getState() != STATE_ON) return false;
try {
- return mService.isPeripheralModeSupported();
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isPeripheralModeSupported();
} catch (RemoteException e) {
Log.e(TAG, "failed to get peripheral mode capability: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
return false;
}
@@ -1316,9 +1379,12 @@
public boolean isOffloadedFilteringSupported() {
if (!getLeAccess()) return false;
try {
- return mService.isOffloadedFilteringSupported();
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isOffloadedFilteringSupported();
} catch (RemoteException e) {
Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
return false;
}
@@ -1331,9 +1397,12 @@
public boolean isOffloadedScanBatchingSupported() {
if (!getLeAccess()) return false;
try {
- return mService.isOffloadedScanBatchingSupported();
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isOffloadedScanBatchingSupported();
} catch (RemoteException e) {
Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
return false;
}
@@ -1399,15 +1468,15 @@
*/
public void requestControllerActivityEnergyInfo(ResultReceiver result) {
try {
- synchronized(mManagerCallback) {
- if (mService != null) {
- mService.requestActivityInfo(result);
- result = null;
- }
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ mService.requestActivityInfo(result);
+ result = null;
}
} catch (RemoteException e) {
Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
} finally {
+ mServiceLock.readLock().unlock();
if (result != null) {
// Only send an immediate result if we failed.
result.send(0, null);
@@ -1432,11 +1501,14 @@
return toDeviceSet(new BluetoothDevice[0]);
}
try {
- synchronized(mManagerCallback) {
- if (mService != null) return toDeviceSet(mService.getBondedDevices());
- }
+ mServiceLock.readLock().lock();
+ if (mService != null) return toDeviceSet(mService.getBondedDevices());
return toDeviceSet(new BluetoothDevice[0]);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return null;
}
@@ -1456,10 +1528,13 @@
public int getConnectionState() {
if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED;
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.getAdapterConnectionState();
- }
- } catch (RemoteException e) {Log.e(TAG, "getConnectionState:", e);}
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.getAdapterConnectionState();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getConnectionState:", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
return BluetoothAdapter.STATE_DISCONNECTED;
}
@@ -1482,11 +1557,12 @@
public int getProfileConnectionState(int profile) {
if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED;
try {
- synchronized(mManagerCallback) {
- if (mService != null) return mService.getProfileConnectionState(profile);
- }
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.getProfileConnectionState(profile);
} catch (RemoteException e) {
Log.e(TAG, "getProfileConnectionState:", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -1790,7 +1866,9 @@
byte[] hash;
byte[] randomizer;
- byte[] ret = mService.readOutOfBandData();
+ byte[] ret = null;
+ mServiceLock.readLock().lock();
+ if (mService != null) mService.readOutOfBandData();
if (ret == null || ret.length != 32) return null;
@@ -1803,7 +1881,12 @@
}
return new Pair<byte[], byte[]>(hash, randomizer);
- } catch (RemoteException e) {Log.e(TAG, "", e);}*/
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ */
return null;
}
@@ -1939,17 +2022,21 @@
new IBluetoothManagerCallback.Stub() {
public void onBluetoothServiceUp(IBluetooth bluetoothService) {
if (VDBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
- synchronized (mManagerCallback) {
- mService = bluetoothService;
- synchronized (mProxyServiceStateCallbacks) {
- for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
- try {
- if (cb != null) {
- cb.onBluetoothServiceUp(bluetoothService);
- } else {
- Log.d(TAG, "onBluetoothServiceUp: cb is null!!!");
- }
- } catch (Exception e) { Log.e(TAG,"",e);}
+
+ mServiceLock.writeLock().lock();
+ mService = bluetoothService;
+ mServiceLock.writeLock().unlock();
+
+ synchronized (mProxyServiceStateCallbacks) {
+ for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ) {
+ try {
+ if (cb != null) {
+ cb.onBluetoothServiceUp(bluetoothService);
+ } else {
+ Log.d(TAG, "onBluetoothServiceUp: cb is null!!!");
+ }
+ } catch (Exception e) {
+ Log.e(TAG,"",e);
}
}
}
@@ -1957,20 +2044,24 @@
public void onBluetoothServiceDown() {
if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
- synchronized (mManagerCallback) {
- mService = null;
- if (mLeScanClients != null) mLeScanClients.clear();
- if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup();
- if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup();
- synchronized (mProxyServiceStateCallbacks) {
- for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
- try {
- if (cb != null) {
- cb.onBluetoothServiceDown();
- } else {
- Log.d(TAG, "onBluetoothServiceDown: cb is null!!!");
- }
- } catch (Exception e) { Log.e(TAG,"",e);}
+
+ mServiceLock.writeLock().lock();
+ mService = null;
+ if (mLeScanClients != null) mLeScanClients.clear();
+ if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup();
+ if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup();
+ mServiceLock.writeLock().unlock();
+
+ synchronized (mProxyServiceStateCallbacks) {
+ for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
+ try {
+ if (cb != null) {
+ cb.onBluetoothServiceDown();
+ } else {
+ Log.d(TAG, "onBluetoothServiceDown: cb is null!!!");
+ }
+ } catch (Exception e) {
+ Log.e(TAG,"",e);
}
}
}
@@ -2033,11 +2124,17 @@
//TODO(BT)
/*
try {
- return mService.changeApplicationBluetoothState(on, new
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.changeApplicationBluetoothState(on, new
StateChangeCallbackWrapper(callback), new Binder());
+ }
} catch (RemoteException e) {
Log.e(TAG, "changeBluetoothState", e);
- }*/
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ */
return false;
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index c1aac85..f6445e6 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1446,7 +1446,7 @@
*
* @return The locale list.
*/
- public LocaleList getLocales() {
+ public @NonNull LocaleList getLocales() {
fixUpLocaleList();
return mLocaleList;
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index dada612..32a2795 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -330,18 +330,43 @@
// doing the conversion here. This impl should be okay because
// we make sure to return a compatible display in the places
// where there are public APIs to retrieve the display... but
- // it would be cleaner and more maintainble to just be
+ // it would be cleaner and more maintainable to just be
// consistently dealing with a compatible display everywhere in
// the framework.
mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
final @Config int configChanges = calcConfigChanges(config);
+ // If even after the update there are no Locales set, grab the default locales.
LocaleList locales = mConfiguration.getLocales();
if (locales.isEmpty()) {
- locales = LocaleList.getAdjustedDefault();
+ locales = LocaleList.getDefault();
mConfiguration.setLocales(locales);
}
+
+ if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
+ if (locales.size() > 1) {
+ // The LocaleList has changed. We must query the AssetManager's available
+ // Locales and figure out the best matching Locale in the new LocaleList.
+ String[] availableLocales = mAssets.getNonSystemLocales();
+ if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+ // No app defined locales, so grab the system locales.
+ availableLocales = mAssets.getLocales();
+ if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+ availableLocales = null;
+ }
+ }
+
+ if (availableLocales != null) {
+ final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
+ availableLocales);
+ if (bestLocale != null && bestLocale != locales.get(0)) {
+ mConfiguration.setLocales(new LocaleList(bestLocale, locales));
+ }
+ }
+ }
+ }
+
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
mMetrics.densityDpi = mConfiguration.densityDpi;
mMetrics.density =
@@ -370,7 +395,7 @@
}
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
- adjustLanguageTag(locales.get(0).toLanguageTag()),
+ adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
mConfiguration.orientation,
mConfiguration.touchscreen,
mConfiguration.densityDpi, mConfiguration.keyboard,
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 8136796..60b618a 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -409,6 +409,14 @@
}
/**
+ * {@hide}
+ */
+ public int getFirstMatchIndex(String[] supportedLocales) {
+ return computeFirstMatchIndex(Arrays.asList(supportedLocales),
+ false /* assume English is not supported */);
+ }
+
+ /**
* Same as getFirstMatch(), but with English assumed to be supported, even if it's not.
* {@hide}
*/
@@ -437,7 +445,11 @@
* Assumes that there is no repetition in the input.
* {@hide}
*/
- public static boolean isPseudoLocalesOnly(String[] supportedLocales) {
+ public static boolean isPseudoLocalesOnly(@Nullable String[] supportedLocales) {
+ if (supportedLocales == null) {
+ return true;
+ }
+
if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) {
// This is for optimization. Since there's no repetition in the input, if we have more
// than the number of pseudo-locales plus one for the empty string, it's guaranteed
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 403e06c..4abbf0e 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -25,8 +25,10 @@
import android.util.Log;
import java.io.ByteArrayInputStream;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
@@ -45,6 +47,8 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import com.android.internal.logging.MetricsLogger;
+
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.SignerInfo;
@@ -69,6 +73,7 @@
/** Used to communicate with recovery. See bootable/recovery/recovery.cpp. */
private static final File RECOVERY_DIR = new File("/cache/recovery");
private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
+ private static final File LAST_INSTALL_FILE = new File(RECOVERY_DIR, "last_install");
private static final String LAST_PREFIX = "last_";
/**
@@ -682,13 +687,64 @@
}
}
+ // Read last_install; then report time for update and I/O to tron.
+ // Only report on the reboots immediately after an OTA update.
+ private static void parseLastInstallLog(Context context) {
+ try (BufferedReader in = new BufferedReader(new FileReader(LAST_INSTALL_FILE))) {
+ String line = null;
+ int bytesWritten = -1, bytesStashed = -1;
+ int timeTotal = -1;
+ while ((line = in.readLine()) != null) {
+ // Here is an example of lines in last_install:
+ // ...
+ // time_total: 101
+ // bytes_written_vendor: 51074
+ // bytes_stashed_vendor: 200
+ int numIndex = line.indexOf(':');
+ if (numIndex == -1 || numIndex + 1 >= line.length()) {
+ continue;
+ }
+ String numString = line.substring(numIndex + 1).trim();
+ int parsedNum;
+ try {
+ parsedNum = Integer.parseInt(numString);
+ } catch (NumberFormatException ignored) {
+ Log.e(TAG, "Failed to parse numbers in " + line);
+ continue;
+ }
+
+ if (line.startsWith("time")) {
+ timeTotal = parsedNum;
+ } else if (line.startsWith("bytes_written")) {
+ bytesWritten = (bytesWritten == -1) ? parsedNum : bytesWritten + parsedNum;
+ } else if (line.startsWith("bytes_stashed")) {
+ bytesStashed = (bytesStashed == -1) ? parsedNum : bytesStashed + parsedNum;
+ }
+ }
+
+ // Don't report data to tron if corresponding entry isn't found in last_install.
+ if (timeTotal != -1) {
+ MetricsLogger.histogram(context, "ota_time_total", timeTotal);
+ }
+ if (bytesWritten != -1) {
+ MetricsLogger.histogram(context, "ota_bytes_written", bytesWritten);
+ }
+ if (bytesStashed != -1) {
+ MetricsLogger.histogram(context, "ota_bytes_stashed", bytesStashed);
+ }
+
+ } catch (IOException ignored) {
+ Log.e(TAG, "Failed to read lines in last_install", ignored);
+ }
+ }
+
/**
* Called after booting to process and remove recovery-related files.
* @return the log file from recovery, or null if none was found.
*
* @hide
*/
- public static String handleAftermath() {
+ public static String handleAftermath(Context context) {
// Record the tail of the LOG_FILE
String log = null;
try {
@@ -699,6 +755,10 @@
Log.e(TAG, "Error reading recovery log", e);
}
+ if (log != null) {
+ parseLastInstallLog(context);
+ }
+
// Only remove the OTA package if it's partially processed (uncrypt'd).
boolean reservePackage = BLOCK_MAP_FILE.exists();
if (!reservePackage && UNCRYPT_PACKAGE_FILE.exists()) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 25fe4ff..1557a27 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -790,11 +790,14 @@
* <p>This method will fail for listeners that have
* not been granted the permission by the user.
*/
- public static void requestRebind(ComponentName componentName)
- throws RemoteException {
+ public static void requestRebind(ComponentName componentName) {
INotificationManager noMan = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- noMan.requestBindListener(componentName);
+ try {
+ noMan.requestBindListener(componentName);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
}
/**
@@ -807,12 +810,16 @@
* <p>The service should wait for the {@link #onListenerConnected()} event
* before performing this operation. I know it's tempting, but you must wait.
*/
- public final void requestUnbind() throws RemoteException {
+ public final void requestUnbind() {
if (mWrapper != null) {
INotificationManager noMan = getNotificationInterface();
- noMan.requestUnbindListener(mWrapper);
- // Disable future messages.
- isConnected = false;
+ try {
+ noMan.requestUnbindListener(mWrapper);
+ // Disable future messages.
+ isConnected = false;
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 37da869..6e6baea 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -18,7 +18,11 @@
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 @@
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 @@
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 @@
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();
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 5db0f16..2d1e0bd 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -142,13 +142,17 @@
public static int loadWebViewNativeLibraryFromPackage(String packageName,
ClassLoader clazzLoader) {
int ret = waitForProviderAndSetPackageInfo();
- if (ret != LIBLOAD_SUCCESS) {
+ if (ret != LIBLOAD_SUCCESS && ret != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
return ret;
}
if (!sPackageInfo.packageName.equals(packageName))
return LIBLOAD_WRONG_PACKAGE_NAME;
- return loadNativeLibrary(clazzLoader);
+ int loadNativeRet = loadNativeLibrary(clazzLoader);
+ // If we failed waiting for relro we want to return that fact even if we successfully load
+ // the relro file.
+ if (loadNativeRet == LIBLOAD_SUCCESS) return ret;
+ return loadNativeRet;
}
static WebViewFactoryProvider getProvider() {
@@ -240,7 +244,8 @@
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
- if (response.status != LIBLOAD_SUCCESS) {
+ if (response.status != LIBLOAD_SUCCESS
+ && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
throw new MissingWebViewPackageException("Failed to load WebView provider: "
+ getWebViewPreparationErrorReason(response.status));
}
@@ -599,8 +604,10 @@
try {
response =
getUpdateService().waitForAndGetProvider();
- if (response.status == WebViewFactory.LIBLOAD_SUCCESS)
+ if (response.status == LIBLOAD_SUCCESS
+ || response.status == LIBLOAD_FAILED_WAITING_FOR_RELRO) {
sPackageInfo = response.packageInfo;
+ }
} catch (RemoteException e) {
Log.e(LOGTAG, "error waiting for relro creation", e);
return LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN;
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index fb2337e..35b53c2 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -43,6 +43,8 @@
import dalvik.system.VMRuntime;
import libcore.util.EmptyArray;
+import java.io.BufferedReader;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
@@ -51,6 +53,8 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Objects;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
public final class ProcessStats implements Parcelable {
public static final String TAG = "ProcessStats";
@@ -151,7 +155,7 @@
};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 20;
+ private static final int PARCEL_VERSION = 21;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -188,6 +192,12 @@
// For reading parcels.
ArrayList<String> mIndexToCommonString;
+ private static final Pattern sPageTypeRegex = Pattern.compile(
+ "^Node\\s+(\\d+),.*. type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$");
+ private final ArrayList<Integer> mPageTypeZones = new ArrayList<Integer>();
+ private final ArrayList<String> mPageTypeLabels = new ArrayList<String>();
+ private final ArrayList<int[]> mPageTypeSizes = new ArrayList<int[]>();
+
public ProcessStats(boolean running) {
mRunning = running;
reset();
@@ -493,6 +503,7 @@
mReadError = null;
mFlags = 0;
evaluateSystemProperties(true);
+ updateFragmentation();
}
public boolean evaluateSystemProperties(boolean update) {
@@ -515,6 +526,101 @@
static final int[] BAD_TABLE = new int[0];
+
+ /**
+ * Load the system's memory fragmentation info.
+ */
+ public void updateFragmentation() {
+ // Parse /proc/pagetypeinfo and store the values.
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader("/proc/pagetypeinfo"));
+ final Matcher matcher = sPageTypeRegex.matcher("");
+ mPageTypeZones.clear();
+ mPageTypeLabels.clear();
+ mPageTypeSizes.clear();
+ while (true) {
+ final String line = reader.readLine();
+ if (line == null) {
+ break;
+ }
+ matcher.reset(line);
+ if (matcher.matches()) {
+ final Integer zone = Integer.valueOf(matcher.group(1), 10);
+ if (zone == null) {
+ continue;
+ }
+ mPageTypeZones.add(zone);
+ mPageTypeLabels.add(matcher.group(2));
+ mPageTypeSizes.add(splitAndParseNumbers(matcher.group(3)));
+ }
+ }
+ } catch (IOException ex) {
+ mPageTypeZones.clear();
+ mPageTypeLabels.clear();
+ mPageTypeSizes.clear();
+ return;
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException allHopeIsLost) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Split the string of digits separaed by spaces. There must be no
+ * leading or trailing spaces. The format is ensured by the regex
+ * above.
+ */
+ private static int[] splitAndParseNumbers(String s) {
+ // These are always positive and the numbers can't be so big that we'll overflow
+ // so just do the parsing inline.
+ boolean digit = false;
+ int count = 0;
+ final int N = s.length();
+ // Count the numbers
+ for (int i=0; i<N; i++) {
+ final char c = s.charAt(i);
+ if (c >= '0' && c <= '9') {
+ if (!digit) {
+ digit = true;
+ count++;
+ }
+ } else {
+ digit = false;
+ }
+ }
+ // Parse the numbers
+ final int[] result = new int[count];
+ int p = 0;
+ int val = 0;
+ for (int i=0; i<N; i++) {
+ final char c = s.charAt(i);
+ if (c >= '0' && c <= '9') {
+ if (!digit) {
+ digit = true;
+ val = c - '0';
+ } else {
+ val *= 10;
+ val += c - '0';
+ }
+ } else {
+ if (digit) {
+ digit = false;
+ result[p++] = val;
+ }
+ }
+ }
+ if (count > 0) {
+ result[count-1] = val;
+ }
+ return result;
+ }
+
+
private void writeCompactedLongArray(Parcel out, long[] array, int num) {
for (int i=0; i<num; i++) {
long val = array[i];
@@ -717,6 +823,15 @@
}
}
+ // Fragmentation info (/proc/pagetypeinfo)
+ final int NPAGETYPES = mPageTypeLabels.size();
+ out.writeInt(NPAGETYPES);
+ for (int i=0; i<NPAGETYPES; i++) {
+ out.writeInt(mPageTypeZones.get(i));
+ out.writeString(mPageTypeLabels.get(i));
+ out.writeIntArray(mPageTypeSizes.get(i));
+ }
+
mCommonStringToIndex = null;
}
@@ -981,6 +1096,20 @@
}
}
+ // Fragmentation info
+ final int NPAGETYPES = in.readInt();
+ mPageTypeZones.clear();
+ mPageTypeZones.ensureCapacity(NPAGETYPES);
+ mPageTypeLabels.clear();
+ mPageTypeLabels.ensureCapacity(NPAGETYPES);
+ mPageTypeSizes.clear();
+ mPageTypeSizes.ensureCapacity(NPAGETYPES);
+ for (int i=0; i<NPAGETYPES; i++) {
+ mPageTypeZones.add(in.readInt());
+ mPageTypeLabels.add(in.readString());
+ mPageTypeSizes.add(in.createIntArray());
+ }
+
mIndexToCommonString = null;
if (DEBUG_PARCEL) Slog.d(TAG, "Successfully read procstats!");
@@ -1271,6 +1400,8 @@
*/
pw.print(" mRunning="); pw.println(mRunning);
}
+
+ dumpFragmentationLocked(pw);
}
public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now, boolean activeOnly) {
@@ -1282,6 +1413,21 @@
dumpTotalsLocked(pw, now);
}
+ private void dumpFragmentationLocked(PrintWriter pw) {
+ pw.println();
+ pw.println("Available pages by page size:");
+ final int NPAGETYPES = mPageTypeLabels.size();
+ for (int i=0; i<NPAGETYPES; i++) {
+ pw.format("Zone %3d %14s ", mPageTypeZones.get(i), mPageTypeLabels.get(i));
+ final int[] sizes = mPageTypeSizes.get(i);
+ final int N = sizes == null ? 0 : sizes.length;
+ for (int j=0; j<N; j++) {
+ pw.format("%6d", sizes[j]);
+ }
+ pw.println();
+ }
+ }
+
long printMemoryCategory(PrintWriter pw, String prefix, String label, double memWeight,
long totalTime, long curTotalMem, int samples) {
if (memWeight != 0) {
@@ -1540,6 +1686,24 @@
pw.print(totalMem.processStateSamples[i]);
}
pw.println();
+
+ final int NPAGETYPES = mPageTypeLabels.size();
+ for (int i=0; i<NPAGETYPES; i++) {
+ pw.print("availablepages,");
+ pw.print(mPageTypeLabels.get(i));
+ pw.print(",");
+ pw.print(mPageTypeZones.get(i));
+ pw.print(",");
+ final int[] sizes = mPageTypeSizes.get(i);
+ final int N = sizes == null ? 0 : sizes.length;
+ for (int j=0; j<N; j++) {
+ if (j != 0) {
+ pw.print(",");
+ }
+ pw.print(sizes[j]);
+ }
+ pw.println();
+ }
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 6d6c162..fbc51cd 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -127,7 +127,7 @@
.append("\n").toString();
final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
- String recovery = RecoverySystem.handleAftermath();
+ String recovery = RecoverySystem.handleAftermath(ctx);
if (recovery != null && db != null) {
db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
}
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 1bc4285..1fc08ed 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -479,17 +479,13 @@
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);
}
}
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 024c21d..982a1f8 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -606,6 +606,18 @@
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 @@
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);
}
// ----------------------------------------------------------------------------
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 3c47705..39e1bbb 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -19,7 +19,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F3374248"
- android:clickable="true" >
+ android:importantForAccessibility="noHideDescendants"
+ android:clickable="true">
<ImageView android:id="@+id/work_widget_app_icon"
android:layout_width="wrap_content"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 3ba6ce1..37fb816 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -24,6 +24,8 @@
<dimen name="thumbnail_height">192dp</dimen>
<!-- The amount to scale a fullscreen screenshot thumbnail. -->
<item name="thumbnail_fullscreen_scale" type="fraction">60%</item>
+ <!-- The width used to calculate scale for full screen thumbnail on TV -->
+ <integer name="thumbnail_width_tv">240</integer>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
@@ -161,6 +163,9 @@
<!-- 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>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dffab2c..45ee778 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4317,4 +4317,7 @@
<!-- Description of notification shown when device has been forced to safe mode after a security compromise. -->
<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>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3f1de6d..f2c3c42 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1496,6 +1496,7 @@
<java-symbol type="integer" name="config_dockedStackDividerSnapMode" />
<java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" />
<java-symbol type="fraction" name="thumbnail_fullscreen_scale" />
+ <java-symbol type="integer" name="thumbnail_width_tv" />
<java-symbol type="dimen" name="resize_shadow_size" />
<java-symbol type="color" name="resize_shadow_start_color" />
<java-symbol type="color" name="resize_shadow_end_color" />
@@ -2435,6 +2436,7 @@
<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" />
@@ -2594,4 +2596,5 @@
<!-- Pinner Service -->
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
+ <java-symbol type="string" name="suspended_widget_accessibility" />
</resources>
diff --git a/core/tests/coretests/apks/locales/Android.mk b/core/tests/coretests/apks/locales/Android.mk
new file mode 100644
index 0000000..9cb13dd
--- /dev/null
+++ b/core/tests/coretests/apks/locales/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := locales
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/locales/AndroidManifest.xml b/core/tests/coretests/apks/locales/AndroidManifest.xml
new file mode 100644
index 0000000..8a1f575
--- /dev/null
+++ b/core/tests/coretests/apks/locales/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.locales">
+
+ <application android:hasCode="false">
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/locales/res/values-pl/values.xml b/core/tests/coretests/apks/locales/res/values-pl/values.xml
new file mode 100644
index 0000000..22237fa8
--- /dev/null
+++ b/core/tests/coretests/apks/locales/res/values-pl/values.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="text">Text (pl)</string>
+</resources>
diff --git a/core/tests/coretests/apks/locales/res/values/values.xml b/core/tests/coretests/apks/locales/res/values/values.xml
new file mode 100644
index 0000000..cb990d2
--- /dev/null
+++ b/core/tests/coretests/apks/locales/res/values/values.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="text">Text</string>
+</resources>
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
new file mode 100644
index 0000000..e85666e
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.content.res;
+
+import android.os.FileUtils;
+import android.os.LocaleList;
+import android.support.test.filters.SmallTest;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.view.Display;
+
+import com.android.frameworks.coretests.R;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Locale;
+
+public class ResourcesLocaleTest extends AndroidTestCase {
+
+ private String extractApkAndGetPath(int id) throws Exception {
+ final Resources resources = getContext().getResources();
+ try (InputStream is = resources.openRawResource(id)) {
+ File path = new File(getContext().getFilesDir(), resources.getResourceEntryName(id));
+ FileUtils.copyToFileOrThrow(is, path);
+ return path.getAbsolutePath();
+ }
+ }
+
+ private Resources createResourcesWithApk(int rawApkId) throws Exception {
+ final AssetManager assets = new AssetManager();
+ assertTrue(assets.addAssetPath(extractApkAndGetPath(rawApkId)) != 0);
+
+ final DisplayMetrics dm = new DisplayMetrics();
+ dm.setToDefaults();
+ return new Resources(assets, dm, new Configuration());
+ }
+
+ private static void ensureNoLanguage(Resources resources, String language) {
+ final String[] supportedLocales = resources.getAssets().getNonSystemLocales();
+ for (String languageTag : supportedLocales) {
+ if ("en-XA".equals(languageTag)) {
+ continue;
+ }
+ assertFalse(
+ "supported locales: " + Arrays.toString(supportedLocales),
+ language.equals(Locale.forLanguageTag(languageTag).getLanguage()));
+ }
+ }
+
+ @SmallTest
+ public void testEnglishIsAlwaysConsideredSupported() throws Exception {
+ final Resources resources = createResourcesWithApk(R.raw.locales);
+ ensureNoLanguage(resources, "en");
+
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("en-US,pl-PL");
+ final Configuration config = new Configuration();
+ config.setLocales(preferredLocales);
+
+ resources.updateConfiguration(config, null);
+
+ // The APK we loaded has default and Polish languages. If English is first in the list,
+ // always take it the default (assumed to be English).
+ assertEquals(Locale.forLanguageTag("en-US"),
+ resources.getConfiguration().getLocales().get(0));
+ }
+
+ @SmallTest
+ public void testSelectFirstSupportedLanguage() throws Exception {
+ final Resources resources = createResourcesWithApk(R.raw.locales);
+ ensureNoLanguage(resources, "fr");
+
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("fr-FR,pl-PL");
+ final Configuration config = new Configuration();
+ config.setLocales(preferredLocales);
+
+ resources.updateConfiguration(config, null);
+
+ // The APK we loaded has default and Polish languages. We expect the Polish language to
+ // therefore be chosen.
+ assertEquals(Locale.forLanguageTag("pl-PL"),
+ resources.getConfiguration().getLocales().get(0));
+ }
+}
diff --git a/docs/html-intl/intl/es/preview/setup-sdk.jd b/docs/html-intl/intl/es/preview/setup-sdk.jd
index 01c715b..769121d 100644
--- a/docs/html-intl/intl/es/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/es/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
y <a href="{@docRoot}preview/api-overview.html">API y funciones de Android N
</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/in/preview/setup-sdk.jd b/docs/html-intl/intl/in/preview/setup-sdk.jd
index a842931..c6e43f1 100644
--- a/docs/html-intl/intl/in/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/in/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
dan <a href="{@docRoot}preview/api-overview.html">Android N API
dan Fiturnya</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/ja/preview/setup-sdk.jd b/docs/html-intl/intl/ja/preview/setup-sdk.jd
index fd5faba..4331641 100644
--- a/docs/html-intl/intl/ja/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/ja/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/ko/preview/setup-sdk.jd b/docs/html-intl/intl/ko/preview/setup-sdk.jd
index adb9edf..91f17c1 100644
--- a/docs/html-intl/intl/ko/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/ko/preview/setup-sdk.jd
@@ -136,7 +136,7 @@
<h2 id="java8">Java 8 JDK 및 JRE 가져오기</h2>
-<p>Android N 플랫폼에 대해 앱을 컴파일하려면
+<p>Android N 플랫폼에 대해 앱을 컴파일하려면
Java 8 개발자 키트(JDK 8)를 사용해야 하며, Android
Studio 2.1의 일부 도구를 사용하려면 Java 8 런타임 환경(JRE 8)을 설치해야
합니다. 따라서 각각 최신 버전이 없는 경우, 지금 JDK 8 및 JRE 8를
@@ -221,4 +221,4 @@
및 기능</a>에서 Android N 플랫폼에
대해 자세히 알아봅니다.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/pt-br/preview/setup-sdk.jd b/docs/html-intl/intl/pt-br/preview/setup-sdk.jd
index 4159c25..9035e50 100644
--- a/docs/html-intl/intl/pt-br/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/pt-br/preview/setup-sdk.jd
@@ -79,7 +79,7 @@
<h2 id="get-sdk">Obter o N Preview SDK</h2>
-<p>Para começar a desenvolver com as APIs do Android N, instale o
+<p>Para começar a desenvolver com as APIs do Android N, instale o
Android N Preview SDK no Android Studio da seguinte maneira:</p>
<ol>
@@ -112,7 +112,7 @@
<p>
Informações detalhadas sobre as APIs do Android N são disponibilizadas na documentação de referência do N Preview
, que pode ser baixada pela tabela a seguir.
- Este pacote contém uma versão off-line resumida do site de desenvolvedores do Android
+ Este pacote contém uma versão off-line resumida do site de desenvolvedores do Android
e inclui uma referência atualizada para as APIs do Android N, além de um relatório
das diferenças entre as APIs.
</p>
@@ -168,7 +168,7 @@
Para usar as APIs do Android N, seu projeto deve ser configurado da maneira apropriada.
</p>
-<p>Se planeja usar os recursos de linguagem do Java 8, consulte
+<p>Se planeja usar os recursos de linguagem do Java 8, consulte
<a href="{@docRoot}preview/j8-jack.html">Recursos de linguagem do Java 8</a>
para saber mais sobre os recursos do Java 8 com suporte e
como configurar seu projeto com o compilador Jack.</p>
@@ -216,9 +216,9 @@
<ul>
<li>Siga o guia para <a href="{@docRoot}preview/download.html">Testar em um dispositivo Android N</a>.</li>
- <li>Saiba mais sobre a plataforma Android N com
+ <li>Saiba mais sobre a plataforma Android N com
<a href="{@docRoot}preview/behavior-changes.html">Mudanças de comportamento</a>
e <a href="{@docRoot}preview/api-overview.html">Recursos de APIs do
Android N</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/ru/preview/setup-sdk.jd b/docs/html-intl/intl/ru/preview/setup-sdk.jd
index 3838700..582fe9f 100644
--- a/docs/html-intl/intl/ru/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/ru/preview/setup-sdk.jd
@@ -79,7 +79,7 @@
<h2 id="get-sdk">Установка N Preview SDK</h2>
-<p>Чтобы начать разработку с использованием API-интерфейсов Android N, необходимо установить
+<p>Чтобы начать разработку с использованием API-интерфейсов Android N, необходимо установить
Android N Preview SDK в Android Studio, выполнив следующие шаги.</p>
<ol>
@@ -136,7 +136,7 @@
<h2 id="java8">Установка Java 8 JDK и JRE</h2>
-<p>Чтобы скомпилировать ваше приложение для платформы Android N, необходимо использовать
+<p>Чтобы скомпилировать ваше приложение для платформы Android N, необходимо использовать
Java 8 Developer Kit (JDK 8), а для использования некоторых инструментов с Android
Studio 2.1 необходимо установить Java 8 Runtime Environment (JRE 8). Поэтому если у вас еще
нет последних версий каждого из этих пакетов, загрузите JDK 8 и JRE 8
@@ -221,4 +221,4 @@
и в описании <a href="{@docRoot}preview/api-overview.html">API-интерфейсов и возможностей Android N</a>.
</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/vi/preview/setup-sdk.jd b/docs/html-intl/intl/vi/preview/setup-sdk.jd
index 2e3a18b..6f6cd20 100644
--- a/docs/html-intl/intl/vi/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/vi/preview/setup-sdk.jd
@@ -98,7 +98,7 @@
<li>Nhấp vào tab <strong>SDK Tools</strong>, rồi chọn
<strong>Android SDK Build Tools</strong>, <strong>Android SDK
- Platform-Tools</strong>, và các hộp kiểm <strong>Android SDK Tools</strong>
+ Platform-Tools</strong>, và các hộp kiểm <strong>Android SDK Tools</strong>
.
</li>
@@ -221,4 +221,4 @@
và<a href="{@docRoot}preview/api-overview.html">API Android N
và Các tính năng</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd b/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
index 216d464..eaef7a1 100644
--- a/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd b/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
index d2a9d9e..7a46aca 100644
--- a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html/images/testing/test_framework.png b/docs/html/images/testing/test_framework.png
deleted file mode 100644
index fbc5fc2..0000000
--- a/docs/html/images/testing/test_framework.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js
index c85ef7e..3aab5bd 100644
--- a/docs/html/jd_extras_en.js
+++ b/docs/html/jd_extras_en.js
@@ -237,7 +237,7 @@
"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": [],
diff --git a/docs/html/preview/setup-sdk.jd b/docs/html/preview/setup-sdk.jd
index 28fc406..ebeae74 100644
--- a/docs/html/preview/setup-sdk.jd
+++ b/docs/html/preview/setup-sdk.jd
@@ -189,4 +189,4 @@
and <a href="{@docRoot}preview/api-overview.html">Android N APIs
and Features</a>.</li>
</ul>
-
+<div class="end-content-title"></div>
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 5561b22..b601cde 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -34,6 +34,7 @@
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 @@
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) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 23ae691..4bdc70e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2748,10 +2748,10 @@
public static abstract class AudioRecordingCallback {
/**
* Called whenever the device recording configuration has changed.
- * @param configs array containing the results of
+ * @param configs list containing the results of
* {@link AudioManager#getActiveRecordingConfigurations()}.
*/
- public void onRecordingConfigChanged(AudioRecordingConfiguration[] configs) {}
+ public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {}
}
private static class AudioRecordingCallbackInfo {
@@ -2765,10 +2765,10 @@
private final static class RecordConfigChangeCallbackData {
final AudioRecordingCallback mCb;
- final AudioRecordingConfiguration[] mConfigs;
+ final List<AudioRecordingConfiguration> mConfigs;
RecordConfigChangeCallbackData(AudioRecordingCallback cb,
- AudioRecordingConfiguration[] configs) {
+ List<AudioRecordingConfiguration> configs) {
mCb = cb;
mConfigs = configs;
}
@@ -2848,10 +2848,10 @@
/**
* Returns the current active audio recording configurations of the device.
- * @return a non-null array of recording configurations. An array of length 0 indicates there is
+ * @return a non-null list of recording configurations. An empty list indicates there is
* no recording active when queried.
*/
- public @NonNull AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
+ public @NonNull List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
final IAudioService service = getService();
try {
return service.getActiveRecordingConfigurations();
@@ -2909,7 +2909,7 @@
private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
- public void dispatchRecordingConfigChange(AudioRecordingConfiguration[] configs) {
+ public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
synchronized(mRecordCallbackLock) {
if (mRecordCallbackList != null) {
for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 4504e69..8efd599 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -94,6 +94,11 @@
* 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 @@
* @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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* @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 @@
* 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) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 12e88a2..9a81668 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -156,7 +156,6 @@
/**
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* <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) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 97f670b..c7931fc 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -164,5 +164,5 @@
oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb);
- AudioRecordingConfiguration[] getActiveRecordingConfigurations();
+ List<AudioRecordingConfiguration> getActiveRecordingConfigurations();
}
diff --git a/media/java/android/media/IRecordingConfigDispatcher.aidl b/media/java/android/media/IRecordingConfigDispatcher.aidl
index e803283..f4e98b2 100644
--- a/media/java/android/media/IRecordingConfigDispatcher.aidl
+++ b/media/java/android/media/IRecordingConfigDispatcher.aidl
@@ -25,6 +25,6 @@
*/
oneway interface IRecordingConfigDispatcher {
- void dispatchRecordingConfigChange(in AudioRecordingConfiguration[] configs);
+ void dispatchRecordingConfigChange(in List<AudioRecordingConfiguration> configs);
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index b9ed269..87c6d88 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1918,13 +1918,15 @@
case CodecProfileLevel.MPEG2ProfileMain:
switch (profileLevel.level) {
case CodecProfileLevel.MPEG2LevelLL:
- FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 4000; break;
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 4000; break;
case CodecProfileLevel.MPEG2LevelML:
- FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break;
+ FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break;
case CodecProfileLevel.MPEG2LevelH14:
- FR = 60; W = 90; H = 68; MBPS = 367200; FS = 6120; BR = 60000; break;
+ FR = 60; W = 90; H = 68; MBPS = 183600; FS = 6120; BR = 60000; break;
case CodecProfileLevel.MPEG2LevelHL:
- FR = 60; W = 120; H = 68; MBPS = 489600; FS = 8160; BR = 80000; break;
+ FR = 60; W = 120; H = 68; MBPS = 244800; FS = 8160; BR = 80000; break;
+ case CodecProfileLevel.MPEG2LevelHP:
+ FR = 60; W = 120; H = 68; MBPS = 489600; FS = 8160; BR = 80000; break;
default:
Log.w(TAG, "Unrecognized profile/level "
+ profileLevel.profile + "/"
@@ -1982,15 +1984,12 @@
FR = 30; W = 22; H = 18; MBPS = 5940; FS = 396; BR = 128; break;
case CodecProfileLevel.MPEG4Level3:
FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 384; break;
- case CodecProfileLevel.MPEG4Level4:
case CodecProfileLevel.MPEG4Level4a:
+ FR = 30; W = 40; H = 30; MBPS = 36000; FS = 1200; BR = 4000; break;
case CodecProfileLevel.MPEG4Level5:
- // While MPEG4 SP does not have level 4 or 5, some vendors
- // report it. Use the same limits as level 3, but mark as
- // unsupported.
- FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 384;
- supported = false;
- break;
+ FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 8000; break;
+ case CodecProfileLevel.MPEG4Level6:
+ FR = 30; W = 80; H = 45; MBPS = 108000; FS = 3600; BR = 12000; break;
default:
Log.w(TAG, "Unrecognized profile/level "
+ profileLevel.profile + "/"
@@ -2007,12 +2006,9 @@
FR = 30; W = 22; H = 18; MBPS = 5940; FS = 396; BR = 384; break;
case CodecProfileLevel.MPEG4Level3:
FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 768; break;
- // case CodecProfileLevel.MPEG4Level3b:
- // TODO: MPEG4 level 3b is not defined in OMX
- // MBPS = 11880; FS = 396; BR = 1500; break;
+ case CodecProfileLevel.MPEG4Level3b:
+ FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 1500; break;
case CodecProfileLevel.MPEG4Level4:
- case CodecProfileLevel.MPEG4Level4a:
- // TODO: MPEG4 level 4a is not defined in spec
FR = 30; W = 44; H = 36; MBPS = 23760; FS = 792; BR = 3000; break;
case CodecProfileLevel.MPEG4Level5:
FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 8000; break;
@@ -2640,9 +2636,11 @@
public static final int MPEG4Level1 = 0x04;
public static final int MPEG4Level2 = 0x08;
public static final int MPEG4Level3 = 0x10;
+ public static final int MPEG4Level3b = 0x18;
public static final int MPEG4Level4 = 0x20;
public static final int MPEG4Level4a = 0x40;
public static final int MPEG4Level5 = 0x80;
+ public static final int MPEG4Level6 = 0x100;
// from OMX_VIDEO_MPEG2PROFILETYPE
public static final int MPEG2ProfileSimple = 0x00;
@@ -2657,6 +2655,7 @@
public static final int MPEG2LevelML = 0x01;
public static final int MPEG2LevelH14 = 0x02;
public static final int MPEG2LevelHL = 0x03;
+ public static final int MPEG2LevelHP = 0x04;
// from OMX_AUDIO_AACPROFILETYPE
public static final int AACObjectMain = 1;
diff --git a/packages/PrintSpooler/res/layout/add_printer_list_header.xml b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
index ff342cb..9eee0c4 100644
--- a/packages/PrintSpooler/res/layout/add_printer_list_header.xml
+++ b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
@@ -16,7 +16,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
- android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="horizontal"
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7a8c6f2..89c46d7 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -809,7 +809,8 @@
<!-- Option in navigation drawer that leads to Settings main screen [CHAR LIMIT=30] -->
<string name="home">Settings Home</string>
- <string-array name="battery_labels" translatable="false">
+ <!-- Labels on the side of batter graph in Quick Settings and Settings [CHAR LIMIT=8] -->
+ <string-array name="battery_labels">
<item>0%</item>
<item>50%</item>
<item>100%</item>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 474e3e6..6bc4df7 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -815,6 +815,14 @@
*/
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);
diff --git a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
index 0f92fa6..8b07599 100644
--- a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
+++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
@@ -19,9 +19,11 @@
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.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 @@
for (File file : files) {
addFileRow(result, file);
}
+ result.setNotificationUri(getContext().getContentResolver(), getNotificationUri());
}
}
return result;
@@ -130,6 +134,12 @@
}
}
+ // 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;
}
diff --git a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
index 201f47d..36df924 100644
--- a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
+++ b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
@@ -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
diff --git a/packages/SystemUI/res/layout/notification_children_divider.xml b/packages/SystemUI/res/layout/notification_children_divider.xml
index dad7cea..76315b8 100644
--- a/packages/SystemUI/res/layout/notification_children_divider.xml
+++ b/packages/SystemUI/res/layout/notification_children_divider.xml
@@ -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" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 658571e..2c8a559 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -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"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b8f576b..cbb3c0a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -248,6 +248,9 @@
<!-- 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>
diff --git a/packages/SystemUI/res/values/values_tv.xml b/packages/SystemUI/res/values/values_tv.xml
index dc0483f..9259415 100644
--- a/packages/SystemUI/res/values/values_tv.xml
+++ b/packages/SystemUI/res/values/values_tv.xml
@@ -16,5 +16,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item format="float" type="integer" name="unselected_scale">1.0</item>
<item format="float" type="integer" name="selected_scale">1.259</item>
- <item format="float" type="integer" name="dismiss_unselected_alpha">0.1</item>
+ <item format="float" type="integer" name="dismiss_unselected_alpha">0.3</item>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index b6d8095..7a5e322 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -45,6 +45,7 @@
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 void onAnimationEnd(Animator animation) {
if (!mCancelled) {
mCallback.setUserExpandedChild(scaledView, expand);
+ } else {
+ mCallback.setExpansionCancelled(scaledView);
}
mCallback.setUserLockedChild(scaledView, false);
mScaleAnimation.removeListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 4341ab9c..ecb64f6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -54,9 +54,9 @@
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.tv.animations.HomeRecentsEnterExitAnimationHolder;
import com.android.systemui.recents.tv.views.RecentsTvView;
+import com.android.systemui.recents.tv.views.TaskCardView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
-import com.android.systemui.recents.views.AnimationProps;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.tv.pip.PipManager;
import com.android.systemui.tv.pip.PipRecentsOverlayManager;
@@ -184,12 +184,14 @@
if (!plan.hasTasks()) {
loader.preloadTasks(plan, -1, !launchState.launchedFromHome);
}
+
+ int numVisibleTasks = TaskCardView.getNumberOfVisibleTasks(getApplicationContext());
mLaunchedFromHome = launchState.launchedFromHome;
TaskStack stack = plan.getTaskStack();
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.runningTaskId = launchState.launchedToTaskId;
- loadOpts.numVisibleTasks = stack.getStackTaskCount();
- loadOpts.numVisibleTaskThumbnails = stack.getStackTaskCount();
+ loadOpts.numVisibleTasks = numVisibleTasks;
+ loadOpts.numVisibleTaskThumbnails = numVisibleTasks;
loader.loadTasks(this, plan, loadOpts);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
index c3efe64..8218599 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
@@ -104,6 +104,7 @@
mTargetView.getDismissIconView().setAlpha(mDismissIconAlpha * level);
mTargetView.getThumbnailView().setZ(z);
+ mTargetView.getDismissIconView().setZ(z);
}
public float getFocusProgress() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 85fb50e..bd85748 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -38,6 +38,8 @@
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
@@ -85,6 +87,24 @@
mCornerRadius = getResources().getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.isTouchExplorationEnabled()) {
+ mDismissIconView.setFocusable(true);
+ mDismissIconView.setFocusableInTouchMode(true);
+ mDismissIconView.setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ setDismissState(true);
+ } else {
+ setDismissState(false);
+ }
+ }
+ });
+ } else {
+ mDismissIconView.setFocusable(false);
+ mDismissIconView.setFocusableInTouchMode(false);
+ }
mViewFocusAnimator = new ViewFocusAnimator(this);
}
@@ -288,7 +308,6 @@
lp.height = LayoutParams.MATCH_PARENT;
screenshotView.setLayoutParams(lp);
- screenshotView.setImageBitmap(screenshot);
screenshotView.setClipToOutline(true);
screenshotView.setOutlineProvider(new ViewOutlineProvider() {
@Override
@@ -296,6 +315,7 @@
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
}
});
+ screenshotView.setImageBitmap(screenshot);
}
private void setAsBannerView(Drawable banner, ImageView bannerView) {
@@ -304,7 +324,6 @@
.getDimensionPixelSize(R.dimen.recents_tv_banner_width);
lp.height = getResources()
.getDimensionPixelSize(R.dimen.recents_tv_banner_height);
-
bannerView.setLayoutParams(lp);
bannerView.setImageDrawable(banner);
}
@@ -327,4 +346,16 @@
public View getDismissIconView() {
return mDismissIconView;
}
+
+ public static int getNumberOfVisibleTasks(Context context) {
+ Resources res = context.getResources();
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ Point size = new Point();
+ display.getSize(size);
+ int screenWidth = size.x;
+ int cardWidth = res.getDimensionPixelSize(R.dimen.recents_tv_card_width);
+ int spacing = res.getDimensionPixelSize(R.dimen.recents_tv_gird_card_spacing);
+ return (int) (1.0 + Math.ceil(screenWidth / (cardWidth + spacing * 2.0)));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index 0a6d1ed..b6b86b4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -23,9 +23,11 @@
import android.view.ViewGroup;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
+import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.AnimationProps;
@@ -122,7 +124,10 @@
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
- holder.init(mTaskList.get(position));
+ Task task = mTaskList.get(position);
+ // Retrives from caches, loading only if necessary
+ Recents.getTaskLoader().loadTaskData(task);
+ holder.init(task);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 64eaeec..f728aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -1065,9 +1065,6 @@
mEntranceAnimationRunning = true;
resizeStack(position, mSnapAlgorithm.getMiddleTarget().position,
mSnapAlgorithm.getMiddleTarget());
-
- // Vibrate after docking
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
@@ -1105,9 +1102,6 @@
mExitStartPosition = getCurrentPosition();
stopDragging(mExitStartPosition, target, 336 /* duration */, 100 /* startDelay */,
0 /* endDelay */, Interpolators.FAST_OUT_SLOW_IN);
-
- // Vibrate after undocking
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index e66dc74..63933d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -189,6 +189,7 @@
protected boolean mVisible;
protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
+ protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
/**
* Notifications with keys in this set are not actually around anymore. We kept them around
@@ -968,7 +969,7 @@
return vetoButton;
}
- private void performRemoveNotification(StatusBarNotification n, boolean removeView) {
+ protected void performRemoveNotification(StatusBarNotification n, boolean removeView) {
final String pkg = n.getPackageName();
final String tag = n.getTag();
final int id = n.getId();
@@ -980,6 +981,9 @@
mKeysKeptForRemoteInput.remove(n.getKey());
removeView = true;
}
+ if (mRemoteInputEntriesToRemoveOnCollapse.remove(mNotificationData.get(n.getKey()))) {
+ removeView = true;
+ }
if (removeView) {
removeNotification(n.getKey(), null);
}
@@ -2085,8 +2089,7 @@
/**
* The LEDs are turned off when the notification panel is shown, even just a little bit.
- * See also NotificationStackScrollLayout.setIsExpanded() for another place where we
- * attempt to do this.
+ * See also PhoneStatusBar.setPanelExpanded for another place where we attempt to do this.
*/
protected void handleVisibleToUserChanged(boolean visibleToUser) {
try {
@@ -2326,8 +2329,9 @@
Entry entry = mNotificationData.get(key);
if (entry == null) {
return;
- } else if (mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
+ } else {
mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+ mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
}
Notification n = notification.getNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index b4ce9cd..c53ab59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -22,7 +22,6 @@
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.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.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 @@
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 @@
*/
private boolean mOnKeyguard;
- private AnimatorSet mTranslateAnim;
+ private Animator mTranslateAnim;
private ArrayList<View> mTranslateableViews;
private NotificationContentView mPublicLayout;
private NotificationContentView mPrivateLayout;
@@ -115,8 +115,6 @@
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 @@
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 @@
mGroupManager.toggleGroupExpansion(mStatusBarNotification);
mOnExpandClickListener.onExpandClicked(mEntry,
mGroupManager.isGroupExpanded(mStatusBarNotification));
+ mGroupExpansionChanging = true;
+ updateBackgroundForGroupState();
} else {
boolean nowExpanded;
if (isPinned()) {
@@ -160,6 +161,29 @@
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 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 @@
updateNotificationColor();
updateClearability();
if (mIsSummaryWithChildren) {
- recreateNotificationHeader();
+ mChildrenContainer.recreateNotificationHeader(mExpandClickListener, mEntry.notification);
mChildrenContainer.onNotificationUpdated();
}
if (mIconAnimationRunning) {
@@ -329,9 +353,6 @@
if (mChildrenContainer != null) {
mChildrenContainer.removeNotification(row);
}
- if (!row.isRemoved()) {
- mHeaderUtil.restoreNotificationHeader(row);
- }
onChildrenCountChanged();
row.setIsChildInGroup(false, null);
}
@@ -352,7 +373,10 @@
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 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 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 @@
continue;
}
mChildrenContainer.removeNotification(row);
- if (!row.isRemoved()) {
- mHeaderUtil.restoreNotificationHeader(row);
- }
row.setIsChildInGroup(false, null);
}
onChildrenCountChanged();
@@ -684,8 +702,13 @@
return mRemoved;
}
- public void setRemoved(boolean removed) {
- mRemoved = removed;
+ public void setRemoved() {
+ mRemoved = true;
+
+ mPrivateLayout.setRemoved();
+ if (mChildrenContainer != null) {
+ mChildrenContainer.setRemoved();
+ }
}
public NotificationChildrenContainer getChildrenContainer() {
@@ -709,6 +732,8 @@
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 @@
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 @@
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 @@
mTranslateableViews.get(i).setTranslationX(translationX);
}
}
- setTranslationForOutline(translationX);
+ invalidateOutline();
if (mSettingsIconRow != null) {
mSettingsIconRow.updateSettingsIcons(translationX, getMeasuredWidth());
}
@@ -867,48 +887,29 @@
// 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;
-
- @Override
- public void onAnimationCancel(Animator anim) {
- cancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator anim) {
- if (!cancelled && mSettingsIconRow != null && leftTarget == 0) {
- mSettingsIconRow.resetState();
- mTranslateAnim = null;
- }
- }
- });
- }
- set.play(translateAnim);
- }
+ final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
+ leftTarget);
+ if (listener != null) {
+ translateAnim.addUpdateListener(listener);
}
- mTranslateAnim = set;
- return set;
+ translateAnim.addListener(new AnimatorListenerAdapter() {
+ boolean cancelled = false;
+
+ @Override
+ public void onAnimationCancel(Animator anim) {
+ cancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator anim) {
+ if (!cancelled && mSettingsIconRow != null && leftTarget == 0) {
+ mSettingsIconRow.resetState();
+ mTranslateAnim = null;
+ }
+ }
+ });
+ mTranslateAnim = translateAnim;
+ return translateAnim;
}
public float getSpaceForGear() {
@@ -937,9 +938,8 @@
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 @@
}
if (mIsSummaryWithChildren) {
mChildrenContainer.setDark(dark, fade, delay);
- mNotificationHeaderWrapper.setDark(dark, fade, delay);
}
}
@@ -1044,6 +1043,9 @@
mPrivateLayout.setUserExpanding(userLocked);
if (mIsSummaryWithChildren) {
mChildrenContainer.setUserLocked(userLocked);
+ if (userLocked) {
+ updateBackgroundForGroupState();
+ }
}
}
@@ -1123,30 +1125,28 @@
}
}
- 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 @@
}
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 void setChildrenExpanded(boolean expanded, boolean animate) {
mChildrenExpanded = expanded;
- if (mNotificationHeader != null) {
- mNotificationHeader.setExpanded(expanded);
- }
if (mChildrenContainer != null) {
mChildrenContainer.setChildrenExpanded(expanded);
}
@@ -1349,6 +1346,14 @@
}
@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 @@
}
}
- 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 @@
@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();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index f98e87d..9d9f3b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -33,22 +33,25 @@
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 @@
@Override
public int getOutlineTranslation() {
- return mCustomOutline ? mOutlineRect.left : 0;
+ return mCustomOutline ? mOutlineRect.left : (int) getTranslation();
+ }
+
+ public void updateOutline() {
+ if (mCustomOutline) {
+ return;
+ }
+ boolean hasOutline = true;
+ if (isChildInGroup()) {
+ hasOutline = isGroupExpanded() && !isGroupExpansionChanging();
+ } else if (isSummaryWithChildren()) {
+ hasOutline = !isGroupExpanded() || isGroupExpansionChanging();
+ }
+ setOutlineProvider(hasOutline ? mProvider : null);
+ }
+
+ public boolean isOutlineShowing() {
+ ViewOutlineProvider op = getOutlineProvider();
+ return op != null;
}
protected void setOutlineRect(float left, float top, float right, float bottom) {
- setOutlineRect(true, left, top, right, bottom);
- }
-
- protected void setOutlineRect(boolean clipToOutline, float left, float top, float right,
- float bottom) {
mCustomOutline = true;
- setClipToOutline(clipToOutline);
+ setClipToOutline(true);
mOutlineRect.set((int) left, (int) top, (int) right, (int) bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 280663b..2c302ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -323,7 +323,7 @@
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 @@
}
/**
+ * @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 {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 5a8d4b3..32b61cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -71,7 +71,6 @@
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 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 @@
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 @@
/**
* @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 HybridNotificationView getSingleLineView() {
return mSingleLineView;
}
+
+ public void setRemoved() {
+ if (mExpandedRemoteInput != null) {
+ mExpandedRemoteInput.setRemoved();
+ }
+ if (mHeadsUpRemoteInput != null) {
+ mHeadsUpRemoteInput.setRemoved();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 7575dc8..7f95d48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -90,7 +90,7 @@
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,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 182075f..0df1204 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -215,17 +215,23 @@
public boolean isOnlyChildInSuppressedGroup(StatusBarNotification sbn) {
return isGroupSuppressed(sbn.getGroupKey())
- && isOnlyChildInGroup(sbn);
+ && isOnlyChild(sbn);
}
- public boolean isOnlyChildInGroup(StatusBarNotification sbn) {
+ private boolean isOnlyChild(StatusBarNotification sbn) {
return !sbn.getNotification().isGroupSummary()
&& getTotalNumberOfChildren(sbn) == 1;
}
+ public boolean isOnlyChildInGroup(StatusBarNotification sbn) {
+ return isOnlyChild(sbn) && getLogicalGroupSummary(sbn) != null;
+ }
+
private int getTotalNumberOfChildren(StatusBarNotification sbn) {
- return getNumberOfIsolatedChildren(sbn.getGroupKey())
- + mGroupMap.get(sbn.getGroupKey()).children.size();
+ int isolatedChildren = getNumberOfIsolatedChildren(sbn.getGroupKey());
+ NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
+ int realChildren = group != null ? group.children.size() : 0;
+ return isolatedChildren + realChildren;
}
private boolean isGroupSuppressed(String groupKey) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 9dbec6d..6a37099 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -39,6 +39,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.TextView;
+
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.AutoReinflateContainer;
@@ -2291,6 +2292,12 @@
setLaunchingAffordance(false);
}
+ @Override
+ public void setAlpha(float alpha) {
+ super.setAlpha(alpha);
+ mNotificationStackScroller.setParentFadingOut(alpha != 1.0f);
+ }
+
/**
* Set whether we are currently launching an affordance. This is currently only set when
* launched via a camera gesture.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e3ce1e2..31fa3bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -263,6 +263,14 @@
* This affects the status bar UI. */
private static final boolean FREEFORM_WINDOW_MANAGEMENT;
+ /**
+ * How long to wait before auto-dismissing a notification that was kept for remote input, and
+ * has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel
+ * these given that they technically don't exist anymore. We wait a bit in case the app issues
+ * an update.
+ */
+ private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200;
+
static {
boolean onlyCoreApps;
boolean freeformWindowManagement;
@@ -1181,16 +1189,24 @@
mIconPolicy.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
- if (FORCE_REMOTE_INPUT_HISTORY) {
- mRemoteInputController.addCallback(new RemoteInputController.Callback() {
- @Override
- public void onRemoteInputSent(Entry entry) {
- if (mKeysKeptForRemoteInput.contains(entry.key)) {
- removeNotification(entry.key, null);
- }
+ mRemoteInputController.addCallback(new RemoteInputController.Callback() {
+ @Override
+ public void onRemoteInputSent(Entry entry) {
+ if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(entry.key)) {
+ removeNotification(entry.key, null);
+ } else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) {
+ // We're currently holding onto this notification, but from the apps point of
+ // view it is already canceled, so we'll need to cancel it on the apps behalf
+ // after sending - unless the app posts an update in the mean time, so wait a
+ // bit.
+ mHandler.postDelayed(() -> {
+ if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) {
+ removeNotification(entry.key, null);
+ }
+ }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
}
- });
- }
+ }
+ });
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
@@ -1507,8 +1523,15 @@
return;
}
Entry entry = mNotificationData.get(key);
+
+ if (entry != null && mRemoteInputController.isRemoteInputActive(entry)) {
+ mLatestRankingMap = ranking;
+ mRemoteInputEntriesToRemoveOnCollapse.add(entry);
+ return;
+ }
+
if (entry != null && entry.row != null) {
- entry.row.setRemoved(true);
+ entry.row.setRemoved();
}
// Let's remove the children if this was a summary
handleGroupSummaryRemoved(key, ranking);
@@ -1556,7 +1579,7 @@
toRemove.get(i).setKeepInParent(true);
// we need to set this state earlier as otherwise we might generate some weird
// animations
- toRemove.get(i).setRemoved(true);
+ toRemove.get(i).setRemoved();
}
for (int i = 0; i < toRemove.size(); i++) {
removeNotification(toRemove.get(i).getStatusBarNotification().getKey(), ranking);
@@ -1568,6 +1591,15 @@
}
@Override
+ protected void performRemoveNotification(StatusBarNotification n, boolean removeView) {
+ Entry entry = mNotificationData.get(n.getKey());
+ if (mRemoteInputController.isRemoteInputActive(entry)) {
+ mRemoteInputController.removeRemoteInput(entry);
+ }
+ super.performRemoveNotification(n, removeView);
+ }
+
+ @Override
protected void refreshLayout(int layoutDirection) {
if (mNavigationBarView != null) {
mNavigationBarView.setLayoutDirection(layoutDirection);
@@ -2464,6 +2496,26 @@
public void setPanelExpanded(boolean isExpanded) {
mStatusBarWindowManager.setPanelExpanded(isExpanded);
+
+ if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
+ if (DEBUG) {
+ Log.v(TAG, "clearing notification effects from setPanelExpanded");
+ }
+ clearNotificationEffects();
+ }
+
+ if (!isExpanded) {
+ removeRemoteInputEntriesKeptUntilCollapsed();
+ }
+ }
+
+ private void removeRemoteInputEntriesKeptUntilCollapsed() {
+ for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) {
+ Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i);
+ mRemoteInputController.removeRemoteInput(entry);
+ removeNotification(entry.key, mLatestRankingMap);
+ }
+ mRemoteInputEntriesToRemoveOnCollapse.clear();
}
public void onScreenTurnedOff() {
@@ -3865,6 +3917,7 @@
mScrimController.forceHideScrims(true /* hide */);
updateMediaMetaData(false, true);
mNotificationPanel.setAlpha(1);
+ mStackScroller.setParentFadingOut(true);
mNotificationPanel.animate()
.alpha(0)
.setStartDelay(FADE_KEYGUARD_START_DELAY)
@@ -4206,6 +4259,9 @@
|| (state == StatusBarState.SHADE && isGoingToNotificationShade()))) {
clearNotificationEffects();
}
+ if (state == StatusBarState.KEYGUARD) {
+ removeRemoteInputEntriesKeptUntilCollapsed();
+ }
mState = state;
mGroupManager.setStatusBarState(state);
mFalsingManager.setStatusBarState(state);
@@ -4334,6 +4390,9 @@
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();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 493b23f..29b4db1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -211,7 +211,7 @@
public void animateToggleQSExpansion() {
// TODO: Better path to animated panel expansion.
- mHeader.performClick();
+ mHeader.callOnClick();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index eae0b8e..9c4480e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -322,6 +322,7 @@
if (scrim instanceof ScrimView) {
float alpha2 = getDozeAlpha(scrim);
float alpha = 1 - (1 - alpha1) * (1 - alpha2);
+ alpha = Math.max(0, Math.min(1.0f, alpha));
((ScrimView) scrim).setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0));
} else {
scrim.setAlpha(alpha1);
@@ -468,6 +469,7 @@
PropertyValuesHolder[] values = previousAnimator.getValues();
float relativeDiff = alpha - previousEndValue;
float newStartValue = previousStartValue + relativeDiff;
+ newStartValue = Math.max(0, Math.min(1.0f, newStartValue));
values[0].setFloatValues(newStartValue, alpha);
scrim.setTag(TAG_START_ALPHA, newStartValue);
scrim.setTag(TAG_END_ALPHA, alpha);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index ddded49..21d03fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -53,7 +53,7 @@
public void showMirror() {
mBrightnessMirror.setVisibility(View.VISIBLE);
- mStackScroller.setFadedOut(true);
+ mStackScroller.setFadingOut(true);
mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, Interpolators.ALPHA_OUT);
outAnimation(mNotificationPanel.animate())
.withLayer();
@@ -67,7 +67,7 @@
@Override
public void run() {
mBrightnessMirror.setVisibility(View.INVISIBLE);
- mStackScroller.setFadedOut(false);
+ mStackScroller.setFadingOut(false);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 095265a..2f522f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,6 +73,7 @@
private ScrollContainer mScrollContainer;
private View mScrollContainerChild;
+ private boolean mRemoved;
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -171,7 +172,12 @@
public void onDefocus() {
mController.removeRemoteInput(mEntry);
mEntry.remoteInputText = mEditText.getText();
- setVisibility(INVISIBLE);
+
+ // During removal, we get reattached and lose focus. Not hiding in that
+ // case to prevent flicker.
+ if (!mRemoved) {
+ setVisibility(INVISIBLE);
+ }
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
mEntry.notification.getPackageName());
}
@@ -347,6 +353,10 @@
return mPendingIntent;
}
+ public void setRemoved() {
+ mRemoved = true;
+ }
+
/**
* An EditText that changes appearance based on whether it's focusable and becomes
* un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -416,6 +426,15 @@
}
@Override
+ public boolean onCheckIsTextEditor() {
+ // Stop being editable while we're being removed. During removal, we get reattached,
+ // and editable views get their spellchecking state re-evaluated which is too costly
+ // during the removal animation.
+ boolean flyingOut = mRemoteInputView != null && mRemoteInputView.mRemoved;
+ return !flyingOut && super.onCheckIsTextEditor();
+ }
+
+ @Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index e919d5c..cb0b848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -16,21 +16,28 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
state.applyViewState(mOverflowNumber, mGroupOverFlowState);
mNeverAppliedGroupState = false;
}
+ if (mNotificationHeader != null) {
+ state.applyViewState(mNotificationHeader, mHeaderViewState);
+ }
}
/**
@@ -491,6 +611,8 @@
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 @@
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 @@
stateAnimator.startViewAnimations(mOverflowNumber, mGroupOverFlowState,
baseDelay, duration);
}
+ if (mNotificationHeader != null) {
+ state.applyViewState(mNotificationHeader, mHeaderViewState);
+ }
}
public ExpandableNotificationRow getViewAtPosition(float y) {
@@ -541,14 +667,45 @@
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 @@
}
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 @@
}
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 @@
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 @@
mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
mNotificationParent.getNotificationColor());
}
+
+ public void setRemoved() {
+ int childCount = mChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ ExpandableNotificationRow child = mChildren.get(i);
+ child.setRemoved();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 70d4aec..4c242cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -34,6 +34,7 @@
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.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;
@@ -333,8 +336,10 @@
private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
private boolean mPulsing;
private boolean mDrawBackgroundAsSrc;
- private boolean mFadedOut;
+ 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 =
@@ -442,7 +447,6 @@
private void initView(Context context) {
mScroller = new OverScroller(getContext());
- setFocusable(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setClipChildren(false);
final ViewConfiguration configuration = ViewConfiguration.get(context);
@@ -470,7 +474,8 @@
}
private void updateSrcDrawing() {
- mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadedOut ? mSrcMode : null);
+ mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && (!mFadingOut && !mParentFadingOut)
+ ? mSrcMode : null);
invalidate();
}
@@ -728,6 +733,7 @@
mQsContainer = qsContainer;
}
+ @Override
public void onChildDismissed(View v) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
if (!row.isDismissed()) {
@@ -815,6 +821,7 @@
return true; // Don't fade out the notification
}
+ @Override
public void onBeginDrag(View v) {
mFalsingManager.onNotificatonStartDismissing();
setSwipingInProgress(true);
@@ -843,6 +850,7 @@
return false;
}
+ @Override
public void onDragCancelled(View v) {
mFalsingManager.onNotificatonStopDismissing();
setSwipingInProgress(false);
@@ -901,11 +909,13 @@
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();
@@ -942,12 +952,14 @@
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,
@@ -955,6 +967,14 @@
}
}
+ @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);
@@ -1065,6 +1085,7 @@
return mScrollingEnabled;
}
+ @Override
public boolean canChildBeDismissed(View v) {
return StackScrollAlgorithm.canChildBeDismissed(v);
}
@@ -1726,6 +1747,15 @@
}
}
mContentHeight = height + mTopPadding;
+ updateScrollability();
+ }
+
+ private void updateScrollability() {
+ boolean scrollable = getScrollRange() > 0;
+ if (scrollable != mScrollable) {
+ mScrollable = scrollable;
+ setFocusable(scrollable);
+ }
}
private void updateBackground() {
@@ -1871,7 +1901,7 @@
}
private void applyCurrentBackgroundBounds() {
- if (!mFadedOut) {
+ if (!mFadingOut) {
mScrimController.setExcludedBackgroundArea(mCurrentBounds);
}
invalidate();
@@ -1913,7 +1943,8 @@
// 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 {
@@ -2927,16 +2958,6 @@
if (changed) {
if (!mIsExpanded) {
mGroupManager.collapseAllGroups();
- } else {
- // XXX: HACK: we should not be clearing notification effects from way down here.
- // But at the moment we don't have a reliable way to know when the window is
- // actually exposed to the air, so
- if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) {
- if (DEBUG) {
- Log.v(TAG, "clearing notification effects from scroller");
- }
- mPhoneStatusBar.clearNotificationEffects();
- }
}
updateNotificationAnimationStates();
updateChronometers();
@@ -3139,8 +3160,9 @@
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());
}
@@ -3511,6 +3533,12 @@
if (!mGroupExpandedForMeasure) {
onHeightChanged(changedRow, false /* needsAnimation */);
}
+ runAfterAnimationFinished(new Runnable() {
+ @Override
+ public void run() {
+ changedRow.onFinishedExpansionChange();
+ }
+ });
}
@Override
@@ -3518,6 +3546,68 @@
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();
@@ -3610,22 +3700,33 @@
updateNotificationAnimationStates();
}
- public void setFadedOut(boolean fadingOut) {
- if (fadingOut != mFadedOut) {
- mFadedOut = fadingOut;
- if (fadingOut) {
- mScrimController.setExcludedBackgroundArea(null);
- } else {
- applyCurrentBackgroundBounds();
- }
- updateSrcDrawing();
+ public void setFadingOut(boolean fadingOut) {
+ if (fadingOut != mFadingOut) {
+ mFadingOut = fadingOut;
+ updateFadingState();
}
}
+ public void setParentFadingOut(boolean fadingOut) {
+ if (fadingOut != mParentFadingOut) {
+ mParentFadingOut = fadingOut;
+ updateFadingState();
+ }
+ }
+
+ private void updateFadingState() {
+ if (mFadingOut || mParentFadingOut || mAmbientState.isDark()) {
+ mScrimController.setExcludedBackgroundArea(null);
+ } else {
+ applyCurrentBackgroundBounds();
+ }
+ updateSrcDrawing();
+ }
+
@Override
public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
super.setAlpha(alpha);
- setFadedOut(alpha != 1.0f);
+ setFadingOut(alpha != 1.0f);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 1ccc908..d6c5506 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -173,6 +173,9 @@
}
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (state.isBottomClipped) {
+ row.setClipToActualHeight(true);
+ }
row.applyChildrenState(this);
}
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 6f451ff..0f94227 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -223,7 +223,7 @@
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 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 @@
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);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
index 1114e03..ecdee4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
@@ -57,6 +57,11 @@
*/
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 @@
clipTopAmount = svs.clipTopAmount;
notGoneIndex = svs.notGoneIndex;
location = svs.location;
+ isBottomClipped = svs.isBottomClipped;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index 74f0cd3..476598d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -227,6 +227,13 @@
}
/**
+ * Updates the PIP per configuration changed.
+ */
+ void onConfigurationChanged() {
+ mPipRecentsOverlayManager.onConfigurationChanged(mContext);
+ }
+
+ /**
* Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
*/
public void showTvPictureInPictureMenu() {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
index fe5d8bc..6e4a593 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -42,9 +42,9 @@
private final PipManager mPipManager = PipManager.getInstance();
private final WindowManager mWindowManager;
- private final View mOverlayView;
- private final PipRecentsControlsView mPipControlsView;
- private final View mRecentsView;
+ private View mOverlayView;
+ private PipRecentsControlsView mPipControlsView;
+ private View mRecentsView;
private final LayoutParams mPipRecentsControlsViewLayoutParams;
private final LayoutParams mPipRecentsControlsViewFocusedLayoutParams;
@@ -73,6 +73,21 @@
PipRecentsOverlayManager(Context context) {
mWindowManager = (WindowManager) context.getSystemService(WindowManager.class);
+ mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ LayoutParams.TYPE_SYSTEM_DIALOG,
+ LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT);
+ mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ LayoutParams.TYPE_SYSTEM_DIALOG,
+ 0,
+ PixelFormat.TRANSLUCENT);
+
+ initViews(context);
+ }
+
+ private void initViews(Context context) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mOverlayView = inflater.inflate(R.layout.tv_pip_recents_overlay, null);
@@ -86,17 +101,6 @@
}
}
});
-
- mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- LayoutParams.TYPE_SYSTEM_DIALOG,
- LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
- PixelFormat.TRANSLUCENT);
- mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- LayoutParams.TYPE_SYSTEM_DIALOG,
- 0,
- PixelFormat.TRANSLUCENT);
}
/**
@@ -210,4 +214,14 @@
boolean isRecentsShown() {
return mIsRecentsShown;
}
+
+ /**
+ * Updates the PIP per configuration changed.
+ */
+ void onConfigurationChanged(Context context) {
+ if (mIsRecentsShown) {
+ Log.w(TAG, "Configuration is changed while Recents is shown");
+ }
+ initViews(context);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
index 182b9b0..b3e9f43 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
@@ -48,6 +48,6 @@
if (!mSupportPip) {
return;
}
- // TODO: handle configuration change.
+ PipManager.getInstance().onConfigurationChanged();
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
index 3f95427..de2c58c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
@@ -44,4 +44,19 @@
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);
+ }
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index ac2dda3..4e0ddd6 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -158,12 +158,6 @@
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 @@
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 @@
* 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 @@
}
}
- private void onUserUnlocked(int userId) {
+ void onUserUnlocked(int userId) {
if (isProfileWithLockedParent(userId)) {
return;
}
@@ -3072,7 +3059,7 @@
return new AtomicFile(settingsFile);
}
- private void onUserStopped(int userId) {
+ void onUserStopped(int userId) {
synchronized (mLock) {
boolean crossProfileWidgetsChanged = false;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 1edc9b1..8ad6e6a 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -125,6 +125,8 @@
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 @@
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 @@
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 @@
while (true)
{
int result = waitForAlarm(mNativeData);
+ mLastWakeup = SystemClock.elapsedRealtime();
triggerList.clear();
@@ -2536,6 +2543,11 @@
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();
}
}
}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index ca3c39f..dab7d70 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1316,13 +1316,14 @@
// For each client, check that the given op is not restricted, or that the given
// package is exempt from the restriction.
ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
- if (restrictionState.hasRestriction(code, packageName, userHandle)
- && AppOpsManager.opAllowSystemBypassRestriction(code)) {
- // If we are the system, bypass user restrictions for certain codes
- synchronized (this) {
- Ops ops = getOpsRawLocked(uid, packageName, true);
- if ((ops != null) && ops.isPrivileged) {
- return false;
+ if (restrictionState.hasRestriction(code, packageName, userHandle)) {
+ if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
+ // If we are the system, bypass user restrictions for certain codes
+ synchronized (this) {
+ Ops ops = getOpsRawLocked(uid, packageName, true);
+ if ((ops != null) && ops.isPrivileged) {
+ return false;
+ }
}
}
return true;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 5e8687a..f30a126 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -52,6 +52,7 @@
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -126,6 +127,8 @@
private IBinder mBluetoothBinder;
private IBluetooth mBluetooth;
private IBluetoothGatt mBluetoothGatt;
+ private final ReentrantReadWriteLock mBluetoothLock =
+ new ReentrantReadWriteLock();
private boolean mBinding;
private boolean mUnbinding;
// used inside handler thread
@@ -190,12 +193,15 @@
}
int st = BluetoothAdapter.STATE_OFF;
- if (mBluetooth != null) {
- try {
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) {
st = mBluetooth.getState();
- } catch (RemoteException e) {
- Slog.e(TAG,"Unable to call getState", e);
}
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call getState", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
Slog.d(TAG, "state" + st);
@@ -208,12 +214,15 @@
if (st == BluetoothAdapter.STATE_BLE_ON) {
//if state is BLE_ON make sure you trigger disableBLE part
try {
+ mBluetoothLock.readLock().lock();
if (mBluetooth != null) {
mBluetooth.onBrEdrDown();
mEnableExternal = false;
}
- } catch(RemoteException e) {
+ } catch (RemoteException e) {
Slog.e(TAG,"Unable to call onBrEdrDown", e);
+ } finally {
+ mBluetoothLock.readLock().lock();
}
} else if (st == BluetoothAdapter.STATE_ON){
// disable without persisting the setting
@@ -366,9 +375,8 @@
Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
msg.obj = callback;
mHandler.sendMessage(msg);
- synchronized(mConnection) {
- return mBluetooth;
- }
+
+ return mBluetooth;
}
public void unregisterAdapter(IBluetoothManagerCallback callback) {
@@ -406,12 +414,13 @@
return false;
}
- synchronized(mConnection) {
- try {
- return (mBluetooth != null && mBluetooth.isEnabled());
- } catch (RemoteException e) {
- Slog.e(TAG, "isEnabled()", e);
- }
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) return mBluetooth.isEnabled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "isEnabled()", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
return false;
}
@@ -424,11 +433,14 @@
if (mBleAppCount == 0) {
if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
try {
+ mBluetoothLock.readLock().lock();
if (mBluetooth != null) {
mBluetooth.onBrEdrDown();
}
- } catch(RemoteException e) {
+ } catch (RemoteException e) {
Slog.e(TAG,"Unable to call onBrEdrDown", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
}
}
@@ -456,9 +468,12 @@
disableBleScanMode();
clearBleApps();
try {
+ mBluetoothLock.readLock().lock();
if (mBluetooth != null) mBluetooth.onBrEdrDown();
} catch (RemoteException e) {
Slog.e(TAG, "error when disabling bluetooth", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
}
}
@@ -472,12 +487,15 @@
// Disable ble scan only mode.
private void disableBleScanMode() {
try {
+ mBluetoothLock.writeLock().lock();
if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
if (DBG) Slog.d(TAG, "Reseting the mEnable flag for clean disable");
mEnable = false;
}
} catch (RemoteException e) {
Slog.e(TAG, "getState()", e);
+ } finally {
+ mBluetoothLock.writeLock().unlock();
}
}
@@ -536,7 +554,8 @@
*/
private void onBluetoothGattServiceUp() {
if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up");
- try{
+ try {
+ mBluetoothLock.readLock().lock();
if (isBleAppPresent() == false && mBluetooth != null
&& mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
mBluetooth.onLeServiceUp();
@@ -546,8 +565,10 @@
persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
Binder.restoreCallingIdentity(callingIdentity);
}
- } catch(RemoteException e) {
- Slog.e(TAG,"Unable to call onServiceUp", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG,"Unable to call onServiceUp", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
}
@@ -558,22 +579,25 @@
private void sendBrEdrDownCallback() {
if (DBG) Slog.d(TAG,"Calling sendBrEdrDownCallback callbacks");
- if(mBluetooth == null) {
+ if (mBluetooth == null) {
Slog.w(TAG, "Bluetooth handle is null");
return;
}
if (isBleAppPresent() == false) {
try {
- mBluetooth.onBrEdrDown();
- } catch(RemoteException e) {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) mBluetooth.onBrEdrDown();
+ } catch (RemoteException e) {
Slog.e(TAG, "Call to onBrEdrDown() failed.", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
} else {
// Need to stay at BLE ON. Disconnect all Gatt connections
- try{
+ try {
mBluetoothGatt.unregAll();
- } catch(RemoteException e) {
+ } catch (RemoteException e) {
Slog.e(TAG, "Unable to disconnect all apps.", e);
}
}
@@ -673,7 +697,8 @@
" mBinding = " + mBinding);
}
- synchronized (mConnection) {
+ try {
+ mBluetoothLock.writeLock().lock();
if (mUnbinding) return;
mUnbinding = true;
if (mBluetooth != null) {
@@ -695,6 +720,8 @@
mUnbinding=false;
}
mBluetoothGatt = null;
+ } finally {
+ mBluetoothLock.writeLock().unlock();
}
}
@@ -1010,14 +1037,13 @@
return BluetoothAdapter.DEFAULT_MAC_ADDRESS;
}
- synchronized(mConnection) {
- if (mBluetooth != null) {
- try {
- return mBluetooth.getAddress();
- } catch (RemoteException e) {
- Slog.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e);
- }
- }
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) return mBluetooth.getAddress();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getAddress(): Unable to retrieve address remotely. Returning cached address", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
// mAddress is accessed from outside.
@@ -1036,15 +1062,15 @@
return null;
}
- synchronized(mConnection) {
- if (mBluetooth != null) {
- try {
- return mBluetooth.getName();
- } catch (RemoteException e) {
- Slog.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e);
- }
- }
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) return mBluetooth.getName();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
+
// mName is accessed from outside.
// It alright without a lock. Here, bluetooth is off, no other thread is
// changing mName
@@ -1101,7 +1127,8 @@
switch (msg.what) {
case MESSAGE_GET_NAME_AND_ADDRESS:
if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
- synchronized(mConnection) {
+ try {
+ mBluetoothLock.writeLock().lock();
if ((mBluetooth == null) && (!mBinding)) {
if (DBG) Slog.d(TAG, "Binding to service to get name and address");
mGetNameAddressOnly = true;
@@ -1127,6 +1154,8 @@
}
mGetNameAddressOnly = false;
}
+ } finally {
+ mBluetoothLock.writeLock().unlock();
}
break;
@@ -1209,7 +1238,8 @@
if (DBG) Slog.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
IBinder service = (IBinder) msg.obj;
- synchronized(mConnection) {
+ try {
+ mBluetoothLock.writeLock().lock();
if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
onBluetoothGattServiceUp();
@@ -1264,6 +1294,8 @@
} catch (RemoteException e) {
Slog.e(TAG,"Unable to call enable()",e);
}
+ } finally {
+ mBluetoothLock.writeLock().unlock();
}
if (!mEnable) {
@@ -1275,9 +1307,10 @@
}
case MESSAGE_TIMEOUT_BIND: {
Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
- synchronized(mConnection) {
- mBinding = false;
- }
+ mBluetoothLock.writeLock().lock();
+ mBinding = false;
+ mBluetoothLock.writeLock().unlock();
+
break;
}
case MESSAGE_BLUETOOTH_STATE_CHANGE:
@@ -1312,7 +1345,8 @@
case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
{
Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
- synchronized(mConnection) {
+ try {
+ mBluetoothLock.writeLock().lock();
if (msg.arg1 == SERVICE_IBLUETOOTH) {
// if service is unbinded already, do nothing and return
if (mBluetooth == null) break;
@@ -1324,6 +1358,8 @@
Slog.e(TAG, "Bad msg.arg1: " + msg.arg1);
break;
}
+ } finally {
+ mBluetoothLock.writeLock().unlock();
}
if (mEnable) {
@@ -1369,9 +1405,9 @@
case MESSAGE_TIMEOUT_UNBIND:
{
Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
- synchronized(mConnection) {
- mUnbinding = false;
- }
+ mBluetoothLock.writeLock().lock();
+ mUnbinding = false;
+ mBluetoothLock.writeLock().unlock();
break;
}
@@ -1381,15 +1417,15 @@
/* disable and enable BT when detect a user switch */
if (mEnable && mBluetooth != null) {
- synchronized (mConnection) {
+ try {
+ mBluetoothLock.readLock().lock();
if (mBluetooth != null) {
- //Unregister callback object
- try {
- mBluetooth.unregisterCallback(mBluetoothCallback);
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to unregister",re);
- }
+ mBluetooth.unregisterCallback(mBluetoothCallback);
}
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to unregister", re);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
@@ -1420,14 +1456,16 @@
bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
BluetoothAdapter.STATE_OFF);
sendBluetoothServiceDownCallback();
- synchronized (mConnection) {
- if (mBluetooth != null) {
- mBluetooth = null;
- //Unbind
- mContext.unbindService(mConnection);
- }
- mBluetoothGatt = null;
+
+ mBluetoothLock.writeLock().lock();
+ if (mBluetooth != null) {
+ mBluetooth = null;
+ // Unbind
+ mContext.unbindService(mConnection);
}
+ mBluetoothGatt = null;
+ mBluetoothLock.writeLock().unlock();
+
SystemClock.sleep(100);
mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
@@ -1450,14 +1488,12 @@
if (DBG) Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
mHandler.removeMessages(MESSAGE_USER_SWITCHED);
- synchronized (mConnection) {
- if (mEnable && !mBinding && (mBluetooth == null)) {
- // We should be connected, but we gave up for some
- // reason; maybe the Bluetooth service wasn't encryption
- // aware, so try binding again.
- if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock");
- handleEnable(mQuietEnable);
- }
+ if (mEnable && !mBinding && (mBluetooth == null)) {
+ // We should be connected, but we gave up for some
+ // reason; maybe the Bluetooth service wasn't encryption
+ // aware, so try binding again.
+ if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock");
+ handleEnable(mQuietEnable);
}
}
}
@@ -1467,7 +1503,8 @@
private void handleEnable(boolean quietMode) {
mQuietEnable = quietMode;
- synchronized(mConnection) {
+ try {
+ mBluetoothLock.writeLock().lock();
if ((mBluetooth == null) && (!mBinding)) {
//Start bind timeout and bind
Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
@@ -1496,6 +1533,8 @@
Slog.e(TAG,"Unable to call enable()",e);
}
}
+ } finally {
+ mBluetoothLock.writeLock().unlock();
}
}
@@ -1510,18 +1549,18 @@
}
private void handleDisable() {
- synchronized(mConnection) {
+ try {
+ mBluetoothLock.readLock().lock();
if (mBluetooth != null) {
if (DBG) Slog.d(TAG,"Sending off request.");
-
- try {
- if(!mBluetooth.disable()) {
- Slog.e(TAG,"IBluetooth.disable() returned false");
- }
- } catch (RemoteException e) {
- Slog.e(TAG,"Unable to call disable()",e);
+ if (!mBluetooth.disable()) {
+ Slog.e(TAG,"IBluetooth.disable() returned false");
}
}
+ } catch (RemoteException e) {
+ Slog.e(TAG,"Unable to call disable()",e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
}
@@ -1648,20 +1687,21 @@
private boolean waitForOnOff(boolean on, boolean off) {
int i = 0;
while (i < 10) {
- synchronized(mConnection) {
- try {
- if (mBluetooth == null) break;
- if (on) {
- if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
- } else if (off) {
- if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
- } else {
- if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "getState()", e);
- break;
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth == null) break;
+ if (on) {
+ if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
+ } else if (off) {
+ if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
+ } else {
+ if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
}
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getState()", e);
+ break;
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
if (on || off) {
SystemClock.sleep(300);
@@ -1684,34 +1724,36 @@
}
private boolean canUnbindBluetoothService() {
- synchronized(mConnection) {
+ try {
//Only unbind with mEnable flag not set
//For race condition: disable and enable back-to-back
//Avoid unbind right after enable due to callback from disable
//Only unbind with Bluetooth at OFF state
//Only unbind without any MESSAGE_BLUETOOTH_STATE_CHANGE message
- try {
- if (mEnable || (mBluetooth == null)) return false;
- if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false;
- return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF);
- } catch (RemoteException e) {
- Slog.e(TAG, "getState()", e);
- }
+ mBluetoothLock.readLock().lock();
+ if (mEnable || (mBluetooth == null)) return false;
+ if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false;
+ return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getState()", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
return false;
}
private void recoverBluetoothServiceFromError() {
Slog.e(TAG,"recoverBluetoothServiceFromError");
- synchronized (mConnection) {
+ try {
+ mBluetoothLock.readLock().lock();
if (mBluetooth != null) {
//Unregister callback object
- try {
- mBluetooth.unregisterCallback(mBluetoothCallback);
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to unregister",re);
- }
+ mBluetooth.unregisterCallback(mBluetoothCallback);
}
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to unregister", re);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
SystemClock.sleep(500);
@@ -1722,14 +1764,15 @@
waitForOnOff(false, true);
sendBluetoothServiceDownCallback();
- synchronized (mConnection) {
- if (mBluetooth != null) {
- mBluetooth = null;
- //Unbind
- mContext.unbindService(mConnection);
- }
- mBluetoothGatt = null;
+
+ mBluetoothLock.writeLock().lock();
+ if (mBluetooth != null) {
+ mBluetooth = null;
+ // Unbind
+ mContext.unbindService(mConnection);
}
+ mBluetoothGatt = null;
+ mBluetoothLock.writeLock().unlock();
mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
mState = BluetoothAdapter.STATE_OFF;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c096fa5f..95a9781 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4066,7 +4066,8 @@
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);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3194d89..2017e48 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -272,6 +272,7 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
@@ -12822,12 +12823,18 @@
com.android.internal.R.dimen.thumbnail_width);
mThumbnailHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_height);
- mFullscreenThumbnailScale = res.getFraction(
- com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
com.android.internal.R.string.config_defaultPictureInPictureBounds));
mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
com.android.internal.R.string.config_appsNotReportingCrashes));
+ if ((mConfiguration.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
+ mFullscreenThumbnailScale = (float) res
+ .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
+ (float) mConfiguration.screenWidthDp;
+ } else {
+ mFullscreenThumbnailScale = res.getFraction(
+ com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
+ }
}
}
@@ -16856,6 +16863,13 @@
return false;
}
+ // If the app is a regular app (uid >= 10000) and not the system server or phone
+ // process, etc, then mark it as being in full backup so that certain calls to the
+ // process can be blocked. This is not reset to false anywhere because we kill the
+ // process after the full backup is done and the ProcessRecord will vaporize anyway.
+ if (UserHandle.isApp(app.uid) && backupMode == IApplicationThread.BACKUP_MODE_FULL) {
+ proc.inFullBackup = true;
+ }
r.app = proc;
mBackupTarget = r;
mBackupAppName = app.packageName;
@@ -18318,21 +18332,21 @@
EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
- final Locale locale;
- if (values.getLocales().size() == 1) {
- // This is an optimization to avoid the JNI call when the result of
- // getFirstMatch() does not depend on the supported locales.
- locale = values.getLocales().get(0);
- } else {
+ final LocaleList locales = values.getLocales();
+ int bestLocaleIndex = 0;
+ if (locales.size() > 1) {
if (mSupportedSystemLocales == null) {
mSupportedSystemLocales =
Resources.getSystem().getAssets().getLocales();
}
- locale = values.getLocales().getFirstMatch(mSupportedSystemLocales);
+ bestLocaleIndex = Math.max(0,
+ locales.getFirstMatchIndex(mSupportedSystemLocales));
}
- SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
+ SystemProperties.set("persist.sys.locale",
+ locales.get(bestLocaleIndex).toLanguageTag());
+ LocaleList.setDefault(locales, bestLocaleIndex);
mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
- locale));
+ locales.get(bestLocaleIndex)));
}
mConfigurationSeq++;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 3811b61..8b637e0 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1819,22 +1819,24 @@
private boolean shouldBeVisible(ActivityRecord r, boolean behindTranslucentActivity,
boolean stackVisibleBehind, ActivityRecord visibleBehind,
boolean behindFullscreenActivity) {
- // mLaunchingBehind: Activities launching behind are at the back of the task stack
- // but must be drawn initially for the animation as though they were visible.
- final boolean activityVisibleBehind =
- (behindTranslucentActivity || stackVisibleBehind) && visibleBehind == r;
if (!okToShowLocked(r)) {
return false;
}
+ // mLaunchingBehind: Activities launching behind are at the back of the task stack
+ // but must be drawn initially for the animation as though they were visible.
+ final boolean activityVisibleBehind =
+ (behindTranslucentActivity || stackVisibleBehind) && visibleBehind == r;
+
boolean isVisible =
!behindFullscreenActivity || r.mLaunchTaskBehind || activityVisibleBehind;
if (isVisible && r.isRecentsActivity()) {
- // Recents activity can only be visible if the home stack isn't fullscreen or is the
- // focused stack.
- isVisible = !mFullscreen || mStackSupervisor.isFocusedStack(this);
+ // Recents activity can only be visible if the home stack is the focused stack or we are
+ // in split-screen mode.
+ isVisible = mStackSupervisor.getStack(DOCKED_STACK_ID) != null
+ || mStackSupervisor.isFocusedStack(this);
}
return isVisible;
@@ -2256,6 +2258,20 @@
}
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
+ } else if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
+ mStackSupervisor.allResumedActivitiesComplete()) {
+ // It is possible for the activity to be resumed when we paused back stacks above if the
+ // next activity doesn't have to wait for pause to complete.
+ // So, nothing else to-do except:
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ ActivityOptions.abort(options);
+ if (DEBUG_STATES) Slog.d(TAG_STATES,
+ "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return true;
}
// If the most recent activity was noHistory but was only stopped rather
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 0acc2a0..8ffc6f3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -258,6 +258,11 @@
if (app.thread == null) {
throw new RemoteException();
}
+ if (app.inFullBackup) {
+ skipReceiverLocked(r);
+ return;
+ }
+
r.receiver = app.thread.asBinder();
r.curApp = app;
app.curReceiver = r;
@@ -341,13 +346,17 @@
}
if (r != null) {
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
+ skipReceiverLocked(r);
}
}
+ private void skipReceiverLocked(BroadcastRecord r) {
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+ }
+
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
@@ -641,9 +650,17 @@
try {
if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
"Delivering to " + filter + " : " + r);
- performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
- new Intent(r.intent), r.resultCode, r.resultData,
- r.resultExtras, r.ordered, r.initialSticky, r.userId);
+ if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
+ // Skip delivery if full backup in progress
+ // If it's an ordered broadcast, we need to continue to the next receiver.
+ if (ordered) {
+ skipReceiverLocked(r);
+ }
+ } else {
+ performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
+ new Intent(r.intent), r.resultCode, r.resultData,
+ r.resultExtras, r.ordered, r.initialSticky, r.userId);
+ }
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 059acbd..da18f32 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -192,6 +192,9 @@
// app that installed the package.
ComponentName errorReportReceiver;
+ // Process is currently hosting a backup agent for backup or restore
+ public boolean inFullBackup;
+
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0874fa7..7777ae23f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6342,7 +6342,7 @@
mRecordMonitor.unregisterRecordingCallback(rcdb);
}
- public AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
+ public List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
return mRecordMonitor.getActiveRecordingConfigurations();
}
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 7a085e1..57d55de 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
/**
* Class to receive and dispatch updates from AudioSystem about recording configurations.
@@ -54,7 +55,7 @@
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
return;
}
- final AudioRecordingConfiguration[] configs =
+ final List<AudioRecordingConfiguration> configs =
updateSnapshot(event, session, source, recordingInfo);
if (configs != null){
synchronized(mClients) {
@@ -104,9 +105,9 @@
}
}
- AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
+ List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
synchronized(mRecordConfigs) {
- return mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
+ return new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());
}
}
@@ -118,13 +119,13 @@
* @param recordingFormat see
* {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
* for the definition of the contents of the array
- * @return null if the list of active recording sessions has not been modified, an array
+ * @return null if the list of active recording sessions has not been modified, a list
* with the current active configurations otherwise.
*/
- private AudioRecordingConfiguration[] updateSnapshot(int event, int session, int source,
+ private List<AudioRecordingConfiguration> updateSnapshot(int event, int session, int source,
int[] recordingInfo) {
final boolean configChanged;
- final AudioRecordingConfiguration[] configs;
+ final ArrayList<AudioRecordingConfiguration> configs;
synchronized(mRecordConfigs) {
switch (event) {
case AudioManager.RECORD_CONFIG_EVENT_STOP:
@@ -171,7 +172,7 @@
configChanged = false;
}
if (configChanged) {
- configs = mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
+ configs = new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());
} else {
configs = null;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 04be34a..946e4f7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2252,6 +2252,7 @@
.setGroup(newAutoBundleKey)
.setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
.setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+ .setColor(adjustedSbn.getNotification().color)
.build();
summaryNotification.extras.putAll(extras);
Intent appIntent = getContext().getPackageManager()
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 00d7a7b..7b573da 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -384,7 +384,7 @@
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() {
diff --git a/services/core/java/com/android/server/notification/RateEstimator.java b/services/core/java/com/android/server/notification/RateEstimator.java
index 4dc30a4..c17db4a 100644
--- a/services/core/java/com/android/server/notification/RateEstimator.java
+++ b/services/core/java/com/android/server/notification/RateEstimator.java
@@ -25,30 +25,43 @@
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);
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 91de797..ecab009 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -217,7 +217,7 @@
mSystemInterface = systemInterface;
}
- private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
+ private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
// Keeps track of the number of running relro creations
private int mNumRelroCreationsStarted = 0;
@@ -487,6 +487,10 @@
// Either the current relro creation isn't done yet, or the new relro creatioin
// hasn't kicked off yet (the last relro creation used an out-of-date WebView).
webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
+ Slog.e(TAG, "Timed out waiting for relro creation, relros started "
+ + mNumRelroCreationsStarted
+ + " relros finished " + mNumRelroCreationsFinished
+ + " package dirty? " + mWebViewPackageDirty);
}
}
if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 82cd0fa..aaed8ca 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2359,7 +2359,13 @@
// immediately after the enter animation is done. If the app is not yet drawn then
// it will show up as a flicker.
win.mRemoveOnExit = true;
- win.mWindowRemovalAllowed = true;
+ // Request a focus update as this window's input channel is already gone. Otherwise
+ // we could have no focused window in input manager.
+ final boolean focusChanged = updateFocusedWindowLocked(
+ UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
+ if (focusChanged) {
+ mInputMonitor.updateInputWindowsLw(false /*force*/);
+ }
Binder.restoreCallingIdentity(origId);
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e5e74a6..c15afb3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1265,8 +1265,8 @@
* @return true if the window should be considered while evaluating allDrawn flags.
*/
boolean mightAffectAllDrawn(boolean visibleOnly) {
- final boolean isViewVisible = (mViewVisibility == View.VISIBLE)
- && (mAppToken == null || !mAppToken.clientHidden);
+ final boolean isViewVisible = (mAppToken == null || !mAppToken.clientHidden)
+ && (mViewVisibility == View.VISIBLE) && !mWindowRemovalAllowed;
return (isOnScreenIgnoringKeyguard() && (!visibleOnly || isViewVisible)
|| mWinAnimator.mAttrType == TYPE_BASE_APPLICATION)
&& !mAnimatingExit && !mDestroying;
@@ -1822,7 +1822,7 @@
/** @return true if this window desires key events. */
boolean canReceiveKeys() {
return isVisibleOrAdding()
- && (mViewVisibility == View.VISIBLE)
+ && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
&& (mAppToken == null || mAppToken.windowsAreFocusable())
&& !isAdjustedForMinimizedDock();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index aea395d..42eddd5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1334,7 +1334,17 @@
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 @@
// 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 {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e29452a..e8d4c58 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -72,15 +72,18 @@
#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 @@
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 {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a5eb0b2..c1913de 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -484,7 +484,8 @@
}
});
}
- if (Intent.ACTION_BOOT_COMPLETED.equals(action)
+ if (Intent.ACTION_USER_UNLOCKED.equals(action)
+ || Intent.ACTION_USER_STARTED.equals(action)
|| KeyChain.ACTION_STORAGE_CHANGED.equals(action)) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
new MonitoringCertNotificationTask().execute(userId);
@@ -1582,6 +1583,7 @@
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 7079b9e..538e8f8 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -648,14 +648,31 @@
// Here's a basic summary of what the IPv6 filter program does:
//
// if it's not ICMPv6:
+ // if it's multicast and we're dropping multicast:
+ // drop
// pass
// if it's ICMPv6 NA to ff02::1:
// drop
- // If not ICMPv6, pass
gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
- // TODO: Drop multicast if the multicast filter is enabled.
- gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+
+ // Drop multicast if the multicast filter is enabled.
+ if (mMulticastFilter) {
+ // Don't touch ICMPv6 multicast here, we deal with it in more detail later.
+ String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter";
+ gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel);
+
+ // Drop all other packets sent to ff00::/8.
+ gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
+ gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL);
+ // Not multicast and not ICMPv6. Pass.
+ gen.addJump(gen.PASS_LABEL);
+ gen.defineLabel(skipIpv6MulticastFilterLabel);
+ } else {
+ // If not ICMPv6, pass.
+ gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+ }
+
// Add unsolicited multicast neighbor announcements filter
String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
// If not neighbor announcements, skip unsolicited multicast NA filter
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 505d15a..99b10c3 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -125,17 +125,18 @@
public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8;
public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9;
- /* Message.arg1 arguments to CMD_POST_DHCP notification */
+ /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
public static final int DHCP_SUCCESS = 1;
public static final int DHCP_FAILURE = 2;
- // Messages.
+ // Internal messages.
private static final int PRIVATE_BASE = Protocol.BASE_DHCP + 100;
private static final int CMD_KICK = PRIVATE_BASE + 1;
private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4;
- private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 5;
+ private static final int CMD_REBIND_DHCP = PRIVATE_BASE + 5;
+ private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 6;
// For message logging.
private static final Class[] sMessageClasses = { DhcpClient.class };
@@ -177,6 +178,7 @@
private final WakeupMessage mKickAlarm;
private final WakeupMessage mTimeoutAlarm;
private final WakeupMessage mRenewAlarm;
+ private final WakeupMessage mRebindAlarm;
private final WakeupMessage mExpiryAlarm;
private final String mIfaceName;
@@ -241,8 +243,9 @@
mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
// Used to time out PacketRetransmittingStates.
mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
- // Used to schedule DHCP renews.
+ // Used to schedule DHCP reacquisition.
mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
+ mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
}
@@ -276,6 +279,10 @@
}
private boolean initSockets() {
+ return initPacketSocket() && initUdpSocket();
+ }
+
+ private boolean initPacketSocket() {
try {
mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.getIndex());
@@ -285,6 +292,10 @@
Log.e(TAG, "Error creating packet socket", e);
return false;
}
+ return true;
+ }
+
+ private boolean initUdpSocket() {
try {
mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
@@ -363,16 +374,25 @@
return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
}
- private boolean transmitPacket(ByteBuffer buf, String description, Inet4Address to) {
+ private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
try {
- if (to.equals(INADDR_BROADCAST)) {
+ if (encap == DhcpPacket.ENCAP_L2) {
if (DBG) Log.d(TAG, "Broadcasting " + description);
Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
+ } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
+ if (DBG) Log.d(TAG, "Broadcasting " + description);
+ // We only send L3-encapped broadcasts in DhcpRebindingState,
+ // where we have an IP address and an unconnected UDP socket.
+ //
+ // N.B.: We only need this codepath because DhcpRequestPacket
+ // hardcodes the source IP address to 0.0.0.0. We could reuse
+ // the packet socket if this ever changes.
+ Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
} else {
// It's safe to call getpeername here, because we only send unicast packets if we
- // have an IP address, and we connect the UDP socket before
- // ConfiguringInterfaceState#exit.
- if (DBG) Log.d(TAG, "Unicasting " + description + " to " + Os.getpeername(mUdpSock));
+ // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
+ if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
+ description, Os.getpeername(mUdpSock)));
Os.write(mUdpSock, buf);
}
} catch(ErrnoException|IOException e) {
@@ -386,14 +406,15 @@
ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
DO_UNICAST, REQUESTED_PARAMS);
- return transmitPacket(packet, "DHCPDISCOVER", INADDR_BROADCAST);
+ return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
}
private boolean sendRequestPacket(
Inet4Address clientAddress, Inet4Address requestedAddress,
Inet4Address serverAddress, Inet4Address to) {
// TODO: should we use the transaction ID from the server?
- int encap = to.equals(INADDR_BROADCAST) ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
+ final int encap = INADDR_ANY.equals(clientAddress)
+ ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
ByteBuffer packet = DhcpPacket.buildRequestPacket(
encap, mTransactionId, getSecs(), clientAddress,
@@ -403,7 +424,7 @@
String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
" request=" + requestedAddress.getHostAddress() +
" serverid=" + serverStr;
- return transmitPacket(packet, description, to);
+ return transmitPacket(packet, description, encap, to);
}
private void scheduleLeaseTimers() {
@@ -413,14 +434,21 @@
}
final long now = SystemClock.elapsedRealtime();
- long renewTime = (now + mDhcpLeaseExpiry) / 2;
- mRenewAlarm.schedule(renewTime);
- long secondsHence = (renewTime - now) / 1000;
- Log.d(TAG, "Scheduling renewal in " + secondsHence + "s");
- mExpiryAlarm.schedule(mDhcpLeaseExpiry);
- secondsHence = (mDhcpLeaseExpiry - now) / 1000;
- Log.d(TAG, "Scheduling expiry in " + secondsHence + "s");
+ // TODO: consider getting the renew and rebind timers from T1 and T2.
+ // See also:
+ // https://tools.ietf.org/html/rfc2131#section-4.4.5
+ // https://tools.ietf.org/html/rfc1533#section-9.9
+ // https://tools.ietf.org/html/rfc1533#section-9.10
+ final long remainingDelay = mDhcpLeaseExpiry - now;
+ final long renewDelay = remainingDelay / 2;
+ final long rebindDelay = remainingDelay * 7 / 8;
+ mRenewAlarm.schedule(now + renewDelay);
+ mRebindAlarm.schedule(now + rebindDelay);
+ mExpiryAlarm.schedule(now + remainingDelay);
+ Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
+ Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
+ Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
}
private void notifySuccess() {
@@ -719,7 +747,6 @@
class DhcpRequestingState extends PacketRetransmittingState {
public DhcpRequestingState() {
- super();
mTimeout = DHCP_TIMEOUT_MS / 2;
}
@@ -777,7 +804,11 @@
@Override
public void exit() {
+ // Clear any extant alarms.
+ mRenewAlarm.cancel();
+ mRebindAlarm.cancel();
mExpiryAlarm.cancel();
+ clearDhcpState();
// Tell IpManager to clear the IPv4 address. There is no need to
// wait for confirmation since any subsequent packets are sent from
// INADDR_ANY anyway (DISCOVER, REQUEST).
@@ -797,21 +828,7 @@
super.processMessage(message);
switch (message.what) {
case EVENT_LINKADDRESS_CONFIGURED:
- if (mDhcpLease.serverAddress != null &&
- !connectUdpSock(mDhcpLease.serverAddress)) {
- // There's likely no point in going into DhcpInitState here, we'll probably
- // just repeat the transaction, get the same IP address as before, and fail.
- //
- // NOTE: It is observed that connectUdpSock() basically never fails, due to
- // SO_BINDTODEVICE. Examining the local socket address shows it will happily
- // return an IPv4 address from another interface, or even return "0.0.0.0".
- //
- // TODO: Consider deleting this check, following testing on several kernels.
- notifyFailure();
- transitionTo(mStoppedState);
- } else {
- transitionTo(mDhcpBoundState);
- }
+ transitionTo(mDhcpBoundState);
return HANDLED;
default:
return NOT_HANDLED;
@@ -823,8 +840,19 @@
@Override
public void enter() {
super.enter();
- // TODO: DhcpStateMachine only supported renewing at 50% of the lease time,
- // and did not support rebinding. Now that the legacy DHCP client is gone, fix this.
+ if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
+ // There's likely no point in going into DhcpInitState here, we'll probably
+ // just repeat the transaction, get the same IP address as before, and fail.
+ //
+ // NOTE: It is observed that connectUdpSock() basically never fails, due to
+ // SO_BINDTODEVICE. Examining the local socket address shows it will happily
+ // return an IPv4 address from another interface, or even return "0.0.0.0".
+ //
+ // TODO: Consider deleting this check, following testing on several kernels.
+ notifyFailure();
+ transitionTo(mStoppedState);
+ }
+
scheduleLeaseTimers();
}
@@ -843,18 +871,10 @@
return NOT_HANDLED;
}
}
-
- @Override
- public void exit() {
- mRenewAlarm.cancel();
- }
}
- class DhcpRenewingState extends PacketRetransmittingState {
- public DhcpRenewingState() {
- super();
- mTimeout = DHCP_TIMEOUT_MS;
- }
+ abstract class DhcpReacquiringState extends PacketRetransmittingState {
+ protected String mLeaseMsg;
@Override
public void enter() {
@@ -862,16 +882,14 @@
startNewTransaction();
}
+ abstract protected Inet4Address packetDestination();
+
protected boolean sendPacket() {
- // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
- // http://b/25343517 . Try to make things work anyway by using broadcast renews.
- Inet4Address to = (mDhcpLease.serverAddress != null) ?
- mDhcpLease.serverAddress : INADDR_BROADCAST;
return sendRequestPacket(
(Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr
INADDR_ANY, // DHCP_REQUESTED_IP
null, // DHCP_SERVER_IDENTIFIER
- to); // packet destination address
+ packetDestination()); // packet destination address
}
protected void receivePacket(DhcpPacket packet) {
@@ -890,7 +908,7 @@
// in IpManager and by any overridden relevant handlers of
// the registered IpManager.Callback. IP address changes
// are not supported here.
- acceptDhcpResults(results, "Renewed");
+ acceptDhcpResults(results, mLeaseMsg);
transitionTo(mDhcpBoundState);
}
} else if (packet instanceof DhcpNakPacket) {
@@ -901,8 +919,57 @@
}
}
- // Not implemented--yet. DhcpStateMachine did not implement it either.
- class DhcpRebindingState extends LoggingState {
+ class DhcpRenewingState extends DhcpReacquiringState {
+ public DhcpRenewingState() {
+ mLeaseMsg = "Renewed";
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (super.processMessage(message) == HANDLED) {
+ return HANDLED;
+ }
+
+ switch (message.what) {
+ case CMD_REBIND_DHCP:
+ transitionTo(mDhcpRebindingState);
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+
+ @Override
+ protected Inet4Address packetDestination() {
+ // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
+ // http://b/25343517 . Try to make things work anyway by using broadcast renews.
+ return (mDhcpLease.serverAddress != null) ?
+ mDhcpLease.serverAddress : INADDR_BROADCAST;
+ }
+ }
+
+ class DhcpRebindingState extends DhcpReacquiringState {
+ public DhcpRebindingState() {
+ mLeaseMsg = "Rebound";
+ }
+
+ @Override
+ public void enter() {
+ super.enter();
+
+ // We need to broadcast and possibly reconnect the socket to a
+ // completely different server.
+ closeQuietly(mUdpSock);
+ if (!initUdpSocket()) {
+ Log.e(TAG, "Failed to recreate UDP socket");
+ transitionTo(mDhcpInitState);
+ }
+ }
+
+ @Override
+ protected Inet4Address packetDestination() {
+ return INADDR_BROADCAST;
+ }
}
class DhcpInitRebootState extends LoggingState {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index d8eab35..cece6c8 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -1027,6 +1027,8 @@
}
class StartedState extends State {
+ private boolean mDhcpActionInFlight;
+
@Override
public void enter() {
mStartTimeMillis = SystemClock.elapsedRealtime();
@@ -1066,7 +1068,7 @@
@Override
public void exit() {
mProvisioningTimeoutAlarm.cancel();
- mDhcpActionTimeoutAlarm.cancel();
+ stopDhcpAction();
if (mIpReachabilityMonitor != null) {
mIpReachabilityMonitor.stop();
@@ -1086,16 +1088,22 @@
resetLinkProperties();
}
- private void startDhcpAction() {
- mCallback.onPreDhcpAction();
- final long alarmTime = SystemClock.elapsedRealtime() +
- mConfiguration.mRequestedPreDhcpActionMs;
- mDhcpActionTimeoutAlarm.schedule(alarmTime);
+ private void ensureDhcpAction() {
+ if (!mDhcpActionInFlight) {
+ mCallback.onPreDhcpAction();
+ mDhcpActionInFlight = true;
+ final long alarmTime = SystemClock.elapsedRealtime() +
+ mConfiguration.mRequestedPreDhcpActionMs;
+ mDhcpActionTimeoutAlarm.schedule(alarmTime);
+ }
}
private void stopDhcpAction() {
mDhcpActionTimeoutAlarm.cancel();
- mCallback.onPostDhcpAction();
+ if (mDhcpActionInFlight) {
+ mCallback.onPostDhcpAction();
+ mDhcpActionInFlight = false;
+ }
}
@Override
@@ -1165,9 +1173,8 @@
break;
case DhcpClient.CMD_PRE_DHCP_ACTION:
- if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
- startDhcpAction();
+ ensureDhcpAction();
} else {
sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
}
@@ -1193,18 +1200,18 @@
// This message is only received when:
//
// a) initial address acquisition succeeds,
- // b) renew succeeds,
- // c) renew fails,
+ // b) renew succeeds or is NAK'd,
+ // c) rebind succeeds or is NAK'd, or
+ // c) the lease expires,
//
// but never when initial address acquisition fails. The latter
// condition is now governed by the provisioning timeout.
- case DhcpClient.CMD_POST_DHCP_ACTION: {
+ case DhcpClient.CMD_POST_DHCP_ACTION:
stopDhcpAction();
- final DhcpResults dhcpResults = (DhcpResults) msg.obj;
switch (msg.arg1) {
case DhcpClient.DHCP_SUCCESS:
- handleIPv4Success(dhcpResults);
+ handleIPv4Success((DhcpResults) msg.obj);
break;
case DhcpClient.DHCP_FAILURE:
handleIPv4Failure();
@@ -1213,7 +1220,6 @@
Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
}
break;
- }
case DhcpClient.CMD_ON_QUIT:
// DHCPv4 quit early for some reason.
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index e9c5bdd..9e04d23 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -720,36 +720,52 @@
}
@LargeTest
- public void testApfFilterIPv4Multicast() throws Exception {
+ public void testApfFilterMulticast() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
byte[] program = ipManagerCallback.getApfProgram();
+ // Construct IPv4 and IPv6 multicast packets.
+ ByteBuffer v4packet = ByteBuffer.wrap(new byte[100]);
+ v4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ v4packet.position(IPV4_DEST_ADDR_OFFSET);
+ v4packet.put(new byte[]{(byte)224,0,0,1});
+
+ ByteBuffer v6packet = ByteBuffer.wrap(new byte[100]);
+ v6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
+ v6packet.position(IPV6_DEST_ADDR_OFFSET);
+ v6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
+
// Verify initially disabled multicast filter is off
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- packet.position(IPV4_DEST_ADDR_OFFSET);
- packet.put(new byte[]{(byte)224,0,0,1});
- assertPass(program, packet.array(), 0);
+ assertPass(program, v4packet.array(), 0);
+ assertPass(program, v6packet.array(), 0);
// Turn on multicast filter and verify it works
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(true);
program = ipManagerCallback.getApfProgram();
- assertDrop(program, packet.array(), 0);
+ assertDrop(program, v4packet.array(), 0);
+ assertDrop(program, v6packet.array(), 0);
// Turn off multicast filter and verify it's off
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(false);
program = ipManagerCallback.getApfProgram();
- assertPass(program, packet.array(), 0);
+ assertPass(program, v4packet.array(), 0);
+ assertPass(program, v6packet.array(), 0);
// Verify it can be initialized to on
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
program = ipManagerCallback.getApfProgram();
- assertDrop(program, packet.array(), 0);
+ assertDrop(program, v4packet.array(), 0);
+ assertDrop(program, v6packet.array(), 0);
+
+ // Verify that ICMPv6 multicast is not dropped.
+ v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
+ assertPass(program, v6packet.array(), 0);
apfFilter.shutdown();
}
@@ -839,7 +855,7 @@
@LargeTest
public void testApfFilterRa() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+ TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
byte[] program = ipManagerCallback.getApfProgram();
// Verify RA is passed the first time
@@ -848,6 +864,8 @@
basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)1000);
+ basePacket.position(IPV6_DEST_ADDR_OFFSET);
+ basePacket.put(IPV6_ALL_NODES_ADDRESS);
assertPass(program, basePacket.array(), 0);
testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);
diff --git a/services/tests/servicestests/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/servicestests/src/com/android/server/notification/RateEstimatorTest.java
index cc0920f..b5698d5 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RateEstimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RateEstimatorTest.java
@@ -26,18 +26,18 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
}
return time;
}
+
+ private void assertUpdateTime(long time) {
+ final float rate = mEstimator.update(time);
+ assertFalse(Float.isInfinite(rate));
+ assertFalse(Float.isNaN(rate));
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 57d68fd..51f5899 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1101,7 +1101,7 @@
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 @@
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) {