Merge "Do not stop window if preserving the window" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 646c56e..95c43b2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -101,7 +101,6 @@
field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS";
field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
field public static final java.lang.String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL";
- field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
field public static final java.lang.String REBOOT = "android.permission.REBOOT";
field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
@@ -2666,6 +2665,7 @@
field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
+ field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
}
@@ -4907,6 +4907,7 @@
field public static final int DEFAULT_VIBRATE = 2; // 0x2
field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+ field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
@@ -5058,6 +5059,7 @@
method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setCategory(java.lang.String);
+ method public android.app.Notification.Builder setChronometerCountsDown(boolean);
method public android.app.Notification.Builder setColor(int);
method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
@@ -19172,7 +19174,6 @@
method public short getLeapSecond();
method public long getTimeInNs();
method public double getTimeUncertaintyInNs();
- method public byte getType();
method public boolean hasBiasInNs();
method public boolean hasBiasUncertaintyInNs();
method public boolean hasDriftInNsPerSec();
@@ -19198,17 +19199,10 @@
method public void setLeapSecond(short);
method public void setTimeInNs(long);
method public void setTimeUncertaintyInNs(double);
- method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
- field public static final byte CLOCK_TYPE_GPS_TIME = 2; // 0x2
- field public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1; // 0x1
- field public static final byte CLOCK_TYPE_UNKNOWN = 0; // 0x0
field public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
}
- public static abstract class GnssClock.GnssClockType implements java.lang.annotation.Annotation {
- }
-
public final class GnssMeasurement implements android.os.Parcelable {
method public int describeContents();
method public double getAccumulatedDeltaRangeInMeters();
@@ -19774,6 +19768,7 @@
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
+ field public static final int SAMPLE_RATE_UNSPECIFIED = 0; // 0x0
}
public static class AudioFormat.Builder {
@@ -20027,6 +20022,7 @@
public abstract interface AudioRouting {
method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+ method public abstract android.media.AudioDeviceInfo getRoutedDevice();
method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
}
@@ -22997,6 +22993,7 @@
field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
+ field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -26888,6 +26885,7 @@
method public static void glProgramBinary(int, int, java.nio.Buffer, int);
method public static void glProgramParameteri(int, int, int);
method public static void glReadBuffer(int);
+ method public static void glReadPixels(int, int, int, int, int, int, int);
method public static void glRenderbufferStorageMultisample(int, int, int, int, int);
method public static void glResumeTransformFeedback();
method public static void glSamplerParameterf(int, int, float);
@@ -29105,7 +29103,6 @@
field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
- field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
}
public final class PowerManager.WakeLock {
@@ -34575,10 +34572,11 @@
method public int getSuppressedVisualEffects();
method public boolean isAmbient();
method public boolean matchesInterruptionFilter();
- field public static final int IMPORTANCE_DEFAULT = 2; // 0x2
- field public static final int IMPORTANCE_HIGH = 3; // 0x3
- field public static final int IMPORTANCE_LOW = 1; // 0x1
- field public static final int IMPORTANCE_MAX = 4; // 0x4
+ field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+ field public static final int IMPORTANCE_HIGH = 4; // 0x4
+ field public static final int IMPORTANCE_LOW = 2; // 0x2
+ field public static final int IMPORTANCE_MAX = 5; // 0x5
+ field public static final int IMPORTANCE_MIN = 1; // 0x1
field public static final int IMPORTANCE_NONE = 0; // 0x0
field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
}
@@ -36509,9 +36507,11 @@
package android.telephony {
public class CarrierConfigManager {
+ method public android.os.PersistableBundle getConfig(int);
method public android.os.PersistableBundle getConfig();
- method public android.os.PersistableBundle getConfigForSubId(int);
- method public void notifyConfigChangedForSubId(int);
+ method public deprecated android.os.PersistableBundle getConfigForSubId(int);
+ method public void notifyConfigChanged(int);
+ method public deprecated void notifyConfigChangedForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final java.lang.String BOOL_ALLOW_EMERGENCY_VIDEO_CALLS = "bool_allow_emergency_video_calls";
field public static final java.lang.String BOOL_ALLOW_VIDEO_PAUSE = "bool_allow_video_pause";
@@ -44081,6 +44081,7 @@
field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
field public static final int TYPE_APPLICATION = 1; // 0x1
field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+ field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
field public static final int TYPE_SYSTEM = 3; // 0x3
}
@@ -47217,6 +47218,7 @@
method public void setChar(int, java.lang.String, char);
method public void setCharSequence(int, java.lang.String, java.lang.CharSequence);
method public void setChronometer(int, long, java.lang.String, boolean);
+ method public void setChronometerCountsDown(int, boolean);
method public void setContentDescription(int, java.lang.CharSequence);
method public void setDisplayedChild(int, int);
method public void setDouble(int, java.lang.String, double);
diff --git a/api/system-current.txt b/api/system-current.txt
index 52460bc..47c642f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -169,7 +169,6 @@
field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
field public static final java.lang.String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL";
field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
- field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
field public static final java.lang.String REBOOT = "android.permission.REBOOT";
field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
@@ -2768,6 +2767,7 @@
field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
+ field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
}
@@ -5039,6 +5039,7 @@
field public static final int DEFAULT_VIBRATE = 2; // 0x2
field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+ field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
@@ -5190,6 +5191,7 @@
method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setCategory(java.lang.String);
+ method public android.app.Notification.Builder setChronometerCountsDown(boolean);
method public android.app.Notification.Builder setColor(int);
method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
@@ -20361,7 +20363,6 @@
method public short getLeapSecond();
method public long getTimeInNs();
method public double getTimeUncertaintyInNs();
- method public byte getType();
method public boolean hasBiasInNs();
method public boolean hasBiasUncertaintyInNs();
method public boolean hasDriftInNsPerSec();
@@ -20387,17 +20388,10 @@
method public void setLeapSecond(short);
method public void setTimeInNs(long);
method public void setTimeUncertaintyInNs(double);
- method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
- field public static final byte CLOCK_TYPE_GPS_TIME = 2; // 0x2
- field public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1; // 0x1
- field public static final byte CLOCK_TYPE_UNKNOWN = 0; // 0x0
field public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
}
- public static abstract class GnssClock.GnssClockType implements java.lang.annotation.Annotation {
- }
-
public final class GnssMeasurement implements android.os.Parcelable {
method public int describeContents();
method public double getAccumulatedDeltaRangeInMeters();
@@ -21270,6 +21264,7 @@
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
+ field public static final int SAMPLE_RATE_UNSPECIFIED = 0; // 0x0
}
public static class AudioFormat.Builder {
@@ -21535,6 +21530,7 @@
public abstract interface AudioRouting {
method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+ method public abstract android.media.AudioDeviceInfo getRoutedDevice();
method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
}
@@ -24708,6 +24704,7 @@
field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
+ field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -29188,6 +29185,7 @@
method public static void glProgramBinary(int, int, java.nio.Buffer, int);
method public static void glProgramParameteri(int, int, int);
method public static void glReadBuffer(int);
+ method public static void glReadPixels(int, int, int, int, int, int, int);
method public static void glRenderbufferStorageMultisample(int, int, int, int, int);
method public static void glResumeTransformFeedback();
method public static void glSamplerParameterf(int, int, float);
@@ -31408,7 +31406,6 @@
field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
- field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1
field public static final int USER_ACTIVITY_EVENT_OTHER = 0; // 0x0
field public static final int USER_ACTIVITY_EVENT_TOUCH = 2; // 0x2
@@ -37118,10 +37115,11 @@
method public int getSuppressedVisualEffects();
method public boolean isAmbient();
method public boolean matchesInterruptionFilter();
- field public static final int IMPORTANCE_DEFAULT = 2; // 0x2
- field public static final int IMPORTANCE_HIGH = 3; // 0x3
- field public static final int IMPORTANCE_LOW = 1; // 0x1
- field public static final int IMPORTANCE_MAX = 4; // 0x4
+ field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+ field public static final int IMPORTANCE_HIGH = 4; // 0x4
+ field public static final int IMPORTANCE_LOW = 2; // 0x2
+ field public static final int IMPORTANCE_MAX = 5; // 0x5
+ field public static final int IMPORTANCE_MIN = 1; // 0x1
field public static final int IMPORTANCE_NONE = 0; // 0x0
field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
}
@@ -39224,10 +39222,12 @@
package android.telephony {
public class CarrierConfigManager {
+ method public android.os.PersistableBundle getConfig(int);
method public android.os.PersistableBundle getConfig();
- method public android.os.PersistableBundle getConfigForSubId(int);
+ method public deprecated android.os.PersistableBundle getConfigForSubId(int);
method public static android.os.PersistableBundle getDefaultConfig();
- method public void notifyConfigChangedForSubId(int);
+ method public void notifyConfigChanged(int);
+ method public deprecated void notifyConfigChangedForSubId(int);
method public void updateConfigForPhoneId(int, java.lang.String);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final java.lang.String BOOL_ALLOW_EMERGENCY_VIDEO_CALLS = "bool_allow_emergency_video_calls";
@@ -46865,6 +46865,7 @@
field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
field public static final int TYPE_APPLICATION = 1; // 0x1
field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+ field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
field public static final int TYPE_SYSTEM = 3; // 0x3
}
@@ -50335,6 +50336,7 @@
method public void setChar(int, java.lang.String, char);
method public void setCharSequence(int, java.lang.String, java.lang.CharSequence);
method public void setChronometer(int, long, java.lang.String, boolean);
+ method public void setChronometerCountsDown(int, boolean);
method public void setContentDescription(int, java.lang.CharSequence);
method public void setDisplayedChild(int, int);
method public void setDouble(int, java.lang.String, double);
diff --git a/api/test-current.txt b/api/test-current.txt
index 6f40052..7a9557c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -101,7 +101,6 @@
field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS";
field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
field public static final java.lang.String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL";
- field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
field public static final java.lang.String REBOOT = "android.permission.REBOOT";
field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
@@ -2666,6 +2665,7 @@
field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
+ field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
}
@@ -4907,6 +4907,7 @@
field public static final int DEFAULT_VIBRATE = 2; // 0x2
field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+ field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
@@ -5058,6 +5059,7 @@
method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setCategory(java.lang.String);
+ method public android.app.Notification.Builder setChronometerCountsDown(boolean);
method public android.app.Notification.Builder setColor(int);
method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
@@ -19180,7 +19182,6 @@
method public short getLeapSecond();
method public long getTimeInNs();
method public double getTimeUncertaintyInNs();
- method public byte getType();
method public boolean hasBiasInNs();
method public boolean hasBiasUncertaintyInNs();
method public boolean hasDriftInNsPerSec();
@@ -19206,17 +19207,10 @@
method public void setLeapSecond(short);
method public void setTimeInNs(long);
method public void setTimeUncertaintyInNs(double);
- method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
- field public static final byte CLOCK_TYPE_GPS_TIME = 2; // 0x2
- field public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1; // 0x1
- field public static final byte CLOCK_TYPE_UNKNOWN = 0; // 0x0
field public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
}
- public static abstract class GnssClock.GnssClockType implements java.lang.annotation.Annotation {
- }
-
public final class GnssMeasurement implements android.os.Parcelable {
method public int describeContents();
method public double getAccumulatedDeltaRangeInMeters();
@@ -19783,6 +19777,7 @@
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
+ field public static final int SAMPLE_RATE_UNSPECIFIED = 0; // 0x0
}
public static class AudioFormat.Builder {
@@ -20036,6 +20031,7 @@
public abstract interface AudioRouting {
method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+ method public abstract android.media.AudioDeviceInfo getRoutedDevice();
method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
}
@@ -23006,6 +23002,7 @@
field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
+ field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -26897,6 +26894,7 @@
method public static void glProgramBinary(int, int, java.nio.Buffer, int);
method public static void glProgramParameteri(int, int, int);
method public static void glReadBuffer(int);
+ method public static void glReadPixels(int, int, int, int, int, int, int);
method public static void glRenderbufferStorageMultisample(int, int, int, int, int);
method public static void glResumeTransformFeedback();
method public static void glSamplerParameterf(int, int, float);
@@ -29114,7 +29112,6 @@
field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
- field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
}
public final class PowerManager.WakeLock {
@@ -34590,10 +34587,11 @@
method public int getSuppressedVisualEffects();
method public boolean isAmbient();
method public boolean matchesInterruptionFilter();
- field public static final int IMPORTANCE_DEFAULT = 2; // 0x2
- field public static final int IMPORTANCE_HIGH = 3; // 0x3
- field public static final int IMPORTANCE_LOW = 1; // 0x1
- field public static final int IMPORTANCE_MAX = 4; // 0x4
+ field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+ field public static final int IMPORTANCE_HIGH = 4; // 0x4
+ field public static final int IMPORTANCE_LOW = 2; // 0x2
+ field public static final int IMPORTANCE_MAX = 5; // 0x5
+ field public static final int IMPORTANCE_MIN = 1; // 0x1
field public static final int IMPORTANCE_NONE = 0; // 0x0
field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
}
@@ -36524,9 +36522,11 @@
package android.telephony {
public class CarrierConfigManager {
+ method public android.os.PersistableBundle getConfig(int);
method public android.os.PersistableBundle getConfig();
- method public android.os.PersistableBundle getConfigForSubId(int);
- method public void notifyConfigChangedForSubId(int);
+ method public deprecated android.os.PersistableBundle getConfigForSubId(int);
+ method public void notifyConfigChanged(int);
+ method public deprecated void notifyConfigChangedForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final java.lang.String BOOL_ALLOW_EMERGENCY_VIDEO_CALLS = "bool_allow_emergency_video_calls";
field public static final java.lang.String BOOL_ALLOW_VIDEO_PAUSE = "bool_allow_video_pause";
@@ -44098,6 +44098,7 @@
field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
field public static final int TYPE_APPLICATION = 1; // 0x1
field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+ field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
field public static final int TYPE_SYSTEM = 3; // 0x3
}
@@ -47234,6 +47235,7 @@
method public void setChar(int, java.lang.String, char);
method public void setCharSequence(int, java.lang.String, java.lang.CharSequence);
method public void setChronometer(int, long, java.lang.String, boolean);
+ method public void setChronometerCountsDown(int, boolean);
method public void setContentDescription(int, java.lang.CharSequence);
method public void setDisplayedChild(int, int);
method public void setDouble(int, java.lang.String, double);
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index 51bbb81..3ae9e12 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -20,6 +20,7 @@
libutils \
liblog \
libbinder \
+ libnativeloader \
libandroid_runtime \
$(app_process_common_shared_libs) \
@@ -52,6 +53,7 @@
libutils \
liblog \
libbinder \
+ libnativeloader \
libandroid_runtime \
$(app_process_common_shared_libs) \
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 2e02382..8bcbf51 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -21,6 +21,7 @@
#include <cutils/properties.h>
#include <cutils/trace.h>
#include <android_runtime/AndroidRuntime.h>
+#include <nativeloader/native_loader.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
namespace android {
@@ -304,6 +305,7 @@
}
if (zygote) {
+ PreloadPublicNativeLibraries();
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 24449d4..4025553 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1490,7 +1490,7 @@
System.err.println(" -i: specify the installer package name");
System.err.println(" -s: install application on sdcard");
System.err.println(" -f: install application on internal flash");
- System.err.println(" -d: allow version code downgrade");
+ System.err.println(" -d: allow version code downgrade (debuggable packages only)");
System.err.println(" -p: partial application install");
System.err.println(" -g: grant all runtime permissions");
System.err.println(" -S: size in bytes of entire session");
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 4bc6b97..fb5f5b9 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -334,7 +334,7 @@
public static final int GLOBAL_ACTION_HOME = 2;
/**
- * Action to open the recent apps.
+ * Action to toggle showing the overview of recent apps
*/
public static final int GLOBAL_ACTION_RECENTS = 3;
@@ -353,6 +353,11 @@
*/
public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
+ /**
+ * Action to toggle docking the current app's window
+ */
+ public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b1927d0..8b010f3 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -716,8 +716,7 @@
try {
return ActivityManagerNative.getDefault().getFrontActivityScreenCompatMode();
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return 0;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -726,7 +725,7 @@
try {
ActivityManagerNative.getDefault().setFrontActivityScreenCompatMode(mode);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -735,8 +734,7 @@
try {
return ActivityManagerNative.getDefault().getPackageScreenCompatMode(packageName);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return 0;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -745,7 +743,7 @@
try {
ActivityManagerNative.getDefault().setPackageScreenCompatMode(packageName, mode);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -754,8 +752,7 @@
try {
return ActivityManagerNative.getDefault().getPackageAskScreenCompat(packageName);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -764,7 +761,7 @@
try {
ActivityManagerNative.getDefault().setPackageAskScreenCompat(packageName, ask);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1049,6 +1046,7 @@
return ActivityManagerNative.getDefault().getTaskDescriptionIcon(iconFilename,
userId);
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
return null;
@@ -1429,8 +1427,7 @@
return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
flags, UserHandle.myUserId());
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1455,8 +1452,7 @@
return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
flags, userId);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1591,8 +1587,7 @@
try {
appTasks = ActivityManagerNative.getDefault().getAppTasks(mContext.getPackageName());
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return null;
+ throw e.rethrowAsRuntimeException();
}
int numAppTasks = appTasks.size();
for (int i = 0; i < numAppTasks; i++) {
@@ -1617,7 +1612,7 @@
try {
mAppTaskThumbnailSize = ActivityManagerNative.getDefault().getAppTaskThumbnailSize();
} catch (RemoteException e) {
- throw new IllegalStateException("System dead?", e);
+ throw e.rethrowAsRuntimeException();
}
}
}
@@ -1683,7 +1678,7 @@
return ActivityManagerNative.getDefault().addAppTask(activity.getActivityToken(),
intent, description, thumbnail);
} catch (RemoteException e) {
- throw new IllegalStateException("System dead?", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1725,8 +1720,7 @@
try {
return ActivityManagerNative.getDefault().getTasks(maxNum, 0);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1742,8 +1736,7 @@
try {
return ActivityManagerNative.getDefault().removeTask(taskId);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1902,8 +1895,7 @@
try {
return ActivityManagerNative.getDefault().getTaskThumbnail(id);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1912,8 +1904,7 @@
try {
return ActivityManagerNative.getDefault().isInHomeStack(taskId);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1962,7 +1953,7 @@
try {
ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2148,8 +2139,7 @@
return ActivityManagerNative.getDefault()
.getServices(maxNum, 0);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2164,8 +2154,7 @@
return ActivityManagerNative.getDefault()
.getRunningServiceControlPanel(service);
} catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2269,6 +2258,7 @@
try {
ActivityManagerNative.getDefault().getMemoryInfo(outInfo);
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2387,7 +2377,7 @@
return ActivityManagerNative.getDefault().clearApplicationUserData(packageName,
observer, UserHandle.myUserId());
} catch (RemoteException e) {
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2421,8 +2411,7 @@
return ActivityManagerNative.getDefault().getGrantedUriPermissions(packageName,
UserHandle.myUserId());
} catch (RemoteException e) {
- Log.e(TAG, "Couldn't get granted URI permissions for :" + packageName, e);
- return ParceledListSlice.emptyList();
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2440,7 +2429,7 @@
ActivityManagerNative.getDefault().clearGrantedUriPermissions(packageName,
UserHandle.myUserId());
} catch (RemoteException e) {
- Log.e(TAG, "Couldn't clear granted URI permissions for :" + packageName, e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2560,7 +2549,7 @@
try {
return ActivityManagerNative.getDefault().getProcessesInErrorState();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2874,7 +2863,7 @@
try {
return ActivityManagerNative.getDefault().getRunningExternalApplications();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2891,7 +2880,7 @@
return ActivityManagerNative.getDefault().setProcessMemoryTrimLevel(process, userId,
level);
} catch (RemoteException e) {
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2909,7 +2898,7 @@
try {
return ActivityManagerNative.getDefault().getRunningAppProcesses();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2928,7 +2917,7 @@
mContext.getOpPackageName());
return RunningAppProcessInfo.procStateToImportance(procState);
} catch (RemoteException e) {
- return RunningAppProcessInfo.IMPORTANCE_GONE;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2947,6 +2936,7 @@
try {
ActivityManagerNative.getDefault().getMyMemoryState(outState);
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2965,7 +2955,7 @@
try {
return ActivityManagerNative.getDefault().getProcessMemoryInfo(pids);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2999,6 +2989,7 @@
ActivityManagerNative.getDefault().killBackgroundProcesses(packageName,
UserHandle.myUserId());
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3015,7 +3006,7 @@
ActivityManagerNative.getDefault().killUid(UserHandle.getAppId(uid),
UserHandle.getUserId(uid), reason);
} catch (RemoteException e) {
- Log.e(TAG, "Couldn't kill uid:" + uid, e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3042,6 +3033,7 @@
try {
ActivityManagerNative.getDefault().forceStopPackage(packageName, userId);
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3060,8 +3052,8 @@
try {
return ActivityManagerNative.getDefault().getDeviceConfigurationInfo();
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
- return null;
}
/**
@@ -3150,8 +3142,8 @@
try {
return ActivityManagerNative.getDefault().isUserAMonkey();
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
- return false;
}
/**
@@ -3226,10 +3218,8 @@
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
- // Should never happen, but if it does... deny!
- Slog.e(TAG, "PackageManager is dead?!?", e);
+ throw e.rethrowAsRuntimeException();
}
- return PackageManager.PERMISSION_DENIED;
}
/** @hide */
@@ -3238,10 +3228,8 @@
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
- // Should never happen, but if it does... deny!
- Slog.e(TAG, "PackageManager is dead?!?", e);
+ throw e.rethrowAsRuntimeException();
}
- return PackageManager.PERMISSION_DENIED;
}
/**
@@ -3277,7 +3265,7 @@
return ActivityManagerNative.getDefault().handleIncomingUser(callingPid,
callingUid, userId, allowAll, requireFull, name, callerPackage);
} catch (RemoteException e) {
- throw new SecurityException("Failed calling activity manager", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3292,7 +3280,7 @@
ui = ActivityManagerNative.getDefault().getCurrentUser();
return ui != null ? ui.id : 0;
} catch (RemoteException e) {
- return 0;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3304,7 +3292,7 @@
try {
return ActivityManagerNative.getDefault().switchUser(userid);
} catch (RemoteException e) {
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3328,7 +3316,7 @@
try {
return ActivityManagerNative.getDefault().isUserRunning(userId, 0);
} catch (RemoteException e) {
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3338,7 +3326,7 @@
return ActivityManagerNative.getDefault().isUserRunning(userId,
ActivityManager.FLAG_AND_LOCKED);
} catch (RemoteException e) {
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3348,7 +3336,7 @@
return ActivityManagerNative.getDefault().isUserRunning(userId,
ActivityManager.FLAG_AND_UNLOCKED);
} catch (RemoteException e) {
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3433,6 +3421,7 @@
ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, pssSize,
mContext.getPackageName());
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3451,6 +3440,7 @@
try {
ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, 0, null);
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3461,6 +3451,7 @@
try {
ActivityManagerNative.getDefault().startLockTaskMode(taskId);
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3471,6 +3462,7 @@
try {
ActivityManagerNative.getDefault().stopLockTaskMode();
} catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3497,7 +3489,7 @@
try {
return ActivityManagerNative.getDefault().getLockTaskModeState();
} catch (RemoteException e) {
- return ActivityManager.LOCK_TASK_MODE_NONE;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3520,7 +3512,7 @@
try {
mAppTaskImpl.finishAndRemoveTask();
} catch (RemoteException e) {
- Slog.e(TAG, "Invalid AppTask", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3533,8 +3525,7 @@
try {
return mAppTaskImpl.getTaskInfo();
} catch (RemoteException e) {
- Slog.e(TAG, "Invalid AppTask", e);
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3548,7 +3539,7 @@
try {
mAppTaskImpl.moveToFront();
} catch (RemoteException e) {
- Slog.e(TAG, "Invalid AppTask", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -3590,7 +3581,7 @@
try {
mAppTaskImpl.setExcludeFromRecents(exclude);
} catch (RemoteException e) {
- Slog.e(TAG, "Invalid AppTask", e);
+ throw e.rethrowAsRuntimeException();
}
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4fa654f..7310d67 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -29,6 +29,31 @@
* @hide Only for use within the system server.
*/
public abstract class ActivityManagerInternal {
+
+ /**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because we had
+ * the surface saved.
+ */
+ public static final int APP_TRANSITION_SAVED_SURFACE = 0;
+
+ /**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew
+ * the starting window.
+ */
+ public static final int APP_TRANSITION_STARTING_WINDOW = 1;
+
+ /**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all
+ * app windows were drawn
+ */
+ public static final int APP_TRANSITION_WINDOWS_DRAWN = 2;
+
+ /**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
+ * timeout.
+ */
+ public static final int APP_TRANSITION_TIMEOUT = 3;
+
// Called by the power manager.
public abstract void onWakefulnessChanged(int wakefulness);
@@ -48,6 +73,7 @@
* with underlying activities.
*/
public static abstract class SleepToken {
+
/**
* Releases the sleep token.
*/
@@ -56,6 +82,7 @@
/**
* Returns home activity for the specified user.
+ *
* @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
*/
public abstract ComponentName getHomeActivityForUser(int userId);
@@ -72,4 +99,19 @@
public abstract void onLocalVoiceInteractionStarted(IBinder callingActivity,
IVoiceInteractionSession mSession,
IVoiceInteractor mInteractor);
+
+ /**
+ * Callback for window manager to let activity manager know that the starting window has been
+ * drawn
+ */
+ public abstract void notifyStartingWindowDrawn();
+
+ /**
+ * Callback for window manager to let activity manager know that we are finally starting the
+ * app transition;
+ *
+ * @param reason The reason why the app transition started. Must be one of the APP_TRANSITION_*
+ * values.
+ */
+ public abstract void notifyAppTransitionStarting(int reason);
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index bb36a3e..a1f82de 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1540,6 +1540,14 @@
return true;
}
+ case GET_MEMORY_TRIM_LEVEL_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int level = getMemoryTrimLevel();
+ reply.writeNoException();
+ reply.writeInt(level);
+ return true;
+ }
+
case ENTER_SAFE_MODE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
enterSafeMode();
@@ -4874,6 +4882,18 @@
data.recycle();
reply.recycle();
}
+ public int getMemoryTrimLevel() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_MEMORY_TRIM_LEVEL_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int level = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return level;
+ }
public void enterSafeMode() throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 49230a0..a38df61 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2241,8 +2241,8 @@
memInfo.getTotalSwappablePss(),
memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
- memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOut() :
- memInfo.getTotalSwappedOutPss(),
+ memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
+ memInfo.getTotalSwappedOut(),
nativeMax+dalvikMax, nativeAllocated+dalvikAllocated,
nativeFree+dalvikFree);
} else {
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 7d0d1b4..b20c091 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -20,16 +20,14 @@
import android.util.ArrayMap;
import dalvik.system.PathClassLoader;
-class ApplicationLoaders
-{
- public static ApplicationLoaders getDefault()
- {
+class ApplicationLoaders {
+ public static ApplicationLoaders getDefault() {
return gApplicationLoaders;
}
- public ClassLoader getClassLoader(String zip, boolean isBundled, String librarySearchPath,
- String libraryPermittedPath, ClassLoader parent)
- {
+ public ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
+ String librarySearchPath, String libraryPermittedPath,
+ ClassLoader parent) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -55,11 +53,22 @@
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
+
PathClassLoader pathClassloader =
- new PathClassLoader(zip, isBundled, librarySearchPath,
- libraryPermittedPath, parent);
+ new PathClassLoader(zip, librarySearchPath, parent);
+
+ String errorMessage = createClassloaderNamespace(pathClassloader,
+ targetSdkVersion,
+ librarySearchPath,
+ libraryPermittedPath,
+ isBundled);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ if (errorMessage != null) {
+ throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " +
+ pathClassloader + ": " + errorMessage);
+ }
+
mLoaders.put(zip, pathClassloader);
return pathClassloader;
}
@@ -71,6 +80,12 @@
}
}
+ private static native String createClassloaderNamespace(ClassLoader classLoader,
+ int targetSdkVersion,
+ String librarySearchPath,
+ String libraryPermittedPath,
+ boolean isShared);
+
private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>();
private static final ApplicationLoaders gApplicationLoaders
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 91eabcc..6d716cc 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -143,7 +143,7 @@
return pi;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(packageName);
@@ -154,7 +154,7 @@
try {
return mPM.currentToCanonicalPackageNames(names);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -163,7 +163,7 @@
try {
return mPM.canonicalToCurrentPackageNames(names);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -227,7 +227,7 @@
return gids;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(packageName);
@@ -252,7 +252,7 @@
return uid;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(packageName);
@@ -267,7 +267,7 @@
return pi;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(name);
@@ -282,7 +282,7 @@
return pi;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(group);
@@ -297,7 +297,7 @@
return pgi;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(name);
@@ -308,7 +308,7 @@
try {
return mPM.getAllPermissionGroups(flags);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -330,7 +330,7 @@
return maybeAdjustApplicationInfo(ai);
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(packageName);
@@ -370,7 +370,7 @@
return ai;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(className.toString());
@@ -385,7 +385,7 @@
return ai;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(className.toString());
@@ -400,7 +400,7 @@
return si;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(className.toString());
@@ -415,7 +415,7 @@
return pi;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(className.toString());
@@ -426,7 +426,7 @@
try {
return mPM.getSystemSharedLibraryNames();
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -436,7 +436,7 @@
try {
return mPM.getServicesSystemSharedLibraryPackageName();
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -445,7 +445,7 @@
try {
return mPM.getSystemAvailableFeatures();
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -459,7 +459,7 @@
try {
return mPM.hasSystemFeature(name, version);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -468,7 +468,7 @@
try {
return mPM.checkPermission(permName, pkgName, mContext.getUserId());
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -477,7 +477,7 @@
try {
return mPM.isPermissionRevokedByPolicy(permName, pkgName, mContext.getUserId());
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -491,7 +491,7 @@
try {
mPermissionsControllerPackageName = mPM.getPermissionControllerPackageName();
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
return mPermissionsControllerPackageName;
@@ -503,7 +503,7 @@
try {
return mPM.addPermission(info);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -512,7 +512,7 @@
try {
return mPM.addPermissionAsync(info);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -521,7 +521,7 @@
try {
mPM.removePermission(name);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -531,7 +531,7 @@
try {
mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier());
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -541,7 +541,7 @@
try {
mPM.revokeRuntimePermission(packageName, permissionName, user.getIdentifier());
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -550,7 +550,7 @@
try {
return mPM.getPermissionFlags(permissionName, packageName, user.getIdentifier());
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -561,7 +561,7 @@
mPM.updatePermissionFlags(permissionName, packageName, flagMask,
flagValues, user.getIdentifier());
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -571,7 +571,7 @@
return mPM.shouldShowRequestPermissionRationale(permission,
mContext.getPackageName(), mContext.getUserId());
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -580,7 +580,7 @@
try {
return mPM.checkSignatures(pkg1, pkg2);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -589,7 +589,7 @@
try {
return mPM.checkUidSignatures(uid1, uid2);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -598,7 +598,7 @@
try {
return mPM.getPackagesForUid(uid);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -607,7 +607,7 @@
try {
return mPM.getNameForUid(uid);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -620,7 +620,7 @@
return uid;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException("No shared userid for user:"+sharedUserName);
}
@@ -638,7 +638,7 @@
ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId);
return slice.getList();
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -652,7 +652,7 @@
permissions, flags, userId);
return slice.getList();
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -664,7 +664,7 @@
ParceledListSlice<ApplicationInfo> slice = mPM.getInstalledApplications(flags, userId);
return slice.getList();
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -680,7 +680,7 @@
}
return Collections.emptyList();
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -695,7 +695,7 @@
}
return null;
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -705,9 +705,8 @@
return mPM.isEphemeralApplication(
mContext.getPackageName(), mContext.getUserId());
} catch (RemoteException e) {
- Log.e(TAG, "System server is dead", e);
+ throw e.rethrowAsRuntimeException();
}
- return false;
}
@Override
@@ -724,11 +723,12 @@
mContext.getPackageName(), mContext.getUserId());
if (cookie != null) {
return cookie;
+ } else {
+ return EmptyArray.BYTE;
}
} catch (RemoteException e) {
- Log.e(TAG, "System server is dead", e);
+ throw e.rethrowAsRuntimeException();
}
- return EmptyArray.BYTE;
}
@Override
@@ -737,9 +737,8 @@
return mPM.setEphemeralApplicationCookie(
mContext.getPackageName(), cookie, mContext.getUserId());
} catch (RemoteException e) {
- Log.e(TAG, "System server is dead", e);
+ throw e.rethrowAsRuntimeException();
}
- return false;
}
@Override
@@ -756,7 +755,7 @@
flags,
userId);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -777,7 +776,7 @@
flags,
userId);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -809,7 +808,7 @@
specificTypes, intent, intent.resolveTypeIfNeeded(resolver),
flags, mContext.getUserId());
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -825,7 +824,7 @@
flags,
userId);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -843,7 +842,7 @@
flags,
mContext.getUserId());
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -856,7 +855,7 @@
flags,
userId);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -872,7 +871,7 @@
return mPM.queryIntentContentProviders(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -892,7 +891,7 @@
try {
return mPM.resolveContentProvider(name, flags, userId);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -904,7 +903,7 @@
= mPM.queryContentProviders(processName, uid, flags);
return slice != null ? slice.getList() : null;
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -919,7 +918,7 @@
return ii;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException(className.toString());
@@ -931,7 +930,7 @@
try {
return mPM.queryInstrumentation(targetPackage, flags);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1198,7 +1197,7 @@
return getResourcesForApplication(ai);
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
throw new NameNotFoundException("Package " + appPackageName + " doesn't exist");
}
@@ -1213,7 +1212,7 @@
}
return mCachedSafeMode != 0;
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1229,7 +1228,7 @@
mPM.addOnPermissionsChangeListener(delegate);
mPermissionListeners.put(listener, delegate);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
}
@@ -1243,7 +1242,7 @@
mPM.removeOnPermissionsChangeListener(delegate);
mPermissionListeners.remove(listener);
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
}
}
@@ -1544,7 +1543,8 @@
try {
mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
verificationParams, null, userId);
- } catch (RemoteException ignored) {
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1563,8 +1563,7 @@
}
return res;
} catch (RemoteException e) {
- // Should never happen!
- throw new NameNotFoundException("Package " + packageName + " doesn't exist");
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1573,7 +1572,7 @@
try {
mPM.verifyPendingInstall(id, response);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1583,7 +1582,7 @@
try {
mPM.extendVerificationTimeout(id, verificationCodeAtTimeout, millisecondsToDelay);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1592,7 +1591,7 @@
try {
mPM.verifyIntentFilter(id, verificationCode, outFailedDomains);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1601,8 +1600,7 @@
try {
return mPM.getIntentVerificationStatus(packageName, userId);
} catch (RemoteException e) {
- // Should never happen!
- return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1611,8 +1609,7 @@
try {
return mPM.updateIntentVerificationStatus(packageName, status, userId);
} catch (RemoteException e) {
- // Should never happen!
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1621,8 +1618,7 @@
try {
return mPM.getIntentFilterVerifications(packageName);
} catch (RemoteException e) {
- // Should never happen!
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1631,8 +1627,7 @@
try {
return mPM.getAllIntentFilters(packageName);
} catch (RemoteException e) {
- // Should never happen!
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1641,8 +1636,7 @@
try {
return mPM.getDefaultBrowserPackageName(userId);
} catch (RemoteException e) {
- // Should never happen!
- return null;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1651,8 +1645,7 @@
try {
return mPM.setDefaultBrowserPackageName(packageName, userId);
} catch (RemoteException e) {
- // Should never happen!
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1662,7 +1655,7 @@
try {
mPM.setInstallerPackageName(targetPackage, installerPackageName);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1671,9 +1664,8 @@
try {
return mPM.getInstallerPackageName(packageName);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return null;
}
@Override
@@ -1796,7 +1788,7 @@
return false;
}
} catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
+ throw e.rethrowAsRuntimeException();
}
// Otherwise we can move to any private volume
@@ -1874,7 +1866,7 @@
try {
mPM.deletePackageAsUser(packageName, observer, userId, flags);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1884,7 +1876,7 @@
try {
mPM.clearApplicationUserData(packageName, observer, mContext.getUserId());
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@Override
@@ -1893,7 +1885,7 @@
try {
mPM.deleteApplicationCacheFiles(packageName, observer);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1903,7 +1895,7 @@
try {
mPM.freeStorageAndNotify(volumeUuid, idealStorageSize, observer);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1912,7 +1904,7 @@
try {
mPM.freeStorage(volumeUuid, freeStorageSize, pi);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1922,9 +1914,8 @@
try {
return mPM.setPackagesSuspendedAsUser(packageNames, suspended, userId);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return packageNames;
}
@Override
@@ -1932,9 +1923,8 @@
try {
return mPM.isPackageSuspendedForUser(packageName, userId);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return false;
}
@Override
@@ -1943,7 +1933,7 @@
try {
mPM.getPackageSizeInfo(packageName, userHandle, observer);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@Override
@@ -1951,7 +1941,7 @@
try {
mPM.addPackageToPreferred(packageName);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1960,7 +1950,7 @@
try {
mPM.removePackageFromPreferred(packageName);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1969,9 +1959,8 @@
try {
return mPM.getPreferredPackages(flags);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return new ArrayList<PackageInfo>();
}
@Override
@@ -1980,7 +1969,7 @@
try {
mPM.addPreferredActivity(filter, match, set, activity, mContext.getUserId());
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -1990,7 +1979,7 @@
try {
mPM.addPreferredActivity(filter, match, set, activity, userId);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2000,7 +1989,7 @@
try {
mPM.replacePreferredActivity(filter, match, set, activity, mContext.getUserId());
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2011,7 +2000,7 @@
try {
mPM.replacePreferredActivity(filter, match, set, activity, userId);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2020,7 +2009,7 @@
try {
mPM.clearPackagePreferredActivities(packageName);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2030,9 +2019,8 @@
try {
return mPM.getPreferredActivities(outFilters, outActivities, packageName);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return 0;
}
@Override
@@ -2040,9 +2028,8 @@
try {
return mPM.getHomeActivities(outActivities);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return null;
}
@Override
@@ -2051,7 +2038,7 @@
try {
mPM.setComponentEnabledSetting(componentName, newState, flags, mContext.getUserId());
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2060,9 +2047,8 @@
try {
return mPM.getComponentEnabledSetting(componentName, mContext.getUserId());
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
@Override
@@ -2072,7 +2058,7 @@
mPM.setApplicationEnabledSetting(packageName, newState, flags,
mContext.getUserId(), mContext.getOpPackageName());
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2081,9 +2067,8 @@
try {
return mPM.getApplicationEnabledSetting(packageName, mContext.getUserId());
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
@Override
@@ -2092,20 +2077,18 @@
try {
return mPM.setApplicationHiddenSettingAsUser(packageName, hidden,
user.getIdentifier());
- } catch (RemoteException re) {
- // Should never happen!
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
- return false;
}
@Override
public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) {
try {
return mPM.getApplicationHiddenSettingAsUser(packageName, user.getIdentifier());
- } catch (RemoteException re) {
- // Should never happen!
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
- return false;
}
/** @hide */
@@ -2113,26 +2096,22 @@
public KeySet getKeySetByAlias(String packageName, String alias) {
Preconditions.checkNotNull(packageName);
Preconditions.checkNotNull(alias);
- KeySet ks;
try {
- ks = mPM.getKeySetByAlias(packageName, alias);
+ return mPM.getKeySetByAlias(packageName, alias);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowAsRuntimeException();
}
- return ks;
}
/** @hide */
@Override
public KeySet getSigningKeySet(String packageName) {
Preconditions.checkNotNull(packageName);
- KeySet ks;
try {
- ks = mPM.getSigningKeySet(packageName);
+ return mPM.getSigningKeySet(packageName);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowAsRuntimeException();
}
- return ks;
}
/** @hide */
@@ -2143,7 +2122,7 @@
try {
return mPM.isPackageSignedByKeySet(packageName, ks);
} catch (RemoteException e) {
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2155,7 +2134,7 @@
try {
return mPM.isPackageSignedByKeySetExactly(packageName, ks);
} catch (RemoteException e) {
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2167,9 +2146,8 @@
try {
return mPM.getVerifierDeviceIdentity();
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return null;
}
/**
@@ -2180,7 +2158,7 @@
try {
return mPM.isUpgrade();
} catch (RemoteException e) {
- return false;
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2218,7 +2196,7 @@
mPM.addCrossProfileIntentFilter(filter, mContext.getOpPackageName(),
sourceUserId, targetUserId, flags);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
@@ -2230,7 +2208,7 @@
try {
mPM.clearCrossProfileIntentFilters(sourceUserId, mContext.getOpPackageName());
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index e4d6835..2cb6151 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -308,6 +308,7 @@
public void setActivityController(IActivityController watcher)
throws RemoteException;
public void setLenientBackgroundCheck(boolean enabled) throws RemoteException;
+ public int getMemoryTrimLevel() throws RemoteException;
public void enterSafeMode() throws RemoteException;
@@ -980,4 +981,5 @@
int NOTIFY_PINNED_STACK_ANIMATION_ENDED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 366;
int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 367;
int SET_LENIENT_BACKGROUND_CHECK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+368;
+ int GET_MEMORY_TRIM_LEVEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+369;
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 837ceb6..838b8cb 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -413,8 +413,9 @@
// as this is early and necessary.
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, isBundledApp,
- librarySearchPath, libraryPermittedPath, mBaseClassLoader);
+ mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
+ mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
+ libraryPermittedPath, mBaseClassLoader);
StrictMode.setThreadPolicy(oldPolicy);
return mClassLoader;
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 3c7f48b..b6e0467 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -95,8 +95,7 @@
private native long loadNativeCode(String path, String funcname, MessageQueue queue,
String internalDataPath, String obbPath, String externalDataPath, int sdkVersion,
- AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath,
- String isolationPath);
+ AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath);
private native String getDlError();
private native void unloadNativeCode(long handle);
private native void onStartNative(long handle);
@@ -177,8 +176,7 @@
getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()),
getAbsolutePath(getExternalFilesDir(null)),
Build.VERSION.SDK_INT, getAssets(), nativeSavedState,
- classLoader, classLoader.getLdLibraryPath(),
- classLoader.getLibraryPermittedPath());
+ classLoader, classLoader.getLdLibraryPath());
if (mNativeHandle == 0) {
throw new UnsatisfiedLinkError(
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 402c112..d98c717 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -823,6 +823,12 @@
public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
/**
+ * {@link #extras} key: whether the chronometer set on the notification should count down
+ * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
+ */
+ public static final String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
+
+ /**
* {@link #extras} key: whether {@link #when} should be shown,
* as supplied to {@link Builder#setShowWhen(boolean)}.
*/
@@ -2158,8 +2164,12 @@
*
* Useful when showing an elapsed time (like an ongoing phone call).
*
+ * The counter can also be set to count down to <code>when</code> when using
+ * {@link #setChronometerCountsDown(boolean)}.
+ *
* @see android.widget.Chronometer
* @see Notification#when
+ * @see #setChronometerCountsDown(boolean)
*/
public Builder setUsesChronometer(boolean b) {
mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
@@ -2167,6 +2177,19 @@
}
/**
+ * Sets the Chronometer to count down instead of counting up.
+ *
+ * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
+ * If it isn't set the chronometer will count up.
+ *
+ * @see #setUsesChronometer(boolean)
+ */
+ public Builder setChronometerCountsDown(boolean countsDown) {
+ mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN, countsDown);
+ return this;
+ }
+
+ /**
* Set the small icon resource, which will be used to represent the notification in the
* status bar.
*
@@ -3097,6 +3120,8 @@
contentView.setLong(R.id.chronometer, "setBase",
mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
contentView.setBoolean(R.id.chronometer, "setStarted", true);
+ boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN);
+ contentView.setChronometerCountsDown(R.id.chronometer, countsDown);
} else {
contentView.setViewVisibility(R.id.time, View.VISIBLE);
contentView.setLong(R.id.time, "setTime", mN.when);
@@ -3328,6 +3353,8 @@
savedBundle.getBoolean(EXTRA_SHOW_WHEN));
publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
+ publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN,
+ savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN));
publicExtras.putCharSequence(EXTRA_TITLE,
mContext.getString(R.string.notification_hidden_text));
mN.extras = publicExtras;
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 4cbaf6c..039c9d7 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -102,12 +102,19 @@
public static final int PRIORITY_SYNC_INITIALIZATION = 20;
/**
- * Value of {@link #getPriority} for the current foreground app (overrides the supplied
+ * Value of {@link #getPriority} for a foreground app (overrides the supplied
* JobInfo priority if it is smaller).
* @hide
*/
public static final int PRIORITY_FOREGROUND_APP = 30;
+ /**
+ * Value of {@link #getPriority} for the current top app (overrides the supplied
+ * JobInfo priority if it is smaller).
+ * @hide
+ */
+ public static final int PRIORITY_TOP_APP = 40;
+
private final int jobId;
private final PersistableBundle extras;
private final ComponentName service;
diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java
index 940a530..95a8ccf 100644
--- a/core/java/android/app/job/JobService.java
+++ b/core/java/android/app/job/JobService.java
@@ -46,10 +46,10 @@
* Job services must be protected with this permission:
*
* <pre class="prettyprint">
- * <service android:name="MyJobService"
- * android:permission="android.permission.BIND_JOB_SERVICE" >
+ * <service android:name="MyJobService"
+ * android:permission="android.permission.BIND_JOB_SERVICE" >
* ...
- * </service>
+ * </service>
* </pre>
*
* <p>If a job service is declared in the manifest but not protected with this
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index e58744b..fb16150 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -98,13 +98,15 @@
/**
* Default trace file path and file
*/
- private static final String DEFAULT_TRACE_PATH_PREFIX =
- Environment.getLegacyExternalStorageDirectory().getPath() + "/";
private static final String DEFAULT_TRACE_BODY = "dmtrace";
private static final String DEFAULT_TRACE_EXTENSION = ".trace";
- private static final String DEFAULT_TRACE_FILE_PATH =
- DEFAULT_TRACE_PATH_PREFIX + DEFAULT_TRACE_BODY
- + DEFAULT_TRACE_EXTENSION;
+ private static class NoPreloadHolder {
+ private static final String DEFAULT_TRACE_PATH_PREFIX =
+ Environment.getLegacyExternalStorageDirectory().getPath() + "/";
+ private static final String DEFAULT_TRACE_FILE_PATH =
+ DEFAULT_TRACE_PATH_PREFIX + DEFAULT_TRACE_BODY
+ + DEFAULT_TRACE_EXTENSION;
+ }
/**
@@ -942,7 +944,7 @@
* tracing.
*/
public static void startMethodTracing() {
- VMDebug.startMethodTracing(DEFAULT_TRACE_FILE_PATH, 0, 0, false, 0);
+ VMDebug.startMethodTracing(fixTraceName(null), 0, 0, false, 0);
}
/**
@@ -1032,9 +1034,9 @@
*/
private static String fixTraceName(String traceName) {
if (traceName == null)
- traceName = DEFAULT_TRACE_FILE_PATH;
+ traceName = NoPreloadHolder.DEFAULT_TRACE_FILE_PATH;
if (traceName.charAt(0) != '/')
- traceName = DEFAULT_TRACE_PATH_PREFIX + traceName;
+ traceName = NoPreloadHolder.DEFAULT_TRACE_PATH_PREFIX + traceName;
if (!traceName.endsWith(DEFAULT_TRACE_EXTENSION))
traceName = traceName + DEFAULT_TRACE_EXTENSION;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index dcc28d6..369ec15 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -224,6 +224,8 @@
* This is used by Gaming and VR applications to ensure the device provides
* will provide consistent performance over a large amount of time.
* </p>
+ *
+ * {@hide}
*/
public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 0x00000100;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index da7f85f..0edc43c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -36,7 +36,6 @@
import android.graphics.drawable.Drawable;
import android.os.storage.StorageManager;
import android.provider.Settings;
-import android.util.Log;
import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
@@ -700,8 +699,7 @@
try {
return mService.getUserInfo(getUserHandle()).name;
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user name", re);
- return "";
+ throw re.rethrowAsRuntimeException();
}
}
@@ -771,8 +769,7 @@
try {
return mService.isRestricted();
} catch (RemoteException re) {
- Log.w(TAG, "Could not check if user is limited ", re);
- return false;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -784,8 +781,7 @@
try {
return mService.canHaveRestrictedProfile(userId);
} catch (RemoteException re) {
- Log.w(TAG, "Could not check if user can have restricted profile", re);
- return false;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -847,8 +843,8 @@
public boolean isUserRunning(int userId) {
try {
return ActivityManagerNative.getDefault().isUserRunning(userId, 0);
- } catch (RemoteException e) {
- return false;
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
}
}
@@ -864,8 +860,8 @@
// TODO: reconcile stopped vs stopping?
return ActivityManagerNative.getDefault().isUserRunning(
user.getIdentifier(), ActivityManager.FLAG_OR_STOPPED);
- } catch (RemoteException e) {
- return false;
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
}
}
@@ -893,8 +889,8 @@
try {
return ActivityManagerNative.getDefault().isUserRunning(
user.getIdentifier(), ActivityManager.FLAG_AND_LOCKED);
- } catch (RemoteException e) {
- return false;
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
}
}
@@ -922,8 +918,8 @@
try {
return ActivityManagerNative.getDefault().isUserRunning(
user.getIdentifier(), ActivityManager.FLAG_AND_UNLOCKED);
- } catch (RemoteException e) {
- return false;
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
}
}
@@ -968,8 +964,7 @@
try {
return mService.getUserInfo(userHandle);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user info", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -990,8 +985,7 @@
try {
return mService.getUserRestrictions(userHandle.getIdentifier());
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user restrictions", re);
- return Bundle.EMPTY;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1007,9 +1001,7 @@
try {
return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier());
} catch (RemoteException re) {
- Log.w(TAG, "Could not get base user restrictions for user " +
- userHandle.getIdentifier(), re);
- return false;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1065,7 +1057,7 @@
try {
mService.setUserRestriction(key, value, userHandle.getIdentifier());
} catch (RemoteException re) {
- Log.w(TAG, "Could not set user restriction", re);
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1092,8 +1084,7 @@
return mService.hasUserRestriction(restrictionKey,
userHandle.getIdentifier());
} catch (RemoteException re) {
- Log.w(TAG, "Could not check user restrictions", re);
- return false;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1147,7 +1138,7 @@
mService.setUserRestriction(DISALLOW_OUTGOING_CALLS, true, user.id);
}
} catch (RemoteException re) {
- Log.w(TAG, "Could not create a user", re);
+ throw re.rethrowAsRuntimeException();
}
return user;
}
@@ -1167,7 +1158,7 @@
Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
}
} catch (RemoteException re) {
- Log.w(TAG, "Could not create a user", re);
+ throw re.rethrowAsRuntimeException();
}
return guest;
}
@@ -1188,8 +1179,7 @@
try {
return mService.createProfileForUser(name, flags, userHandle);
} catch (RemoteException re) {
- Log.w(TAG, "Could not create a user", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1211,10 +1201,9 @@
UserHandle.of(user.id));
}
return user;
- } catch (RemoteException e) {
- Log.w(TAG, "Could not create a restricted profile", e);
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
}
- return null;
}
/**
@@ -1282,8 +1271,7 @@
try {
return mService.getSeedAccountName();
} catch (RemoteException re) {
- Log.w(TAG, "Could not get the seed account name", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1297,8 +1285,7 @@
try {
return mService.getSeedAccountType();
} catch (RemoteException re) {
- Log.w(TAG, "Could not get the seed account type", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1314,8 +1301,7 @@
try {
return mService.getSeedAccountOptions();
} catch (RemoteException re) {
- Log.w(TAG, "Could not get the seed account options", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1336,7 +1322,7 @@
mService.setSeedAccountData(userId, accountName, accountType, accountOptions,
/* persist= */ true);
} catch (RemoteException re) {
- Log.w(TAG, "Could not set the seed account data", re);
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1349,7 +1335,7 @@
try {
mService.clearSeedAccountData();
} catch (RemoteException re) {
- Log.w(TAG, "Could not clear the seed account data", re);
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1364,8 +1350,7 @@
try {
return mService.markGuestForDeletion(userHandle);
} catch (RemoteException re) {
- Log.w(TAG, "Could not mark guest for deletion", re);
- return false;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1384,8 +1369,8 @@
public void setUserEnabled(@UserIdInt int userHandle) {
try {
mService.setUserEnabled(userHandle);
- } catch (RemoteException e) {
- Log.w(TAG, "Could not enable the profile", e);
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1407,8 +1392,7 @@
try {
return mService.getUsers(false);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user list", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1430,8 +1414,7 @@
}
return result;
} catch (RemoteException re) {
- Log.w(TAG, "Could not get users list", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1447,8 +1430,7 @@
try {
return mService.getUserAccount(userHandle);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user account", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1464,7 +1446,7 @@
try {
mService.setUserAccount(userHandle, accountName);
} catch (RemoteException re) {
- Log.w(TAG, "Could not set user account", re);
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1479,8 +1461,7 @@
try {
return mService.getPrimaryUser();
} catch (RemoteException re) {
- Log.w(TAG, "Could not get Primary user", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1517,8 +1498,7 @@
try {
return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne);
} catch (RemoteException re) {
- Log.w(TAG, "Could not check if we can add more managed profiles", re);
- return false;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1537,8 +1517,7 @@
try {
return mService.getProfiles(userHandle, false /* enabledOnly */);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user list", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1553,8 +1532,7 @@
try {
return mService.isSameProfileGroup(userId, otherUserId);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user list", re);
- return false;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1572,8 +1550,7 @@
try {
return mService.getProfiles(userHandle, true /* enabledOnly */);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user list", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1589,8 +1566,7 @@
try {
users = mService.getProfiles(UserHandle.myUserId(), true /* enabledOnly */);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user list", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
for (UserInfo info : users) {
UserHandle userHandle = new UserHandle(info.id);
@@ -1610,8 +1586,7 @@
try {
return mService.getCredentialOwnerProfile(userHandle);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get credential owner", re);
- return -1;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1625,8 +1600,7 @@
try {
return mService.getProfileParent(userHandle);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get profile parent", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1640,8 +1614,8 @@
public void setQuietModeEnabled(@UserIdInt int userHandle, boolean enableQuietMode) {
try {
mService.setQuietModeEnabled(userHandle, enableQuietMode);
- } catch (RemoteException e) {
- Log.w(TAG, "Could not change the profile's quiet mode", e);
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1654,10 +1628,9 @@
public boolean isQuietModeEnabled(UserHandle userHandle) {
try {
return mService.isQuietModeEnabled(userHandle.getIdentifier());
- } catch (RemoteException e) {
- Log.w(TAG, "Could not query the profile's quiet mode", e);
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
}
- return false;
}
/**
@@ -1742,8 +1715,7 @@
try {
return mService.getUsers(excludeDying);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user list", re);
- return null;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1757,8 +1729,7 @@
try {
return mService.removeUser(userHandle);
} catch (RemoteException re) {
- Log.w(TAG, "Could not remove user ", re);
- return false;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1774,7 +1745,7 @@
try {
mService.setUserName(userHandle, name);
} catch (RemoteException re) {
- Log.w(TAG, "Could not set the user name ", re);
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1788,7 +1759,7 @@
try {
mService.setUserIcon(userHandle, icon);
} catch (RemoteException re) {
- Log.w(TAG, "Could not set the user icon ", re);
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1813,7 +1784,7 @@
}
}
} catch (RemoteException re) {
- Log.w(TAG, "Could not get the user icon ", re);
+ throw re.rethrowAsRuntimeException();
}
return null;
}
@@ -1869,9 +1840,8 @@
try {
return mService.getUserSerialNumber(userHandle);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get serial number for user " + userHandle);
+ throw re.rethrowAsRuntimeException();
}
- return -1;
}
/**
@@ -1887,9 +1857,8 @@
try {
return mService.getUserHandle(userSerialNumber);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get userHandle for user " + userSerialNumber);
+ throw re.rethrowAsRuntimeException();
}
- return -1;
}
/**
@@ -1915,9 +1884,8 @@
try {
return mService.getApplicationRestrictions(packageName);
} catch (RemoteException re) {
- Log.w(TAG, "Could not get application restrictions for package " + packageName);
+ throw re.rethrowAsRuntimeException();
}
- return null;
}
/**
@@ -1927,9 +1895,8 @@
try {
return mService.getApplicationRestrictionsForUser(packageName, user.getIdentifier());
} catch (RemoteException re) {
- Log.w(TAG, "Could not get application restrictions for user " + user.getIdentifier());
+ throw re.rethrowAsRuntimeException();
}
- return null;
}
/**
@@ -1940,7 +1907,7 @@
try {
mService.setApplicationRestrictions(packageName, restrictions, user.getIdentifier());
} catch (RemoteException re) {
- Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier());
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1964,7 +1931,7 @@
try {
mService.setDefaultGuestRestrictions(restrictions);
} catch (RemoteException re) {
- Log.w(TAG, "Could not set guest restrictions");
+ throw re.rethrowAsRuntimeException();
}
}
@@ -1976,9 +1943,8 @@
try {
return mService.getDefaultGuestRestrictions();
} catch (RemoteException re) {
- Log.w(TAG, "Could not set guest restrictions");
+ throw re.rethrowAsRuntimeException();
}
- return new Bundle();
}
/**
@@ -1991,8 +1957,7 @@
try {
return mService.getUserCreationTime(userHandle.getIdentifier());
} catch (RemoteException re) {
- Log.w(TAG, "Could not get user creation time", re);
- return 0;
+ throw re.rethrowAsRuntimeException();
}
}
@@ -2008,8 +1973,7 @@
try {
return mService.someUserHasSeedAccount(accountName, accountType);
} catch (RemoteException re) {
- Log.w(TAG, "Could not check seed accounts", re);
- return false;
+ throw re.rethrowAsRuntimeException();
}
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index eaff1ac..cb45deb 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -58,6 +58,7 @@
import android.util.AndroidException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.LocaleList;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
@@ -2071,6 +2072,8 @@
if (outConfig.fontScale < 0) {
outConfig.fontScale = 1;
}
+ outConfig.setLocales(LocaleList.forLanguageTags(
+ Settings.System.getStringForUser(cr, SYSTEM_LOCALES, userHandle)));
}
/**
@@ -2079,6 +2082,9 @@
*/
public static void clearConfiguration(Configuration inoutConfig) {
inoutConfig.fontScale = 0;
+ if (!inoutConfig.userSetLocale) {
+ inoutConfig.setLocales(LocaleList.getEmptyLocaleList());
+ }
}
/**
@@ -2096,12 +2102,15 @@
/** @hide */
public static boolean putConfigurationForUser(ContentResolver cr, Configuration config,
int userHandle) {
- return Settings.System.putFloatForUser(cr, FONT_SCALE, config.fontScale, userHandle);
+ return Settings.System.putFloatForUser(cr, FONT_SCALE, config.fontScale, userHandle) &&
+ Settings.System.putStringForUser(
+ cr, SYSTEM_LOCALES, config.getLocales().toLanguageTags(), userHandle);
}
/** @hide */
public static boolean hasInterestingConfigurationChanges(int changes) {
- return (changes&ActivityInfo.CONFIG_FONT_SCALE) != 0;
+ return (changes & ActivityInfo.CONFIG_FONT_SCALE) != 0 ||
+ (changes & ActivityInfo.CONFIG_LOCALE) != 0;
}
/** @deprecated - Do not use */
@@ -2480,6 +2489,18 @@
};
/**
+ * The serialized system locale value.
+ *
+ * Do not use this value directory.
+ * To get system locale, use {@link android.util.LocaleList#getDefault} instead.
+ * To update system locale, use {@link com.android.internal.app.LocalePicker#updateLocales}
+ * instead.
+ * @hide
+ */
+ public static final String SYSTEM_LOCALES = "system_locales";
+
+
+ /**
* Name of an application package to be debugged.
*
* @deprecated Use {@link Global#DEBUG_APP} instead
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 7b461b1..53c92bf 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -929,26 +929,31 @@
public static final int IMPORTANCE_NONE = 0;
/**
- * Low notification importance: only shows in the shade, below the fold.
+ * Min notification importance: only shows in the shade, below the fold.
*/
- public static final int IMPORTANCE_LOW = 1;
+ public static final int IMPORTANCE_MIN = 1;
/**
- * Default notification importance: shows everywhere, but is not intrusive.
+ * Low notification importance: shows everywhere, but is not intrusive.
*/
- public static final int IMPORTANCE_DEFAULT = 2;
+ public static final int IMPORTANCE_LOW = 2;
/**
- * Higher notification importance: shows everywhere, makes noise,
+ * Default notification importance: shows everywhere, allowed to makes noise,
* but does not visually intrude.
*/
- public static final int IMPORTANCE_HIGH = 3;
+ public static final int IMPORTANCE_DEFAULT = 3;
/**
- * Highest notification importance: shows everywhere, makes noise,
- * and also visually intrudes.
+ * Higher notification importance: shows everywhere, allowed to makes noise and peek.
*/
- public static final int IMPORTANCE_MAX = 4;
+ public static final int IMPORTANCE_HIGH = 4;
+
+ /**
+ * Highest notification importance: shows everywhere, allowed to makes noise, peek, and
+ * use full screen intents.
+ */
+ public static final int IMPORTANCE_MAX = 5;
private String mKey;
private int mRank = -1;
@@ -1041,7 +1046,7 @@
CharSequence explanation) {
mKey = key;
mRank = rank;
- mIsAmbient = importance < IMPORTANCE_DEFAULT;
+ mIsAmbient = importance < IMPORTANCE_LOW;
mMatchesInterruptionFilter = matchesInterruptionFilter;
mVisibilityOverride = visibilityOverride;
mSuppressedVisualEffects = suppressedVisualEffects;
@@ -1058,6 +1063,8 @@
return "UNSPECIFIED";
case IMPORTANCE_NONE:
return "NONE";
+ case IMPORTANCE_MIN:
+ return "MIN";
case IMPORTANCE_LOW:
return "LOW";
case IMPORTANCE_DEFAULT:
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 367c9fb..e79dfca 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -437,6 +437,7 @@
if (handled) {
adjustMetaAfterKeypress(content);
+ return true;
}
return super.onKeyDown(view, content, keyCode, event);
@@ -470,4 +471,3 @@
return true;
}
}
-
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index a75e8a7..ad78b68 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -64,6 +64,12 @@
*/
public static final int TYPE_ACCESSIBILITY_OVERLAY = 4;
+ /**
+ * Window type: A system window used to divide the screen in split-screen mode.
+ * This type of window is present only in split-screen mode.
+ */
+ public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5;
+
private static final int UNDEFINED = -1;
private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
@@ -551,6 +557,9 @@
case TYPE_ACCESSIBILITY_OVERLAY: {
return "TYPE_ACCESSIBILITY_OVERLAY";
}
+ case TYPE_SPLIT_SCREEN_DIVIDER: {
+ return "TYPE_SPLIT_SCREEN_DIVIDER";
+ }
default:
return "<UNKNOWN>";
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 4c81d1a..54b3932 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1632,7 +1632,6 @@
final PopupDecorView decorView = mDecorView;
final View contentView = mContentView;
- final OnDismissListener dismissListener = mOnDismissListener;
final ViewGroup contentHolder;
final ViewParent contentParent = contentView.getParent();
@@ -1676,16 +1675,19 @@
new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
- dismissImmediate(decorView, contentHolder,
- contentView, dismissListener);
+ dismissImmediate(decorView, contentHolder, contentView);
}
});
} else {
- dismissImmediate(decorView, contentHolder, contentView, dismissListener);
+ dismissImmediate(decorView, contentHolder, contentView);
}
// Clears the anchor view.
unregisterForViewTreeChanges();
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
+ }
}
/**
@@ -1727,8 +1729,7 @@
* Removes the popup from the window manager and tears down the supporting
* view hierarchy, if necessary.
*/
- private void dismissImmediate(View decorView, ViewGroup contentHolder,
- View contentView, OnDismissListener listener) {
+ private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
// If this method gets called and the decor view doesn't have a parent,
// then it was either never added or was already removed. That should
// never happen, but it's worth checking to avoid potential crashes.
@@ -1745,10 +1746,6 @@
mDecorView = null;
mBackgroundView = null;
mIsTransitioningToDismiss = false;
-
- if (mOnDismissListener != null) {
- mOnDismissListener.onDismiss();
- }
}
/**
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1cccfae..063288e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2563,6 +2563,8 @@
* @param format The Chronometer format string, or null to
* simply display the timer value.
* @param started True if you want the clock to be started, false if not.
+ *
+ * @see #setChronometerCountsDown(int, boolean)
*/
public void setChronometer(int viewId, long base, String format, boolean started) {
setLong(viewId, "setBase", base);
@@ -2571,6 +2573,18 @@
}
/**
+ * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
+ * the chronometer with the given viewId.
+ *
+ * @param viewId The id of the {@link Chronometer} to change
+ * @param isCountDown True if you want the chronometer to count down to base instead of
+ * counting up.
+ */
+ public void setChronometerCountsDown(int viewId, boolean isCountDown) {
+ setBoolean(viewId, "setCountDown", isCountDown);
+ }
+
+ /**
* Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
* {@link ProgressBar#setProgress ProgressBar.setProgress}, and
* {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 6a365e0..b1b019c 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -275,7 +275,7 @@
config.setLocales(locales);
config.userSetLocale = true;
- am.updateConfiguration(config);
+ am.updatePersistentConfiguration(config);
// Trigger the dirty bit for the Settings Provider.
BackupManager.dataChanged("com.android.providers.settings");
} catch (RemoteException e) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 08d4fba..64c5b8d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -60,6 +60,7 @@
void showRecentApps(boolean triggeredFromAltTab);
void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
void toggleRecentApps();
+ void toggleSplitScreen();
void preloadRecentApps();
void cancelPreloadRecentApps();
void showScreenPinningRequest();
diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
index 77859b8..451078b 100644
--- a/core/java/com/android/internal/util/WakeupMessage.java
+++ b/core/java/com/android/internal/util/WakeupMessage.java
@@ -21,7 +21,7 @@
import android.os.Handler;
import android.os.Message;
- /**
+/**
* An AlarmListener that sends the specified message to a Handler and keeps the system awake until
* the message is processed.
*
@@ -33,19 +33,17 @@
* the message, but does not guarantee that the system will be awake until the target object has
* processed it. This is because as soon as the onAlarmListener sends the message and returns, the
* AlarmManager releases its wakelock and the system is free to go to sleep again.
- *
*/
public class WakeupMessage implements AlarmManager.OnAlarmListener {
- private static AlarmManager sAlarmManager;
+ private final AlarmManager mAlarmManager;
private final Handler mHandler;
private final String mCmdName;
private final int mCmd, mArg1, mArg2;
+ private boolean mScheduled;
public WakeupMessage(Context context, Handler handler,
String cmdName, int cmd, int arg1, int arg2) {
- if (sAlarmManager == null) {
- sAlarmManager = context.getSystemService(AlarmManager.class);
- }
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mHandler = handler;
mCmdName = cmdName;
mCmd = cmd;
@@ -61,19 +59,43 @@
this(context, handler, cmdName, cmd, 0, 0);
}
- public void schedule(long when) {
- sAlarmManager.setExact(
+ /**
+ * Schedule the message to be delivered at the time in milliseconds of the
+ * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} clock and wakeup
+ * the device when it goes off. If schedule is called multiple times without the message being
+ * dispatched then the alarm is rescheduled to the new time.
+ */
+ public synchronized void schedule(long when) {
+ mAlarmManager.setExact(
AlarmManager.ELAPSED_REALTIME_WAKEUP, when, mCmdName, this, mHandler);
+ mScheduled = true;
}
- public void cancel() {
- sAlarmManager.cancel(this);
+ /**
+ * Cancel all pending messages. This includes alarms that may have been fired, but have not been
+ * run on the handler yet.
+ */
+ public synchronized void cancel() {
+ if (mScheduled) {
+ mAlarmManager.cancel(this);
+ mScheduled = false;
+ }
}
@Override
public void onAlarm() {
- Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
- mHandler.handleMessage(msg);
- msg.recycle();
+ // Once this method is called the alarm has already been fired and removed from
+ // AlarmManager (it is still partially tracked, but only for statistics). The alarm can now
+ // be marked as unscheduled so that it can be rescheduled in the message handler.
+ final boolean stillScheduled;
+ synchronized (this) {
+ stillScheduled = mScheduled;
+ mScheduled = false;
+ }
+ if (stillScheduled) {
+ Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
+ mHandler.handleMessage(msg);
+ msg.recycle();
+ }
}
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 8b686b7..a8d684d 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -7,7 +7,7 @@
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-non-virtual-dtor
LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
-LOCAL_CFLAGS += -DHWUI_NEW_OPS
+#LOCAL_CFLAGS += -DHWUI_NEW_OPS
LOCAL_CPPFLAGS += -Wno-conversion-null
ifeq ($(TARGET_ARCH), arm)
@@ -33,6 +33,7 @@
com_android_internal_content_NativeLibraryHelper.cpp \
com_google_android_gles_jni_EGLImpl.cpp \
com_google_android_gles_jni_GLImpl.cpp.arm \
+ android_app_ApplicationLoaders.cpp \
android_app_NativeActivity.cpp \
android_auditing_SecurityLog.cpp \
android_opengl_EGL14.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 017fb53..6ed07a7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -177,6 +177,7 @@
extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
extern int register_android_app_backup_FullBackup(JNIEnv *env);
+extern int register_android_app_ApplicationLoaders(JNIEnv* env);
extern int register_android_app_ActivityThread(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
extern int register_android_media_RemoteDisplay(JNIEnv *env);
@@ -1371,6 +1372,7 @@
REG_JNI(register_android_backup_FileBackupHelperBase),
REG_JNI(register_android_backup_BackupHelperDispatcher),
REG_JNI(register_android_app_backup_FullBackup),
+ REG_JNI(register_android_app_ApplicationLoaders),
REG_JNI(register_android_app_ActivityThread),
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_util_jar_StrictJarFile),
diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp
new file mode 100644
index 0000000..89f22eb
--- /dev/null
+++ b/core/jni/android_app_ApplicationLoaders.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include <string>
+
+#include "nativeloader/native_loader.h"
+
+#include "core_jni_helpers.h"
+
+
+static jstring createClassloaderNamespace_native(JNIEnv* env,
+ jobject clazz,
+ jobject classLoader,
+ jint targetSdkVersion,
+ jstring librarySearchPath,
+ jstring libraryPermittedPath,
+ jboolean isShared) {
+ return android::CreateClassLoaderNamespace(env, targetSdkVersion,
+ classLoader, isShared == JNI_TRUE,
+ librarySearchPath, libraryPermittedPath);
+}
+
+static const JNINativeMethod g_methods[] = {
+ { "createClassloaderNamespace",
+ "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;",
+ reinterpret_cast<void*>(createClassloaderNamespace_native) },
+};
+
+static const char* const kApplicationLoadersPathName = "android/app/ApplicationLoaders";
+
+namespace android
+{
+
+int register_android_app_ApplicationLoaders(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, kApplicationLoadersPathName, g_methods, NELEM(g_methods));
+}
+
+} // namespace android
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 88a56d2..6431b94 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -259,8 +259,7 @@
loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
jobject messageQueue, jstring internalDataDir, jstring obbDir,
jstring externalDataDir, jint sdkVersion, jobject jAssetMgr,
- jbyteArray savedState, jobject classLoader, jstring libraryPath,
- jstring isolationPath) {
+ jbyteArray savedState, jobject classLoader, jstring libraryPath) {
if (kLogTrace) {
ALOGD("loadNativeCode_native");
}
@@ -269,8 +268,7 @@
std::unique_ptr<NativeCode> code;
bool needNativeBridge = false;
- void* handle = OpenNativeLibrary(env, sdkVersion, pathStr, classLoader,
- false, libraryPath, isolationPath);
+ void* handle = OpenNativeLibrary(env, sdkVersion, pathStr, classLoader, libraryPath);
if (handle == NULL) {
if (NativeBridgeIsSupported(pathStr)) {
handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY);
@@ -656,7 +654,7 @@
static const JNINativeMethod g_methods[] = {
{ "loadNativeCode",
- "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[BLjava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)J",
+ "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[BLjava/lang/ClassLoader;Ljava/lang/String;)J",
(void*)loadNativeCode_native },
{ "getDlError", "()Ljava/lang/String;", (void*) getDlError_native },
{ "unloadNativeCode", "(J)V", (void*)unloadNativeCode_native },
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index d0326f1..3e4e352 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -180,54 +180,15 @@
// ----------------------------------------------------------------------------
static jint
android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jobject jaa, jint sampleRateInHertz, jint channelMask, jint channelIndexMask,
- jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName)
+ jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
+ jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
+ jlong nativeRecordInJavaObj)
{
//ALOGV(">> Entering android_media_AudioRecord_setup");
- //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
- // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
-
- if (jaa == 0) {
- ALOGE("Error creating AudioRecord: invalid audio attributes");
- return (jint) AUDIO_JAVA_ERROR;
- }
-
- // channel index mask takes priority over channel position masks.
- if (channelIndexMask) {
- // Java channel index masks need the representation bits set.
- channelMask = audio_channel_mask_from_representation_and_bits(
- AUDIO_CHANNEL_REPRESENTATION_INDEX,
- channelIndexMask);
- }
- // Java channel position masks map directly to the native definition
-
- if (!audio_is_input_channel(channelMask)) {
- ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
- return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
- }
- uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
-
- // compare the format against the Java constants
- audio_format_t format = audioFormatToNative(audioFormat);
- if (format == AUDIO_FORMAT_INVALID) {
- ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
- return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
- }
-
- size_t bytesPerSample = audio_bytes_per_sample(format);
-
- if (buffSizeInBytes == 0) {
- ALOGE("Error creating AudioRecord: frameCount is 0.");
- return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
- }
- size_t frameSize = channelCount * bytesPerSample;
- size_t frameCount = buffSizeInBytes / frameSize;
-
- jclass clazz = env->GetObjectClass(thiz);
- if (clazz == NULL) {
- ALOGE("Can't find %s when setting up callback.", kClassPathName);
- return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
- }
+ //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d "
+ // "nativeRecordInJavaObj=0x%llX",
+ // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes, nativeRecordInJavaObj);
+ audio_channel_mask_t localChanMask = inChannelMaskToNative(channelMask);
if (jSession == NULL) {
ALOGE("Error creating AudioRecord: invalid session ID pointer");
@@ -243,55 +204,132 @@
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
- ScopedUtfChars opPackageNameStr(env, opPackageName);
-
- // create an uninitialized AudioRecord object
- sp<AudioRecord> lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
-
audio_attributes_t *paa = NULL;
- // read the AudioAttributes values
- paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- const jstring jtags =
- (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
- const char* tags = env->GetStringUTFChars(jtags, NULL);
- // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
- strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
- env->ReleaseStringUTFChars(jtags, tags);
- paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
- paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
- ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
+ sp<AudioRecord> lpRecorder = 0;
+ audiorecord_callback_cookie *lpCallbackData = NULL;
- audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
- if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
- flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
+ jclass clazz = env->GetObjectClass(thiz);
+ if (clazz == NULL) {
+ ALOGE("Can't find %s when setting up callback.", kClassPathName);
+ return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
}
- // create the callback information:
- // this data will be passed with every AudioRecord callback
- audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
- lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
- // we use a weak reference so the AudioRecord object can be garbage collected.
- lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
- lpCallbackData->busy = false;
- const status_t status = lpRecorder->set(paa->source,
- sampleRateInHertz,
- format, // word length, PCM
- channelMask,
- frameCount,
- recorderCallback,// callback_t
- lpCallbackData,// void* user
- 0, // notificationFrames,
- true, // threadCanCallJava
- sessionId,
- AudioRecord::TRANSFER_DEFAULT,
- flags,
- -1, -1, // default uid, pid
- paa);
+ // if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one.
+ if (nativeRecordInJavaObj == 0) {
+ if (jaa == 0) {
+ ALOGE("Error creating AudioRecord: invalid audio attributes");
+ return (jint) AUDIO_JAVA_ERROR;
+ }
- if (status != NO_ERROR) {
- ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
- status);
- goto native_init_failure;
+ if (jSampleRate == 0) {
+ ALOGE("Error creating AudioRecord: invalid sample rates");
+ return (jint) AUDIO_JAVA_ERROR;
+ }
+ jint elements[1];
+ env->GetIntArrayRegion(jSampleRate, 0, 1, elements);
+ int sampleRateInHertz = elements[0];
+
+ // channel index mask takes priority over channel position masks.
+ if (channelIndexMask) {
+ // Java channel index masks need the representation bits set.
+ localChanMask = audio_channel_mask_from_representation_and_bits(
+ AUDIO_CHANNEL_REPRESENTATION_INDEX,
+ channelIndexMask);
+ }
+ // Java channel position masks map directly to the native definition
+
+ if (!audio_is_input_channel(localChanMask)) {
+ ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", localChanMask);
+ return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
+ }
+ uint32_t channelCount = audio_channel_count_from_in_mask(localChanMask);
+
+ // compare the format against the Java constants
+ audio_format_t format = audioFormatToNative(audioFormat);
+ if (format == AUDIO_FORMAT_INVALID) {
+ ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
+ return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
+ }
+
+ size_t bytesPerSample = audio_bytes_per_sample(format);
+
+ if (buffSizeInBytes == 0) {
+ ALOGE("Error creating AudioRecord: frameCount is 0.");
+ return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
+ }
+ size_t frameSize = channelCount * bytesPerSample;
+ size_t frameCount = buffSizeInBytes / frameSize;
+
+ ScopedUtfChars opPackageNameStr(env, opPackageName);
+
+ // create an uninitialized AudioRecord object
+ lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
+
+ // read the AudioAttributes values
+ paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ const jstring jtags =
+ (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
+ const char* tags = env->GetStringUTFChars(jtags, NULL);
+ // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
+ strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+ env->ReleaseStringUTFChars(jtags, tags);
+ paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
+ paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+ ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
+
+ audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
+ if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
+ flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
+ }
+ // create the callback information:
+ // this data will be passed with every AudioRecord callback
+ lpCallbackData = new audiorecord_callback_cookie;
+ lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
+ // we use a weak reference so the AudioRecord object can be garbage collected.
+ lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
+ lpCallbackData->busy = false;
+
+ const status_t status = lpRecorder->set(paa->source,
+ sampleRateInHertz,
+ format, // word length, PCM
+ localChanMask,
+ frameCount,
+ recorderCallback,// callback_t
+ lpCallbackData,// void* user
+ 0, // notificationFrames,
+ true, // threadCanCallJava
+ sessionId,
+ AudioRecord::TRANSFER_DEFAULT,
+ flags,
+ -1, -1, // default uid, pid
+ paa);
+
+ if (status != NO_ERROR) {
+ ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
+ status);
+ goto native_init_failure;
+ }
+ } else { // end if nativeRecordInJavaObj == 0)
+ lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
+ // TODO: We need to find out which members of the Java AudioRecord might need to be
+ // initialized from the Native AudioRecord
+ // these are directly returned from getters:
+ // mSampleRate
+ // mRecordSource
+ // mAudioFormat
+ // mChannelMask
+ // mChannelCount
+ // mState (?)
+ // mRecordingState (?)
+ // mPreferredDevice
+
+ // create the callback information:
+ // this data will be passed with every AudioRecord callback
+ lpCallbackData = new audiorecord_callback_cookie;
+ lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
+ // we use a weak reference so the AudioRecord object can be garbage collected.
+ lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
+ lpCallbackData->busy = false;
}
nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
@@ -304,6 +342,11 @@
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
+ {
+ const jint elements[1] = { (jint) lpRecorder->getSampleRate() };
+ env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
+ }
+
{ // scope for the lock
Mutex::Autolock l(sLock);
sAudioRecordCallBackCookies.add(lpCallbackData);
@@ -717,8 +760,8 @@
// name, signature, funcPtr
{"native_start", "(II)I", (void *)android_media_AudioRecord_start},
{"native_stop", "()V", (void *)android_media_AudioRecord_stop},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIIII[ILjava/lang/String;)I",
- (void *)android_media_AudioRecord_setup},
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
+ (void *)android_media_AudioRecord_setup},
{"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
{"native_release", "()V", (void *)android_media_AudioRecord_release},
{"native_read_in_byte_array",
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 1ab9504..660cbdc 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -213,51 +213,17 @@
// ----------------------------------------------------------------------------
static jint
-android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jobject jaa,
- jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask,
- jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
+android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
+ jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
+ jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
+ jlong nativeAudioTrack) {
- ALOGV("sampleRate=%d, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d",
- sampleRateInHertz, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes);
+ ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
+ "nativeAudioTrack=0x%llX",
+ jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
+ nativeAudioTrack);
- if (jaa == 0) {
- ALOGE("Error creating AudioTrack: invalid audio attributes");
- return (jint) AUDIO_JAVA_ERROR;
- }
-
- // Invalid channel representations are caught by !audio_is_output_channel() below.
- audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
- channelPositionMask, channelIndexMask);
- if (!audio_is_output_channel(nativeChannelMask)) {
- ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
- return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
- }
-
- uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
-
- // check the format.
- // This function was called from Java, so we compare the format against the Java constants
- audio_format_t format = audioFormatToNative(audioFormat);
- if (format == AUDIO_FORMAT_INVALID) {
- ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
- return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
- }
-
- // compute the frame count
- size_t frameCount;
- if (audio_has_proportional_frames(format)) {
- const size_t bytesPerSample = audio_bytes_per_sample(format);
- frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
- } else {
- frameCount = buffSizeInBytes;
- }
-
- jclass clazz = env->GetObjectClass(thiz);
- if (clazz == NULL) {
- ALOGE("Can't find %s when setting up callback.", kClassPathName);
- return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
- }
+ sp<AudioTrack> lpTrack = 0;
if (jSession == NULL) {
ALOGE("Error creating AudioTrack: invalid session ID pointer");
@@ -273,91 +239,168 @@
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
- // create the native AudioTrack object
- sp<AudioTrack> lpTrack = new AudioTrack();
+ AudioTrackJniStorage* lpJniStorage = NULL;
audio_attributes_t *paa = NULL;
- // read the AudioAttributes values
- paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- const jstring jtags =
- (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
- const char* tags = env->GetStringUTFChars(jtags, NULL);
- // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
- strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
- env->ReleaseStringUTFChars(jtags, tags);
- paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
- paa->content_type =
- (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
- paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
- ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
- paa->usage, paa->content_type, paa->flags, paa->tags);
+ jclass clazz = env->GetObjectClass(thiz);
+ if (clazz == NULL) {
+ ALOGE("Can't find %s when setting up callback.", kClassPathName);
+ return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
+ }
- // initialize the callback information:
- // this data will be passed with every AudioTrack callback
- AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
- lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
- // we use a weak reference so the AudioTrack object can be garbage collected.
- lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
- lpJniStorage->mCallbackData.busy = false;
+ // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
+ if (nativeAudioTrack == 0) {
+ if (jaa == 0) {
+ ALOGE("Error creating AudioTrack: invalid audio attributes");
+ return (jint) AUDIO_JAVA_ERROR;
+ }
- // initialize the native AudioTrack object
- status_t status = NO_ERROR;
- switch (memoryMode) {
- case MODE_STREAM:
+ if (jSampleRate == 0) {
+ ALOGE("Error creating AudioTrack: invalid sample rates");
+ return (jint) AUDIO_JAVA_ERROR;
+ }
- status = lpTrack->set(
- AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
- sampleRateInHertz,
- format,// word length, PCM
- nativeChannelMask,
- frameCount,
- AUDIO_OUTPUT_FLAG_NONE,
- audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
- 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
- 0,// shared mem
- true,// thread can call Java
- sessionId,// audio session ID
- AudioTrack::TRANSFER_SYNC,
- NULL, // default offloadInfo
- -1, -1, // default uid, pid values
- paa);
- break;
+ int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
+ int sampleRateInHertz = sampleRates[0];
+ env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
- case MODE_STATIC:
- // AudioTrack is using shared memory
+ // Invalid channel representations are caught by !audio_is_output_channel() below.
+ audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
+ channelPositionMask, channelIndexMask);
+ if (!audio_is_output_channel(nativeChannelMask)) {
+ ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
+ return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
+ }
- if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
- ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
+ uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
+
+ // check the format.
+ // This function was called from Java, so we compare the format against the Java constants
+ audio_format_t format = audioFormatToNative(audioFormat);
+ if (format == AUDIO_FORMAT_INVALID) {
+ ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
+ return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
+ }
+
+ // compute the frame count
+ size_t frameCount;
+ if (audio_is_linear_pcm(format)) {
+ const size_t bytesPerSample = audio_bytes_per_sample(format);
+ frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
+ } else {
+ frameCount = buffSizeInBytes;
+ }
+
+ // create the native AudioTrack object
+ lpTrack = new AudioTrack();
+
+ // read the AudioAttributes values
+ paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ const jstring jtags =
+ (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
+ const char* tags = env->GetStringUTFChars(jtags, NULL);
+ // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
+ strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+ env->ReleaseStringUTFChars(jtags, tags);
+ paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
+ paa->content_type =
+ (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
+ paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+
+ ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
+ paa->usage, paa->content_type, paa->flags, paa->tags);
+
+ // initialize the callback information:
+ // this data will be passed with every AudioTrack callback
+ lpJniStorage = new AudioTrackJniStorage();
+ lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
+ // we use a weak reference so the AudioTrack object can be garbage collected.
+ lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
+ lpJniStorage->mCallbackData.busy = false;
+
+ // initialize the native AudioTrack object
+ status_t status = NO_ERROR;
+ switch (memoryMode) {
+ case MODE_STREAM:
+
+ status = lpTrack->set(
+ AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
+ sampleRateInHertz,
+ format,// word length, PCM
+ nativeChannelMask,
+ frameCount,
+ AUDIO_OUTPUT_FLAG_NONE,
+ audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
+ 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
+ 0,// shared mem
+ true,// thread can call Java
+ sessionId,// audio session ID
+ AudioTrack::TRANSFER_SYNC,
+ NULL, // default offloadInfo
+ -1, -1, // default uid, pid values
+ paa);
+ break;
+
+ case MODE_STATIC:
+ // AudioTrack is using shared memory
+
+ if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
+ ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
+ goto native_init_failure;
+ }
+
+ status = lpTrack->set(
+ AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
+ sampleRateInHertz,
+ format,// word length, PCM
+ nativeChannelMask,
+ frameCount,
+ AUDIO_OUTPUT_FLAG_NONE,
+ audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
+ 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
+ lpJniStorage->mMemBase,// shared mem
+ true,// thread can call Java
+ sessionId,// audio session ID
+ AudioTrack::TRANSFER_SHARED,
+ NULL, // default offloadInfo
+ -1, -1, // default uid, pid values
+ paa);
+ break;
+
+ default:
+ ALOGE("Unknown mode %d", memoryMode);
goto native_init_failure;
}
- status = lpTrack->set(
- AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
- sampleRateInHertz,
- format,// word length, PCM
- nativeChannelMask,
- frameCount,
- AUDIO_OUTPUT_FLAG_NONE,
- audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
- 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
- lpJniStorage->mMemBase,// shared mem
- true,// thread can call Java
- sessionId,// audio session ID
- AudioTrack::TRANSFER_SHARED,
- NULL, // default offloadInfo
- -1, -1, // default uid, pid values
- paa);
- break;
+ if (status != NO_ERROR) {
+ ALOGE("Error %d initializing AudioTrack", status);
+ goto native_init_failure;
+ }
+ } else { // end if (nativeAudioTrack == 0)
+ lpTrack = (AudioTrack*)nativeAudioTrack;
+ // TODO: We need to find out which members of the Java AudioTrack might
+ // need to be initialized from the Native AudioTrack
+ // these are directly returned from getters:
+ // mSampleRate
+ // mAudioFormat
+ // mStreamType
+ // mChannelConfiguration
+ // mChannelCount
+ // mState (?)
+ // mPlayState (?)
+ // these may be used internally (Java AudioTrack.audioParamCheck():
+ // mChannelMask
+ // mChannelIndexMask
+ // mDataLoadMode
- default:
- ALOGE("Unknown mode %d", memoryMode);
- goto native_init_failure;
- }
-
- if (status != NO_ERROR) {
- ALOGE("Error %d initializing AudioTrack", status);
- goto native_init_failure;
+ // initialize the callback information:
+ // this data will be passed with every AudioTrack callback
+ lpJniStorage = new AudioTrackJniStorage();
+ lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
+ // we use a weak reference so the AudioTrack object can be garbage collected.
+ lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
+ lpJniStorage->mCallbackData.busy = false;
}
nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
@@ -370,6 +413,11 @@
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
+ {
+ const jint elements[1] = { (jint) lpTrack->getSampleRate() };
+ env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
+ }
+
{ // scope for the lock
Mutex::Autolock l(sLock);
sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
@@ -385,9 +433,11 @@
// since we had audio attributes, the stream type was derived from them during the
// creation of the native AudioTrack: push the same value to the Java object
env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
- // audio attributes were copied in AudioTrack creation
- free(paa);
- paa = NULL;
+ if (paa != NULL) {
+ // audio attributes were copied in AudioTrack creation
+ free(paa);
+ paa = NULL;
+ }
return (jint) AUDIO_JAVA_SUCCESS;
@@ -409,7 +459,6 @@
return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}
-
// ----------------------------------------------------------------------------
static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
@@ -1114,7 +1163,7 @@
{"native_stop", "()V", (void *)android_media_AudioTrack_stop},
{"native_pause", "()V", (void *)android_media_AudioTrack_pause},
{"native_flush", "()V", (void *)android_media_AudioTrack_flush},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIIIII[I)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
(void *)android_media_AudioTrack_setup},
{"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
{"native_release", "()V", (void *)android_media_AudioTrack_release},
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index c9b5af7..2d69eaa 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -5155,6 +5155,21 @@
}
}
+/* void glReadPixels ( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint offset ) */
+static void
+android_glReadPixels__IIIIIII
+ (JNIEnv *_env, jobject _this, jint x, jint y, jint width, jint height, jint format, jint type, jint offset) {
+ glReadPixels(
+ (GLint)x,
+ (GLint)y,
+ (GLsizei)width,
+ (GLsizei)height,
+ (GLenum)format,
+ (GLenum)type,
+ reinterpret_cast<GLvoid *>(offset)
+ );
+}
+
static const char *classPathName = "android/opengl/GLES30";
static const JNINativeMethod methods[] = {
@@ -5320,6 +5335,7 @@
{"glTexStorage3D", "(IIIIII)V", (void *) android_glTexStorage3D__IIIIII },
{"glGetInternalformativ", "(IIII[II)V", (void *) android_glGetInternalformativ__IIII_3II },
{"glGetInternalformativ", "(IIIILjava/nio/IntBuffer;)V", (void *) android_glGetInternalformativ__IIIILjava_nio_IntBuffer_2 },
+{"glReadPixels", "(IIIIIII)V", (void *) android_glReadPixels__IIIIIII },
};
int register_android_opengl_jni_GLES30(JNIEnv *_env)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 30d6f29..03d93a1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -965,16 +965,6 @@
<!-- INSTALL PERMISSIONS -->
<!-- ====================================================================== -->
-` <!-- =========================================== -->
- <!-- Permissions for accessing contact metadata -->
- <!-- =========================================== -->
- <eat-comment />
-
- <!-- @SystemApi Allows an application to read/write contact metadata.
- <p>Not for use by third-party applications. -->
- <permission android:name="android.permission.READ_WRITE_CONTACT_METADATA"
- android:protectionLevel="signature|system" />
-
<!-- ================================== -->
<!-- Permissions for accessing messages -->
<!-- ================================== -->
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index a3a3112..ae19150 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -25,7 +25,7 @@
<bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
<!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
- <string translatable="false" name="config_defaultPictureInPictureBounds">"1328 54 1808 324"</string>
+ <string translatable="false" name="config_defaultPictureInPictureBounds">"1420 100 1820 325"</string>
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
is located in center. -->
@@ -34,5 +34,4 @@
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
when the PIP is shown with Recents. -->
<string translatable="false" name="config_pictureInPictureBoundsInRecents">"1480 123 1760 303"</string>
-
</resources>
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index f949e1a..3c6c32e 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -12,7 +12,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test
+ android-support-test \
+ mockito-target
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
new file mode 100644
index 0000000..da8bc1d
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Unit tests for {@link com.android.internal.util.WakeupMessage}.
+ */
+@SmallTest
+public class WakeupMessageTest {
+ private static final String TEST_CMD_NAME = "TEST cmd Name";
+ private static final int TEST_CMD = 18;
+ private static final int TEST_ARG1 = 33;
+ private static final int TEST_ARG2 = 182;
+
+ @Mock AlarmManager mAlarmManager;
+ WakeupMessage mMessage;
+ // Make a spy so that we can verify calls to it
+ @Spy MessageCapturingHandler mHandler = new MessageCapturingHandler();
+
+ ArgumentCaptor<AlarmManager.OnAlarmListener> mListenerCaptor =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+
+ /**
+ * A Handler that will capture the most recent message sent to it.
+ *
+ * This handler is setup on the main Looper
+ */
+ public static class MessageCapturingHandler extends Handler {
+ private Message mLastMessage;
+
+ public MessageCapturingHandler() {
+ super(Looper.getMainLooper(), /* Nothing is actually dispatched on this Looper */
+ null, false);
+ }
+
+ @Override
+ public void handleMessage(Message m) {
+ // need to copy since it will be recycled after this method returns
+ mLastMessage = Message.obtain(m);
+ }
+
+ public Message getLastMessage() {
+ return mLastMessage;
+ }
+ }
+
+ /**
+ * Sets up the test.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Context context = mock(Context.class);
+ when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+ // capture the listener for each AlarmManager.setExact call
+ doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), any(String.class),
+ mListenerCaptor.capture(), any(Handler.class));
+
+ mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
+ TEST_ARG2);
+ }
+
+ /**
+ * Ensure the test is cleaned up and ready for the next test.
+ */
+ @After
+ public void cleanup() {
+ validateMockitoUsage();
+ }
+
+ private void scheduleAndVerifyAlarm(long when) {
+ mMessage.schedule(when);
+ verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(when),
+ eq(TEST_CMD_NAME), any(AlarmManager.OnAlarmListener.class), eq(mHandler));
+ }
+
+ private void verifyMessageDispatchedOnce() {
+ verify(mHandler, times(1)).handleMessage(any(Message.class));
+ assertEquals("what", TEST_CMD, mHandler.getLastMessage().what);
+ assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1);
+ assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2);
+ }
+
+ /**
+ * Schedule and deliver a single message
+ */
+ @Test
+ public void scheduleAndDeliverMessage() {
+ final long when = 1001;
+ scheduleAndVerifyAlarm(when);
+ verify(mHandler, never()).handleMessage(any(Message.class));
+ mListenerCaptor.getValue().onAlarm();
+ verifyMessageDispatchedOnce();
+ }
+
+ /**
+ * Check that the message is not delivered if cancel is called it after its alarm fires but
+ * before onAlarm is called.
+ *
+ * This ensures that if cancel is called on the handler thread, any previously-scheduled message
+ * is guaranteed not to be delivered.
+ */
+ @Test
+ public void scheduleAndCancelMessage() {
+ final long when = 1010;
+ scheduleAndVerifyAlarm(when);
+ mMessage.cancel();
+ mListenerCaptor.getValue().onAlarm();
+ verify(mHandler, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Verify nothing happens when cancel is called without a schedule
+ */
+ @Test
+ public void cancelWithoutSchedule() {
+ mMessage.cancel();
+ }
+
+ /**
+ * Verify that the message is silently rescheduled if schedule is called twice without the
+ * message being dispatched first.
+ */
+ @Test
+ public void scheduleTwiceWithoutMessageDispatched() {
+ final long when1 = 1011;
+ final long when2 = 1012;
+ scheduleAndVerifyAlarm(when1);
+ scheduleAndVerifyAlarm(when2);
+ mListenerCaptor.getValue().onAlarm();
+ verifyMessageDispatchedOnce();
+ }
+
+}
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index d05c66a..3b0e7e8 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -898,19 +898,19 @@
* @param res the resources used to inflate density-dependent values
*/
final void updateDensity(Resources res) {
- if (mSourceRes != null) {
+ if (res != null) {
mSourceRes = res;
- }
- // The density may have changed since the last update (if any). Any
- // dimension-type attributes will need their default values scaled.
- final int targetDensity = Drawable.resolveDensity(res, mDensity);
- final int sourceDensity = mDensity;
- mDensity = targetDensity;
+ // The density may have changed since the last update (if any). Any
+ // dimension-type attributes will need their default values scaled.
+ final int targetDensity = Drawable.resolveDensity(res, mDensity);
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
- if (sourceDensity != targetDensity) {
- mCheckedConstantSize = false;
- mCheckedPadding = false;
+ if (sourceDensity != targetDensity) {
+ mCheckedConstantSize = false;
+ mCheckedPadding = false;
+ }
}
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index da7b7fb..8831baf 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-HWUI_NEW_OPS := true
+HWUI_NEW_OPS := false
# Enables fine-grained GLES error checking
# If set to true, every GLES call is wrapped & error checked
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 35c8f6b..5f689b4 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -347,7 +347,7 @@
if (valid) {
dirty = android::Rect(uiDirty.left, uiDirty.top, uiDirty.right, uiDirty.bottom);
} else {
- dirty = android::Rect(0, 0,
+ dirty = android::Rect(
mRenderTarget.offscreenBuffer->viewportWidth,
mRenderTarget.offscreenBuffer->viewportHeight);
}
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index a542c26..682bd04 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -21,6 +21,15 @@
namespace android {
namespace uirenderer {
+static int computeClipSideFlags(const Rect& clip, const Rect& bounds) {
+ int clipSideFlags = 0;
+ if (clip.left > bounds.left) clipSideFlags |= OpClipSideFlags::Left;
+ if (clip.top > bounds.top) clipSideFlags |= OpClipSideFlags::Top;
+ if (clip.right < bounds.right) clipSideFlags |= OpClipSideFlags::Right;
+ if (clip.bottom < bounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
+ return clipSideFlags;
+}
+
ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
const RecordedOp& recordedOp, bool expandForStroke) {
// resolvedMatrix = parentMatrix * localMatrix
@@ -55,10 +64,7 @@
clippedBounds.setEmpty();
} else {
// Not rejected! compute true clippedBounds and clipSideFlags
- if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
- if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
- if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right;
- if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
+ clipSideFlags = computeClipSideFlags(clipRect, clippedBounds);
clippedBounds.doIntersect(clipRect);
}
}
@@ -69,11 +75,13 @@
, clippedBounds(clipState->rect)
, clipSideFlags(OpClipSideFlags::Full) {}
-ResolvedRenderState::ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect)
+ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& dstRect)
: transform(Matrix4::identity())
- , clipState(viewportRect)
+ , clipState(clipRect)
, clippedBounds(dstRect)
- , clipSideFlags(OpClipSideFlags::None) {}
+ , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect)) {
+ clippedBounds.doIntersect(clipRect->rect);
+}
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 5a5845a..4365ef8 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -175,8 +175,8 @@
, projectionPathMask(snapshot.projectionPathMask)
, op(shadowOpPtr) {}
- BakedOpState(const ClipRect* viewportRect, const Rect& dstRect, const RecordedOp& recordedOp)
- : computedState(viewportRect, dstRect)
+ BakedOpState(const ClipRect* clipRect, const Rect& dstRect, const RecordedOp& recordedOp)
+ : computedState(clipRect, dstRect)
, alpha(1.0f)
, roundRectClipState(nullptr)
, projectionPathMask(nullptr)
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 00560d7..a14bdc4 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -415,12 +415,7 @@
void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
mDisplayList->ref(tree);
- const SkBitmap& bitmap = tree->getBitmapUpdateIfDirty();
- SkPaint* paint = tree->getPaint();
- const SkRect bounds = tree->getBounds();
- addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap),
- 0, 0, bitmap.width(), bitmap.height(),
- bounds.left(), bounds.top(), bounds.right(), bounds.bottom(), refPaint(paint)));
+ addDrawOp(new (alloc()) DrawVectorDrawableOp(tree));
}
void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count,
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 98315d0..516e619 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -28,6 +28,7 @@
#include "UvMapper.h"
#include "utils/LinearAllocator.h"
#include "utils/PaintUtils.h"
+#include "VectorDrawable.h"
#include <algorithm>
@@ -1107,6 +1108,30 @@
float* mRadius;
};
+class DrawVectorDrawableOp : public DrawOp {
+public:
+ DrawVectorDrawableOp(VectorDrawableRoot* tree)
+ : DrawOp(nullptr), mTree(tree) {}
+
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ const SkBitmap& bitmap = mTree->getBitmapUpdateIfDirty();
+ SkPaint* paint = mTree->getPaint();
+ const SkRect bounds = mTree->getBounds();
+ renderer.drawBitmap(&bitmap, Rect(0, 0, bitmap.width(), bitmap.height()),
+ bounds, paint);
+ }
+
+ virtual void output(int level, uint32_t logFlags) const override {
+ OP_LOG("Draw Vector Drawable %p", mTree);
+ }
+
+ virtual const char* name() override { return "DrawVectorDrawable"; }
+
+private:
+ VectorDrawableRoot* mTree;
+
+};
+
class DrawOvalOp : public DrawStrokableOp {
public:
DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint)
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 4f51036..04de98a 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -782,39 +782,46 @@
boundsTransform.mapRect(dstRect);
dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip());
- // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume)
- OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr);
+ if (dstRect.isEmpty()) {
+ // Unclipped layer rejected - push a null op, so next EndUnclippedLayerOp is ignored
+ currentLayer().activeUnclippedSaveLayers.push_back(nullptr);
+ } else {
+ // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume)
+ OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr);
- /**
- * First, defer an operation to copy out the content from the rendertarget into a layer.
- */
- auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle);
- BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
- &(currentLayer().viewportClip), dstRect, *copyToOp);
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
+ /**
+ * First, defer an operation to copy out the content from the rendertarget into a layer.
+ */
+ auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle);
+ BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
+ &(currentLayer().repaintClip), dstRect, *copyToOp);
+ currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
- /**
- * Defer a clear rect, so that clears from multiple unclipped layers can be drawn
- * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible
- */
- currentLayer().deferLayerClear(dstRect);
+ /**
+ * Defer a clear rect, so that clears from multiple unclipped layers can be drawn
+ * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible
+ */
+ currentLayer().deferLayerClear(dstRect);
- /**
- * And stash an operation to copy that layer back under the rendertarget until
- * a balanced EndUnclippedLayerOp is seen
- */
- auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle);
- bakedState = BakedOpState::directConstruct(mAllocator,
- &(currentLayer().viewportClip), dstRect, *copyFromOp);
- currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
+ /**
+ * And stash an operation to copy that layer back under the rendertarget until
+ * a balanced EndUnclippedLayerOp is seen
+ */
+ auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle);
+ bakedState = BakedOpState::directConstruct(mAllocator,
+ &(currentLayer().repaintClip), dstRect, *copyFromOp);
+ currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
+ }
}
void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
- currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer);
currentLayer().activeUnclippedSaveLayers.pop_back();
+ if (copyFromLayerOp) {
+ currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer);
+ }
}
} // namespace uirenderer
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index c5d7191..bc39621 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -199,10 +199,10 @@
: width(width)
, height(height)
, repaintRect(repaintRect)
+ , repaintClip(repaintRect)
, offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
, beginLayerOp(beginLayerOp)
- , renderNode(renderNode)
- , viewportClip(Rect(width, height)) {}
+ , renderNode(renderNode) {}
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
@@ -260,7 +260,7 @@
Matrix4::identity(), nullptr, paint,
verts, vertCount);
BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
- &viewportClip, bounds, *op);
+ &repaintClip, bounds, *op);
deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
}
}
diff --git a/libs/hwui/LayerBuilder.h b/libs/hwui/LayerBuilder.h
index 99968e1..4a7ca2d 100644
--- a/libs/hwui/LayerBuilder.h
+++ b/libs/hwui/LayerBuilder.h
@@ -109,10 +109,10 @@
const uint32_t width;
const uint32_t height;
const Rect repaintRect;
+ const ClipRect repaintClip;
OffscreenBuffer* offscreenBuffer;
const BeginLayerOp* beginLayerOp;
const RenderNode* renderNode;
- const ClipRect viewportClip;
// list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
std::vector<BakedOpState*> activeUnclippedSaveLayers;
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index f49dd3f..f86898f 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -651,6 +651,62 @@
<< "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
}
+TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
+ class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
+ public:
+ void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ }
+ void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ ASSERT_NE(nullptr, op.paint);
+ EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
+ EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
+ << "Expect dirty rect as clip";
+ ASSERT_NE(nullptr, state.computedState.clipState);
+ EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
+ EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ };
+
+ auto node = TestUtils::createNode(0, 0, 200, 200,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ // save smaller than clip, so we get unclipped behavior
+ canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
+ canvas.drawRect(0, 0, 200, 200, SkPaint());
+ canvas.restore();
+ });
+
+ // draw with partial screen dirty, and assert we see that rect later
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
+ TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+ SaveLayerUnclippedClearClipTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex());
+}
+
+TEST(FrameBuilder, saveLayerUnclipped_reject) {
+ auto node = TestUtils::createNode(0, 0, 200, 200,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ // unclipped savelayer + rect both in area that won't intersect with dirty
+ canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
+ canvas.drawRect(100, 100, 200, 200, SkPaint());
+ canvas.restore();
+ });
+
+ // draw with partial screen dirty that doesn't intersect with savelayer
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+ TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+ FailRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+}
+
/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
* - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
* - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
diff --git a/location/java/android/location/GnssClock.java b/location/java/android/location/GnssClock.java
index 5ba3f2c..3c8a78d 100644
--- a/location/java/android/location/GnssClock.java
+++ b/location/java/android/location/GnssClock.java
@@ -16,42 +16,16 @@
package android.location;
-import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
/**
* A class containing a GPS clock timestamp.
* It represents a measurement of the GPS receiver's clock.
*/
public final class GnssClock implements Parcelable {
-
// The following enumerations must be in sync with the values declared in gps.h
- /** The type of the GPS Clock. */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({CLOCK_TYPE_UNKNOWN, CLOCK_TYPE_LOCAL_HW_TIME, CLOCK_TYPE_GPS_TIME})
- public @interface GnssClockType {}
-
- /**
- * The type of the time stored is not available or it is unknown.
- */
- public static final byte CLOCK_TYPE_UNKNOWN = 0;
-
- /**
- * The source of the time value reported by this class is the 'Local Hardware Clock'.
- */
- public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1;
-
- /**
- * The source of the time value reported by this class is the 'GPS time' derived from
- * satellites (epoch = Jan 6, 1980).
- */
- public static final byte CLOCK_TYPE_GPS_TIME = 2;
-
private static final short HAS_NO_FLAGS = 0;
private static final short HAS_LEAP_SECOND = (1<<0);
private static final short HAS_TIME_UNCERTAINTY = (1<<1);
@@ -65,7 +39,6 @@
private short mFlags;
private short mLeapSecond;
- private byte mType;
private long mTimeInNs;
private double mTimeUncertaintyInNs;
private long mFullBiasInNs;
@@ -85,7 +58,6 @@
public void set(GnssClock clock) {
mFlags = clock.mFlags;
mLeapSecond = clock.mLeapSecond;
- mType = clock.mType;
mTimeInNs = clock.mTimeInNs;
mTimeUncertaintyInNs = clock.mTimeUncertaintyInNs;
mFullBiasInNs = clock.mFullBiasInNs;
@@ -104,38 +76,6 @@
}
/**
- * Gets the type of time reported by {@link #getTimeInNs()}.
- */
- @GnssClockType
- public byte getType() {
- return mType;
- }
-
- /**
- * Sets the type of time reported.
- */
- public void setType(@GnssClockType byte value) {
- mType = value;
- }
-
- /**
- * Gets a string representation of the 'type'.
- * For internal and logging use only.
- */
- private String getTypeString() {
- switch (mType) {
- case CLOCK_TYPE_UNKNOWN:
- return "Unknown";
- case CLOCK_TYPE_GPS_TIME:
- return "GpsTime";
- case CLOCK_TYPE_LOCAL_HW_TIME:
- return "LocalHwClock";
- default:
- return "<Invalid:" + mType + ">";
- }
- }
-
- /**
* Returns true if {@link #getLeapSecond()} is available, false otherwise.
*/
public boolean hasLeapSecond() {
@@ -170,10 +110,7 @@
}
/**
- * Gets the GPS receiver internal clock value in nanoseconds.
- * This can be either the 'local hardware clock' value ({@link #CLOCK_TYPE_LOCAL_HW_TIME}), or the
- * current GPS time derived inside GPS receiver ({@link #CLOCK_TYPE_GPS_TIME}).
- * {@link #getType()} defines the time reported.
+ * Gets the GNSS receiver internal clock value in nanoseconds.
*
* For 'local hardware clock' this value is expected to be monotonically increasing during the
* reporting session. The real GPS time can be derived by compensating
@@ -241,15 +178,14 @@
* Gets the difference between hardware clock ({@link #getTimeInNs()}) inside GPS receiver and
* the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
*
- * This value is available if {@link #CLOCK_TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved
- * the clock for GPS time.
- * {@link #getBiasUncertaintyInNs()} should be used for quality check.
+ * This value is available if the receiver has estimated GPS time. If the computed time is for a
+ * non-GPS constellation, the time offset of that constellation to GPS has to be applied to fill
+ * this value. The value contains the 'bias uncertainty' {@link #getBiasUncertaintyInNs()} in
+ * it, and it should be used for quality check. The value is only available if
+ * {@link #hasFullBiasInNs()} is true.
*
* The sign of the value is defined by the following equation:
- * true time (GPS time) = time_ns + (full_bias_ns + bias_ns)
- *
- * The reported full bias includes {@link #getBiasUncertaintyInNs()}.
- * The value is onl available if {@link #hasFullBiasInNs()} is true.
+ * local estimate of GPS time = time_ns + (full_bias_ns + bias_ns)
*/
public long getFullBiasInNs() {
return mFullBiasInNs;
@@ -423,7 +359,6 @@
gpsClock.mFlags = (short) parcel.readInt();
gpsClock.mLeapSecond = (short) parcel.readInt();
- gpsClock.mType = parcel.readByte();
gpsClock.mTimeInNs = parcel.readLong();
gpsClock.mTimeUncertaintyInNs = parcel.readDouble();
gpsClock.mFullBiasInNs = parcel.readLong();
@@ -446,7 +381,6 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mFlags);
parcel.writeInt(mLeapSecond);
- parcel.writeByte(mType);
parcel.writeLong(mTimeInNs);
parcel.writeDouble(mTimeUncertaintyInNs);
parcel.writeLong(mFullBiasInNs);
@@ -468,8 +402,6 @@
final String formatWithUncertainty = " %-15s = %-25s %-26s = %s\n";
StringBuilder builder = new StringBuilder("GnssClock:\n");
- builder.append(String.format(format, "Type", getTypeString()));
-
builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
builder.append(String.format(
@@ -498,17 +430,12 @@
"DriftUncertaintyInNsPerSec",
hasDriftUncertaintyInNsPerSec() ? mDriftUncertaintyInNsPerSec : null));
- builder.append(String.format(format, "HardwareClockDiscontinuityCount",
- getType() == CLOCK_TYPE_LOCAL_HW_TIME
- ? mHardwareClockDiscontinuityCount : null));
-
return builder.toString();
}
private void initialize() {
mFlags = HAS_NO_FLAGS;
resetLeapSecond();
- setType(CLOCK_TYPE_UNKNOWN);
setTimeInNs(Long.MIN_VALUE);
resetTimeUncertaintyInNs();
resetFullBiasInNs();
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index a619ab2..abdc13c 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -272,8 +272,9 @@
/**
* Gets the time offset at which the measurement was taken in nanoseconds.
- * The reference receiver's time is specified by {@link GnssClock#getTimeInNs()} and should be
- * interpreted in the same way as indicated by {@link GnssClock#getType()}.
+ *
+ * The reference receiver's time from which this is offset is specified by
+ * {@link GnssClock#getTimeInNs()}.
*
* The sign of this value is given by the following equation:
* measurement time = time_ns + time_offset_ns
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 22f4f04..a0dfaa0f 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -335,6 +335,24 @@
CHANNEL_OUT_LOW_FREQUENCY);
// CHANNEL_OUT_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_OUT_ALL
+ /** Minimum value for sample rate,
+ * assuming AudioTrack and AudioRecord share the same limitations.
+ * @hide
+ */
+ // never unhide
+ public static final int SAMPLE_RATE_HZ_MIN = 4000;
+ /** Maximum value for sample rate,
+ * assuming AudioTrack and AudioRecord share the same limitations.
+ * @hide
+ */
+ // never unhide
+ public static final int SAMPLE_RATE_HZ_MAX = 192000;
+ /** Sample rate will be a route-dependent value.
+ * For AudioTrack, it is usually the sink sample rate,
+ * and for AudioRecord it is usually the source sample rate.
+ */
+ public static final int SAMPLE_RATE_UNSPECIFIED = 0;
+
/**
* @hide
* Return the input channel mask corresponding to an output channel mask.
@@ -561,7 +579,7 @@
}
/**
- * Constructor used by the JNI
+ * Constructor used by the JNI. Parameters are not checked for validity.
*/
// Update sound trigger JNI in core/jni/android_hardware_SoundTrigger.cpp when modifying this
// constructor
@@ -610,12 +628,9 @@
/**
* Return the sample rate.
* @return one of the values that can be set in {@link Builder#setSampleRate(int)} or
- * 0 if not set.
+ * {@link #SAMPLE_RATE_UNSPECIFIED} if not set.
*/
public int getSampleRate() {
- if ((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) == 0) {
- return 0;
- }
return mSampleRate;
}
@@ -687,7 +702,7 @@
*/
public static class Builder {
private int mEncoding = ENCODING_INVALID;
- private int mSampleRate = 0;
+ private int mSampleRate = SAMPLE_RATE_UNSPECIFIED;
private int mChannelMask = CHANNEL_INVALID;
private int mChannelIndexMask = 0;
private int mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_NONE;
@@ -718,6 +733,8 @@
public AudioFormat build() {
AudioFormat af = new AudioFormat(1980/*ignored*/);
af.mEncoding = mEncoding;
+ // not calling setSampleRate is equivalent to calling
+ // setSampleRate(SAMPLE_RATE_UNSPECIFIED)
af.mSampleRate = mSampleRate;
af.mChannelMask = mChannelMask;
af.mChannelIndexMask = mChannelIndexMask;
@@ -795,7 +812,7 @@
* are specified but do not have the same channel count.
*/
public @NonNull Builder setChannelMask(int channelMask) {
- if (channelMask == 0) {
+ if (channelMask == CHANNEL_INVALID) {
throw new IllegalArgumentException("Invalid zero channel mask");
} else if (/* channelMask != 0 && */ mChannelIndexMask != 0 &&
Integer.bitCount(channelMask) != Integer.bitCount(mChannelIndexMask)) {
@@ -867,7 +884,11 @@
* @throws java.lang.IllegalArgumentException
*/
public Builder setSampleRate(int sampleRate) throws IllegalArgumentException {
- if ((sampleRate <= 0) || (sampleRate > 192000)) {
+ // TODO Consider whether to keep the MIN and MAX range checks here.
+ // It is not necessary and poses the problem of defining the limits independently from
+ // native implementation or platform capabilities.
+ if (((sampleRate < SAMPLE_RATE_HZ_MIN) || (sampleRate > SAMPLE_RATE_HZ_MAX)) &&
+ sampleRate != SAMPLE_RATE_UNSPECIFIED) {
throw new IllegalArgumentException("Invalid sample rate " + sampleRate);
}
mSampleRate = sampleRate;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index dc534be..38e3b15 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3321,6 +3321,7 @@
* Used as a key for {@link #getProperty} to request the native or optimal output sample rate
* for this device's primary output stream, in decimal Hz.
*/
+ // FIXME Deprecate
public static final String PROPERTY_OUTPUT_SAMPLE_RATE =
"android.media.property.OUTPUT_SAMPLE_RATE";
@@ -3328,6 +3329,7 @@
* Used as a key for {@link #getProperty} to request the native or optimal output buffer size
* for this device's primary output stream, in decimal PCM frames.
*/
+ // FIXME Deprecate
public static final String PROPERTY_OUTPUT_FRAMES_PER_BUFFER =
"android.media.property.OUTPUT_FRAMES_PER_BUFFER";
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index e342385..b8f0717 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -57,10 +57,6 @@
// Constants
//--------------------
- /** Minimum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MIN = 4000;
- /** Maximum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MAX = 192000;
/**
* indicates AudioRecord state is not successfully initialized.
@@ -168,8 +164,9 @@
//--------------------
/**
* The audio data sampling rate in Hz.
+ * Never {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED}.
*/
- private int mSampleRate;
+ private int mSampleRate; // initialized by all constructors via audioParamCheck()
/**
* The number of input audio channels (1 is mono, 2 is stereo)
*/
@@ -251,6 +248,9 @@
* @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only
* rate that is guaranteed to work on all devices, but other rates such as 22050,
* 16000, and 11025 may work on some devices.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
+ * which is usually the sample rate of the source.
+ * {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_IN_MONO} and
* {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
@@ -337,16 +337,9 @@
mAudioAttributes = attributes;
}
- int rate = 0;
- if ((format.getPropertySetMask()
- & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
- {
- rate = format.getSampleRate();
- } else {
- rate = AudioSystem.getPrimaryOutputSamplingRate();
- if (rate <= 0) {
- rate = 44100;
- }
+ int rate = format.getSampleRate();
+ if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
+ rate = 0;
}
int encoding = AudioFormat.ENCODING_DEFAULT;
@@ -373,19 +366,21 @@
audioBuffSizeCheck(bufferSizeInBytes);
+ int[] sampleRate = new int[] {mSampleRate};
int[] session = new int[1];
session[0] = sessionId;
//TODO: update native initialization when information about hardware init failure
// due to capture device already open is available.
int initResult = native_setup( new WeakReference<AudioRecord>(this),
- mAudioAttributes, mSampleRate, mChannelMask, mChannelIndexMask,
+ mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
mAudioFormat, mNativeBufferSizeInBytes,
- session, ActivityThread.currentOpPackageName());
+ session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing native AudioRecord object.");
return; // with mState == STATE_UNINITIALIZED
}
+ mSampleRate = sampleRate[0];
mSessionId = session[0];
mState = STATE_INITIALIZED;
@@ -395,12 +390,32 @@
* A constructor which explicitly connects a Native (C++) AudioRecord. For use by
* the AudioRecordRoutingProxy subclass.
* @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord
- * (associated with an OpenSL ES recorder).
+ * (associated with an OpenSL ES recorder). Note: the caller must ensure a correct
+ * value here as no error checking is or can be done.
*/
/*package*/ AudioRecord(long nativeRecordInJavaObj) {
- mNativeRecorderInJavaObj = nativeRecordInJavaObj;
+ int[] session = { 0 };
+ int[] rates = { 0 };
+ //TODO: update native initialization when information about hardware init failure
+ // due to capture device already open is available.
+ // Note that for this native_setup, we are providing an already created/initialized
+ // *Native* AudioRecord, so the attributes parameters to native_setup() are ignored.
+ int initResult = native_setup(new WeakReference<AudioRecord>(this),
+ null /*mAudioAttributes*/,
+ rates /*mSampleRates*/,
+ 0 /*mChannelMask*/,
+ 0 /*mChannelIndexMask*/,
+ 0 /*mAudioFormat*/,
+ 0 /*mNativeBufferSizeInBytes*/,
+ session,
+ ActivityThread.currentOpPackageName(),
+ nativeRecordInJavaObj);
+ if (initResult != SUCCESS) {
+ loge("Error code "+initResult+" when initializing native AudioRecord object.");
+ return; // with mState == STATE_UNINITIALIZED
+ }
- // other initialization here...
+ mSessionId = session[0];
mState = STATE_INITIALIZED;
}
@@ -623,6 +638,7 @@
return mask;
}
+
// postconditions:
// mRecordSource is valid
// mAudioFormat is valid
@@ -642,7 +658,9 @@
//--------------
// sample rate
- if ((sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX)) {
+ if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
+ sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
+ sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
throw new IllegalArgumentException(sampleRateInHz
+ "Hz is not a supported sample rate.");
}
@@ -714,7 +732,11 @@
// Getters
//--------------------
/**
- * Returns the configured audio data sample rate in Hz
+ * Returns the configured audio sink sample rate in Hz.
+ * The sink sample rate never changes after construction.
+ * If the constructor had a specific sample rate, then the sink sample rate is that value.
+ * If the constructor had {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED},
+ * then the sink sample rate is a route-dependent default value based on the source [sic].
*/
public int getSampleRate() {
return mSampleRate;
@@ -861,6 +883,7 @@
* See {@link #AudioRecord(int, int, int, int, int)} for more information on valid
* configuration values.
* @param sampleRateInHz the sample rate expressed in Hertz.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_IN_MONO} and
* {@link AudioFormat#CHANNEL_IN_STEREO}
@@ -1708,8 +1731,9 @@
private native final int native_setup(Object audiorecord_this,
Object /*AudioAttributes*/ attributes,
- int sampleRate, int channelMask, int channelIndexMask, int audioFormat,
- int buffSizeInBytes, int[] sessionId, String opPackageName);
+ int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
+ int buffSizeInBytes, int[] sessionId, String opPackageName,
+ long nativeRecordInJavaObj);
// TODO remove: implementation calls directly into implementation of native_release()
private native final void native_finalize();
diff --git a/media/java/android/media/AudioRouting.java b/media/java/android/media/AudioRouting.java
index 2161cf3..41f92d4 100644
--- a/media/java/android/media/AudioRouting.java
+++ b/media/java/android/media/AudioRouting.java
@@ -41,6 +41,14 @@
public AudioDeviceInfo getPreferredDevice();
/**
+ * Returns an {@link AudioDeviceInfo} identifying the current routing of this
+ * AudioTrack/AudioRecord.
+ * Note: The query is only valid if the AudioTrack/AudioRecord is currently playing.
+ * If it is not, <code>getRoutedDevice()</code> will return null.
+ */
+ public AudioDeviceInfo getRoutedDevice();
+
+ /**
* Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
* changes on this AudioTrack/AudioRecord.
* @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 6fc2f87..f02e8375 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -91,11 +91,6 @@
*/
private static final float GAIN_MAX = 1.0f;
- /** Minimum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MIN = 4000;
- /** Maximum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MAX = 192000;
-
/** Maximum value for AudioTrack channel count
* @hide public for MediaCode only, do not un-hide or change to a numeric literal
*/
@@ -254,6 +249,7 @@
private final Looper mInitializationLooper;
/**
* The audio data source sampling rate in Hz.
+ * Never {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED}.
*/
private int mSampleRate; // initialized by all constructors via audioParamCheck()
/**
@@ -340,6 +336,9 @@
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
* {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
* @param sampleRateInHz the initial source sample rate expressed in Hz.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
+ * which is usually the sample rate of the sink.
+ * {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -389,6 +388,8 @@
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
* {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
* @param sampleRateInHz the initial source sample rate expressed in Hz.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
+ * which is usually the sample rate of the sink.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -461,16 +462,11 @@
looper = Looper.getMainLooper();
}
- int rate = 0;
- if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
- {
- rate = format.getSampleRate();
- } else {
- rate = AudioSystem.getPrimaryOutputSamplingRate();
- if (rate <= 0) {
- rate = 44100;
- }
+ int rate = format.getSampleRate();
+ if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
+ rate = 0;
}
+
int channelIndexMask = 0;
if ((format.getPropertySetMask()
& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0) {
@@ -503,17 +499,19 @@
throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
}
+ int[] sampleRate = new int[] {mSampleRate};
int[] session = new int[1];
session[0] = sessionId;
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
- mSampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
- mNativeBufferSizeInBytes, mDataLoadMode, session);
+ sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
+ mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
}
+ mSampleRate = sampleRate[0];
mSessionId = session[0];
if (mDataLoadMode == MODE_STATIC) {
@@ -530,8 +528,6 @@
* (associated with an OpenSL ES player).
*/
/*package*/ AudioTrack(long nativeTrackInJavaObj) {
- mNativeTrackInJavaObj = nativeTrackInJavaObj;
-
// "final"s
mAttributes = null;
mAppOps = null;
@@ -544,6 +540,26 @@
mInitializationLooper = looper;
// other initialization...
+ // Note that for this native_setup, we are providing an already created/initialized
+ // *Native* AudioTrack, so the attributes parameters to native_setup() are ignored.
+ int[] session = { 0 };
+ int[] rates = { 0 };
+ int initResult = native_setup(new WeakReference<AudioTrack>(this),
+ null /*mAttributes - NA*/,
+ rates /*sampleRate - NA*/,
+ 0 /*mChannelMask - NA*/,
+ 0 /*mChannelIndexMask - NA*/,
+ 0 /*mAudioFormat - NA*/,
+ 0 /*mNativeBufferSizeInBytes - NA*/,
+ 0 /*mDataLoadMode - NA*/,
+ session,
+ nativeTrackInJavaObj);
+ if (initResult != SUCCESS) {
+ loge("Error code "+initResult+" when initializing AudioTrack.");
+ return; // with mState == STATE_UNINITIALIZED
+ }
+
+ mSessionId = session[0];
mState = STATE_INITIALIZED;
}
@@ -712,7 +728,7 @@
if (mFormat == null) {
mFormat = new AudioFormat.Builder()
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
- .setSampleRate(AudioSystem.getPrimaryOutputSamplingRate())
+ //.setSampleRate(AudioFormat.SAMPLE_RATE_UNSPECIFIED)
.setEncoding(AudioFormat.ENCODING_DEFAULT)
.build();
}
@@ -762,7 +778,9 @@
int audioFormat, int mode) {
//--------------
// sample rate, note these values are subject to change
- if (sampleRateInHz < SAMPLE_RATE_HZ_MIN || sampleRateInHz > SAMPLE_RATE_HZ_MAX) {
+ if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
+ sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
+ sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
throw new IllegalArgumentException(sampleRateInHz
+ "Hz is not a supported sample rate.");
}
@@ -948,7 +966,13 @@
}
/**
- * Returns the configured audio data sample rate in Hz
+ * Returns the configured audio source sample rate in Hz.
+ * The initial source sample rate depends on the constructor parameters,
+ * but the source sample rate may change if {@link #setPlaybackRate(int)} is called.
+ * If the constructor had a specific sample rate, then the initial sink sample rate is that
+ * value.
+ * If the constructor had {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED},
+ * then the initial sink sample rate is a route-dependent default value based on the source [sic].
*/
public int getSampleRate() {
return mSampleRate;
@@ -1218,6 +1242,7 @@
* to a higher value than the initial source sample rate, be sure to configure the buffer size
* based on the highest planned sample rate.
* @param sampleRateInHz the source sample rate expressed in Hz.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -1255,7 +1280,9 @@
}
// sample rate, note these values are subject to change
- if ( (sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX) ) {
+ // Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed
+ if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||
+ (sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {
loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate.");
return ERROR_BAD_VALUE;
}
@@ -2763,8 +2790,8 @@
// AudioAttributes.USAGE_MEDIA will map to AudioManager.STREAM_MUSIC
private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this,
Object /*AudioAttributes*/ attributes,
- int sampleRate, int channelMask, int channelIndexMask, int audioFormat,
- int buffSizeInBytes, int mode, int[] sessionId);
+ int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
+ int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack);
private native final void native_finalize();
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 4ffac6d..55fb82b 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -277,6 +277,7 @@
mRouteFlags = ROUTE_FLAG_RENDER;
}
if (mFormat == null) {
+ // FIXME Can we eliminate this? Will AudioMix work with an unspecified sample rate?
int rate = AudioSystem.getPrimaryOutputSamplingRate();
if (rate <= 0) {
rate = 44100;
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 13db00e..622900f 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -592,10 +592,11 @@
}
/**
- * Request that the player prepare its playback. Once the preparation is done, the session
- * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
- * {@link #play} can be called to start playback. If the preparation is not needed,
- * {@link #play} can be directly called without this method.
+ * Request that the player prepare its playback. In other words, other sessions can continue
+ * to play during the preparation of this session. This method can be used to speed up the
+ * start of the playback. Once the preparation is done, the session will change its playback
+ * state to {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to
+ * start playback.
*/
public void prepare() {
try {
@@ -606,10 +607,12 @@
}
/**
- * Request that the player prepare playback for a specific media id. Once the preparation is
- * done, the session will change its playback state to {@link PlaybackState#STATE_PAUSED}.
- * Afterwards, {@link #play} can be called to start playback. If the preparation is not
- * needed, {@link #playFromMediaId} can be directly called without this method.
+ * Request that the player prepare playback for a specific media id. In other words, other
+ * sessions can continue to play during the preparation of this session. This method can be
+ * used to speed up the start of the playback. Once the preparation is done, the session
+ * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
+ * {@link #play} can be called to start playback. If the preparation is not needed,
+ * {@link #playFromMediaId} can be directly called without this method.
*
* @param mediaId The id of the requested media.
* @param extras Optional extras that can include extra information about the media item
@@ -628,12 +631,13 @@
}
/**
- * Request that the player prepare playback for a specific search query.
- * An empty or null query should be treated as a request to prepare any
- * music. Once the preparation is done, the session will change its playback state to
- * {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to start
- * playback. If the preparation is not needed, {@link #playFromSearch} can be directly
- * called without this method.
+ * Request that the player prepare playback for a specific search query. An empty or null
+ * query should be treated as a request to prepare any music. In other words, other sessions
+ * can continue to play during the preparation of this session. This method can be used to
+ * speed up the start of the playback. Once the preparation is done, the session will
+ * change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
+ * {@link #play} can be called to start playback. If the preparation is not needed,
+ * {@link #playFromSearch} can be directly called without this method.
*
* @param query The search query.
* @param extras Optional extras that can include extra information
@@ -653,11 +657,12 @@
}
/**
- * Request that the player prepare playback for a specific {@link Uri}.
- * Once the preparation is done, the session will change its playback state to
- * {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to start
- * playback. If the preparation is not needed, {@link #playFromUri} can be directly
- * called without this method.
+ * Request that the player prepare playback for a specific {@link Uri}. In other words,
+ * other sessions can continue to play during the preparation of this session. This method
+ * can be used to speed up the start of the playback. Once the preparation is done, the
+ * session will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
+ * {@link #play} can be called to start playback. If the preparation is not needed,
+ * {@link #playFromUri} can be directly called without this method.
*
* @param uri The URI of the requested media.
* @param extras Optional extras that can include extra information about the media item
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 9073077..0bd1713 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -830,40 +830,45 @@
}
/**
- * Override to handle requests to prepare playback. The state of playback should be updated
- * to {@link PlaybackState#STATE_PAUSED} after the preparation is done. Override
- * {@link #onPlay} to handle requests for starting playback of prepared content.
+ * Override to handle requests to prepare playback. During the preparation, a session should
+ * not hold audio focus in order to allow other sessions play seamlessly. The state of
+ * playback should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is
+ * done.
*/
public void onPrepare() {
}
/**
* Override to handle requests to prepare for playing a specific mediaId that was provided
- * by your app's {@link MediaBrowserService}. The state of playback should be updated
- * to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback of
- * the prepared content should start in the implementation of {@link #onPlay}. Override
- * {@link #onPlayFromMediaId} to handle requests for starting playback without preparation.
+ * by your app's {@link MediaBrowserService}. During the preparation, a session should not
+ * hold audio focus in order to allow other sessions play seamlessly. The state of playback
+ * should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+ * The playback of the prepared content should start in the implementation of
+ * {@link #onPlay}. Override {@link #onPlayFromMediaId} to handle requests for starting
+ * playback without preparation.
*/
public void onPrepareFromMediaId(String mediaId, Bundle extras) {
}
/**
- * Override to handle requests to prepare playback from a search query. An
- * empty query indicates that the app may prepare any music. The
- * implementation should attempt to make a smart choice about what to
- * play. The state of playback should be updated to {@link PlaybackState#STATE_PAUSED}
- * after the preparation is done. The playback of the prepared content should start
- * in the implementation of {@link #onPlay}. Override {@link #onPlayFromSearch}
- * to handle requests for starting playback without preparation.
+ * Override to handle requests to prepare playback from a search query. An empty query
+ * indicates that the app may prepare any music. The implementation should attempt to make a
+ * smart choice about what to play. During the preparation, a session should not hold audio
+ * focus in order to allow other sessions play seamlessly. The state of playback should be
+ * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback
+ * of the prepared content should start in the implementation of {@link #onPlay}. Override
+ * {@link #onPlayFromSearch} to handle requests for starting playback without preparation.
*/
public void onPrepareFromSearch(String query, Bundle extras) {
}
/**
* Override to handle requests to prepare a specific media item represented by a URI.
- * The state of playback should be updated to {@link PlaybackState#STATE_PAUSED}
- * after the preparation is done. The playback of the prepared content should start in
- * the implementation of {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests
+ * During the preparation, a session should not hold audio focus in order to allow
+ * other sessions play seamlessly. The state of playback should be updated to
+ * {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+ * The playback of the prepared content should start in the implementation of
+ * {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests
* for starting playback without preparation.
*/
public void onPrepareFromUri(Uri uri, Bundle extras) {
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index b8a1374..ec65ffe 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -105,7 +105,8 @@
public static final int TYPE_DISPLAY_PORT = 1008;
/**
- * The ID of the TV input to provide to the setup activity and settings activity.
+ * Used as a String extra field in setup intents created by {@link #createSetupIntent()} to
+ * supply the ID of a specific TV input to set up.
*/
public static final String EXTRA_INPUT_ID = "android.media.tv.extra.INPUT_ID";
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 7089c00..2703b1a 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -266,6 +266,14 @@
public static final String META_DATA_CONTENT_RATING_SYSTEMS =
"android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
+ /**
+ * Activity action to set up channel sources i.e. TV inputs of type
+ * {@link TvInputInfo#TYPE_TUNER}. When invoked, the system will display an appropriate UI for
+ * the user to initiate the individual setup flow provided by
+ * {@link android.R.attr#setupActivity} of each TV input service.
+ */
+ public static final String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
+
private final ITvInputManager mService;
private final Object mLock = new Object();
diff --git a/opengl/java/android/opengl/GLES30.java b/opengl/java/android/opengl/GLES30.java
index 342ffa4..9c3b505 100644
--- a/opengl/java/android/opengl/GLES30.java
+++ b/opengl/java/android/opengl/GLES30.java
@@ -1791,4 +1791,16 @@
java.nio.IntBuffer params
);
+ // C function void glReadPixels ( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint offset )
+
+ public static native void glReadPixels(
+ int x,
+ int y,
+ int width,
+ int height,
+ int format,
+ int type,
+ int offset
+ );
+
}
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 1a4e3eb..e1650e1 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -8,6 +8,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
# The design lib requires that the client package use appcompat themes.
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
# Supplies material design components, e.g. Snackbar.
LOCAL_STATIC_JAVA_LIBRARIES += android-support-design
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index 07498a0..8a76540 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -19,7 +19,7 @@
<bool name="config_defaultAdvancedDevices">false</bool>
<!-- Intentionally unset. Vendors should set this in an overlay. -->
- <string name="trusted_quick_viewer_package"></string>
+ <string name="trusted_quick_viewer_package" translatable="false"></string>
<bool name="list_divider_inset_left">true</bool>
<bool name="always_show_summary">false</bool>
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index b97918e..3dc111a 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -134,6 +134,8 @@
<string name="copy_notification_title">Copying files</string>
<!-- Title of the move notification [CHAR LIMIT=24] -->
<string name="move_notification_title">Moving files</string>
+ <!-- Title of the move notification [CHAR LIMIT=24] -->
+ <string name="delete_notification_title">Deleting files</string>
<!-- Text shown on the copy notification to indicate remaining time, in minutes [CHAR LIMIT=24] -->
<string name="copy_remaining"><xliff:g id="duration" example="3 minutes">%s</xliff:g> left</string>
<!-- Toast shown when a file copy is kicked off -->
@@ -206,4 +208,11 @@
<string name="allow">Allow</string>
<!-- Text in the button asking user to deny access to a given directory. -->
<string name="deny">Deny</string>
+ <!-- Dialog title shown to users when asking if they want to delete files (a confirmation). -->
+ <string name="delete_confirmation_title">Delete files?</string>
+ <!-- Dialog text shown to users when asking if they want to delete files (a confirmation). -->
+ <plurals name="delete_confirmation_message">
+ <item quantity="one">Are you sure you want to delete <xliff:g id="count" example="1">%1$d</xliff:g> file?</item>
+ <item quantity="other">Are you sure you want to delete <xliff:g id="count" example="3">%1$d</xliff:g> files?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index cb6cf8d..6211085 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -31,6 +31,7 @@
import android.annotation.StringRes;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.AlertDialog;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
@@ -38,6 +39,7 @@
import android.content.ClipData;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
@@ -52,6 +54,7 @@
import android.provider.DocumentsContract.Document;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
+import android.support.v13.view.DragStartHelper;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
import android.support.v7.widget.RecyclerView;
@@ -72,7 +75,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewParent;
import android.widget.ImageView;
import android.widget.TextView;
@@ -149,9 +151,6 @@
private static final String TAG = "DirectoryFragment";
private static final int LOADER_ID = 42;
- private static final int DELETE_UNDO_TIMEOUT = 5000;
- private static final int DELETE_JOB_DELAY = 5500;
- private static final int EMPTY_REVEAL_DURATION = 250;
private Model mModel;
private MultiSelectManager mSelectionManager;
@@ -258,7 +257,8 @@
}
mRecView.setLayoutManager(mLayout);
- mGestureDetector = new ListeningGestureDetector(this.getContext(), new GestureListener());
+ mGestureDetector =
+ new ListeningGestureDetector(this.getContext(), mDragHelper, new GestureListener());
mRecView.addOnItemTouchListener(mGestureDetector);
@@ -703,46 +703,28 @@
final DocumentInfo srcParent = getDisplayState().stack.peek();
new GetDocumentsTask() {
@Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- // Hide the files in the UI.
- final SparseArray<String> hidden = mAdapter.hide(selected.getAll());
-
- checkState(DELETE_JOB_DELAY > DELETE_UNDO_TIMEOUT);
- String operationId = FileOperations.delete(
- getActivity(), docs, srcParent, getDisplayState().stack,
- DELETE_JOB_DELAY);
- showDeleteSnackbar(hidden, operationId);
- }
- }.execute(selected);
- }
-
- private void showDeleteSnackbar(final SparseArray<String> hidden, final String jobId) {
-
- Context context = getActivity();
- String message = Shared.getQuantityString(context, R.plurals.deleting, hidden.size());
-
- // Show a snackbar informing the user that files will be deleted, and give them an option to
- // cancel.
- final Activity activity = getActivity();
- Snackbars.makeSnackbar(activity, message, DELETE_UNDO_TIMEOUT)
- .setAction(
- R.string.undo,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {}
- })
- .setCallback(
- new Snackbar.Callback() {
- @Override
- public void onDismissed(Snackbar snackbar, int event) {
- if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) {
- // If the delete was cancelled, just unhide the files.
- FileOperations.cancel(activity, jobId);
- mAdapter.unhide(hidden);
- }
+ void onDocumentsReady(final List<DocumentInfo> docs) {
+ new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.delete_confirmation_title)
+ .setMessage(
+ Shared.getQuantityString(
+ getActivity(),
+ R.plurals.delete_confirmation_message,
+ docs.size()))
+ .setPositiveButton(
+ android.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // Hide the files in the UI.
+ mAdapter.hide(selected.getAll());
+ FileOperations.delete(
+ getActivity(), docs, srcParent, getDisplayState().stack);
}
})
- .show();
+ .setNegativeButton(android.R.string.no, null)
+ .show();
+ }
+ }.execute(selected);
}
private void transferDocuments(final Selection selected, final @OpType int mode) {
@@ -1051,7 +1033,7 @@
view.setOnDragListener(mOnDragListener);
}
- view.setOnLongClickListener(mLongClickListener);
+ view.setOnLongClickListener(mDragHelper);
}
private View.OnDragListener mOnDragListener = new View.OnDragListener() {
@@ -1062,11 +1044,15 @@
// TODO: Check if the event contains droppable data.
return true;
- // TODO: Highlight potential drop target directory?
// TODO: Expand drop target directory on hover?
case DragEvent.ACTION_DRAG_ENTERED:
- case DragEvent.ACTION_DRAG_LOCATION:
+ setDropTargetHighlight(v, true);
+ return true;
case DragEvent.ACTION_DRAG_EXITED:
+ setDropTargetHighlight(v, false);
+ return true;
+
+ case DragEvent.ACTION_DRAG_LOCATION:
case DragEvent.ACTION_DRAG_ENDED:
return true;
@@ -1080,10 +1066,24 @@
// TODO: Do not drop into the directory where the documents came from.
}
copyFromClipData(event.getClipData(), dstDir);
+ // Clean up the UI.
+ setDropTargetHighlight(v, false);
+ mSelectionManager.clearSelection();
return true;
}
return false;
}
+
+ private void setDropTargetHighlight(View v, boolean highlight) {
+ // Note: use exact comparison - this code is searching for views which are children of
+ // the RecyclerView instance in the UI.
+ if (v.getParent() == mRecView) {
+ RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(v);
+ if (vh instanceof DocumentHolder) {
+ ((DocumentHolder) vh).setHighlighted(highlight);
+ }
+ }
+ }
};
/**
@@ -1109,21 +1109,14 @@
* a document item view.
*/
private String getModelId(View view) {
- while (true) {
- if (view.getLayoutParams() instanceof RecyclerView.LayoutParams) {
- RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(view);
- if (vh instanceof DocumentHolder) {
- return ((DocumentHolder) vh).modelId;
- } else {
- return null;
- }
+ View itemView = mRecView.findContainingItemView(view);
+ if (itemView != null) {
+ RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(itemView);
+ if (vh instanceof DocumentHolder) {
+ return ((DocumentHolder) vh).modelId;
}
- ViewParent parent = view.getParent();
- if (parent == null || !(parent instanceof View)) {
- return null;
- }
- view = (View) parent;
}
+ return null;
}
private List<DocumentInfo> getDraggableDocuments(View currentItemView) {
@@ -1304,10 +1297,10 @@
}
}
- private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
+ private DragStartHelper mDragHelper = new DragStartHelper(null) {
@Override
- public boolean onLongClick(View v) {
- if (mGestureDetector.mouseSpawnedLastEvent()) {
+ protected boolean onDragStart(View v) {
+ if (isSelected(getModelId(v))) {
List<DocumentInfo> docs = getDraggableDocuments(v);
if (docs.isEmpty()) {
return false;
@@ -1333,9 +1326,12 @@
implements OnItemTouchListener {
private int mLastTool = -1;
+ private DragStartHelper mDragHelper;
- public ListeningGestureDetector(Context context, GestureListener listener) {
+ public ListeningGestureDetector(
+ Context context, DragStartHelper dragHelper, GestureListener listener) {
super(context, listener);
+ mDragHelper = dragHelper;
setOnDoubleTapListener(listener);
}
@@ -1350,12 +1346,27 @@
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
mLastTool = e.getToolType(0);
- onTouchEvent(e); // bounce this forward to our detecty heart
+
+ // Detect drag events. When a drag is detected, intercept the rest of the gesture.
+ View itemView = rv.findChildViewUnder(e.getX(), e.getY());
+ if (itemView != null && mDragHelper.handleTouch(itemView, e)) {
+ return true;
+ }
+ // Forward unhandled events to the GestureDetector.
+ onTouchEvent(e);
+
return false;
}
@Override
- public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
+ public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+ View itemView = rv.findChildViewUnder(e.getX(), e.getY());
+ mDragHelper.handleTouch(itemView, e);
+ // Note: even though this event is being handled as part of a drag gesture, continue
+ // forwarding to the GestureDetector. The detector needs to see the entire cluster of
+ // events in order to properly interpret gestures.
+ onTouchEvent(e);
+ }
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
index 1bfc6e9..2967a90 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
@@ -75,11 +75,25 @@
*/
public abstract void bind(Cursor cursor, String modelId, State state);
+ /**
+ * Makes the associated item view appear selected. Note that this merely affects the appearance
+ * of the view, it doesn't actually select the item.
+ *
+ * @param selected
+ */
public void setSelected(boolean selected) {
itemView.setActivated(selected);
itemView.setBackgroundColor(selected ? mSelectedItemColor : mDefaultItemColor);
}
+ /**
+ * Highlights the associated item view.
+ * @param highlighted
+ */
+ public void setHighlighted(boolean highlighted) {
+ itemView.setBackgroundColor(highlighted ? mSelectedItemColor : mDefaultItemColor);
+ }
+
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// Event listener should always be set.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
index 844d07a..faedd5e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
@@ -67,12 +67,15 @@
import java.util.List;
class CopyJob extends Job {
+
private static final String TAG = "CopyJob";
- private static final int PROGRESS_INTERVAL_MILLIS = 1000;
+ private static final int PROGRESS_INTERVAL_MILLIS = 500;
+
final List<DocumentInfo> mSrcs;
final ArrayList<DocumentInfo> convertedFiles = new ArrayList<>();
private long mStartTime = -1;
+
private long mBatchSize;
private long mBytesCopied;
private long mLastNotificationTime;
@@ -496,8 +499,8 @@
try {
while ((len = in.read(buffer)) != -1) {
if (isCanceled()) {
- throw new ResourceException("Canceled copy mid-copy of %s",
- src.derivedUri);
+ if (DEBUG) Log.d(TAG, "Canceled copy mid-copy of: " + src.derivedUri);
+ return;
}
out.write(buffer, 0, len);
makeCopyProgress(len);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
index 25bca4f..8f45162 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
@@ -57,8 +57,8 @@
@Override
Builder createProgressBuilder() {
return super.createProgressBuilder(
- service.getString(R.string.move_notification_title),
- R.drawable.ic_menu_copy,
+ service.getString(R.string.delete_notification_title),
+ R.drawable.ic_menu_delete,
service.getString(android.R.string.cancel),
R.drawable.ic_cab_cancel);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
index 9d017ee..b53e165 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
@@ -22,7 +22,6 @@
import static com.android.documentsui.Shared.asArrayList;
import static com.android.documentsui.Shared.getQuantityString;
import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL;
-import static com.android.documentsui.services.FileOperationService.EXTRA_DELAY;
import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID;
import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION;
import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST;
@@ -165,19 +164,16 @@
* Use {@link #createJobId} if you don't have one handy.
* @param srcDocs A list of src files to copy.
* @param srcParent Parent of all the source documents.
- * @param delay Number of milliseconds to wait before executing the job.
* @return Id of the job.
*/
public static String delete(
Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent,
- DocumentStack location, int delay) {
+ DocumentStack location) {
String jobId = createJobId();
- if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id " + jobId
- + " delayed by " + delay + " milliseconds.");
+ if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id " + jobId + ".");
Intent intent = createBaseIntent(OPERATION_DELETE, activity, jobId, srcDocs, srcParent,
location);
- intent.putExtra(EXTRA_DELAY, delay);
activity.startService(intent);
return jobId;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
index afb3374..a158654 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
@@ -201,8 +201,8 @@
}
Notification getSetupNotification(String content) {
- mProgressBuilder.setProgress(0, 0, true);
- mProgressBuilder.setContentText(content);
+ mProgressBuilder.setProgress(0, 0, true)
+ .setContentText(content);
return mProgressBuilder.build();
}
@@ -221,6 +221,7 @@
.setCategory(Notification.CATEGORY_ERROR)
.setSmallIcon(icon)
.setAutoCancel(true);
+
return errorBuilder.build();
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 38e1257..498471e 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -119,24 +119,23 @@
device.waitForIdle();
bots.main.menuDelete().click();
- bots.directory.waitForDeleteSnackbar();
+ bots.main.findDialogOkButton().click();
+
bots.directory.assertDocumentsAbsent("file1.png");
+ }
- bots.directory.waitForDeleteSnackbarGone();
- bots.directory.assertDocumentsAbsent("file1.png");
+ public void testDeleteDocument_Cancel() throws Exception {
+ initTestFiles();
- // Now delete from another root.
- bots.roots.openRoot(ROOT_1_ID);
+ bots.roots.openRoot(ROOT_0_ID);
- bots.directory.clickDocument("poodles.text");
+ bots.directory.clickDocument("file1.png");
device.waitForIdle();
bots.main.menuDelete().click();
- bots.directory.waitForDeleteSnackbar();
- bots.directory.assertDocumentsAbsent("poodles.text");
+ bots.main.findDialogCancelButton().click();
- bots.directory.waitForDeleteSnackbarGone();
- bots.directory.assertDocumentsAbsent("poodles.text");
+ bots.directory.assertDocumentsPresent("file1.png");
}
// Tests that pressing tab switches focus between the roots and directory listings.
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
index 2833418..5f33b32 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
@@ -80,7 +80,7 @@
bots.main.setDialogText(newName);
device.waitForIdle(TIMEOUT);
- bots.main.findRenameDialogOkButton().click();
+ bots.main.findDialogOkButton().click();
device.waitForIdle(TIMEOUT);
bots.directory.assertDocumentsAbsent(fileName1);
@@ -108,7 +108,7 @@
bots.main.setDialogText(newName);
device.waitForIdle(TIMEOUT);
- bots.main.findRenameDialogCancelButton().click();
+ bots.main.findDialogCancelButton().click();
device.waitForIdle(TIMEOUT);
bots.directory.assertDocumentsPresent(fileName1);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
index fe2a3c3..c112867 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
@@ -168,12 +168,16 @@
return findObject("android:id/content", "android:id/text1");
}
- public UiObject findRenameDialogOkButton() {
- return findObject("android:id/content", "android:id/button1");
+ public UiObject findDialogOkButton() {
+ UiObject object = findObject("android:id/content", "android:id/button1");
+ object.waitForExists(mTimeout);
+ return object;
}
- public UiObject findRenameDialogCancelButton() {
- return findObject("android:id/content", "android:id/button2");
+ public UiObject findDialogCancelButton() {
+ UiObject object = findObject("android:id/content", "android:id/button2");
+ object.waitForExists(mTimeout);
+ return object;
}
UiObject findMenuLabelWithName(String label) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
index 6a98405..38435f4 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
@@ -133,6 +133,8 @@
return mCallback.readObjectBytes(inode, offset, size, mBuffer);
} catch (IOException e) {
return -OsConstants.EIO;
+ } catch (UnsupportedOperationException e) {
+ return -OsConstants.ENOTSUP;
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index 9578e6b..4bed003 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -89,7 +89,8 @@
* @return If roots are added or removed from the database.
* @throws FileNotFoundException
*/
- synchronized boolean putStorageDocuments(String parentDocumentId, MtpRoot[] roots)
+ synchronized boolean putStorageDocuments(
+ String parentDocumentId, int[] operationsSupported, MtpRoot[] roots)
throws FileNotFoundException {
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
database.beginTransaction();
@@ -100,7 +101,11 @@
valuesList[i] = new ContentValues();
extraValuesList[i] = new ContentValues();
MtpDatabase.getStorageDocumentValues(
- valuesList[i], extraValuesList[i], parentDocumentId, roots[i]);
+ valuesList[i],
+ extraValuesList[i],
+ parentDocumentId,
+ operationsSupported,
+ roots[i]);
}
final boolean changed = putDocuments(
parentDocumentId,
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index e14109a..701543b 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -689,9 +689,7 @@
values.putNull(Document.COLUMN_SIZE);
extraValues.clear();
- extraValues.put(
- Root.COLUMN_FLAGS,
- Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
+ extraValues.put(Root.COLUMN_FLAGS, getRootFlags(device.operationsSupported));
extraValues.putNull(Root.COLUMN_AVAILABLE_BYTES);
extraValues.putNull(Root.COLUMN_CAPACITY_BYTES);
extraValues.put(Root.COLUMN_MIME_TYPES, "");
@@ -700,12 +698,16 @@
/**
* Gets {@link ContentValues} for the given root.
* @param values {@link ContentValues} that receives values.
+ * @param extraValues {@link ContentValues} that receives extra values for roots.
+ * @param parentDocumentId Parent document ID.
+ * @param supportedOperations Array of Operation code supported by the device.
* @param root Root to be converted {@link ContentValues}.
*/
static void getStorageDocumentValues(
ContentValues values,
ContentValues extraValues,
String parentDocumentId,
+ int[] operationsSupported,
MtpRoot root) {
values.clear();
values.put(COLUMN_DEVICE_ID, root.mDeviceId);
@@ -722,9 +724,7 @@
values.put(Document.COLUMN_FLAGS, 0);
values.put(Document.COLUMN_SIZE, root.mMaxCapacity - root.mFreeSpace);
- extraValues.put(
- Root.COLUMN_FLAGS,
- Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
+ extraValues.put(Root.COLUMN_FLAGS, getRootFlags(operationsSupported));
extraValues.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
extraValues.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
extraValues.put(Root.COLUMN_MIME_TYPES, "");
@@ -785,6 +785,14 @@
return "application/octet-stream";
}
+ private static int getRootFlags(int[] operationsSupported) {
+ int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD;
+ if (MtpDeviceRecord.isWritingSupported(operationsSupported)) {
+ rootFlag |= Root.FLAG_SUPPORTS_CREATE;
+ }
+ return rootFlag;
+ }
+
static String[] strings(Object... args) {
final String[] results = new String[args.length];
for (int i = 0; i < args.length; i++) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
index 71716bd..393c4de 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
@@ -17,6 +17,7 @@
package com.android.mtp;
import android.annotation.Nullable;
+import android.mtp.MtpConstants;
class MtpDeviceRecord {
public final int deviceId;
@@ -38,4 +39,29 @@
this.operationsSupported = operationsSupported;
this.eventsSupported = eventsSupported;
}
+
+ /**
+ * Helper method to check operations/events are supported by the device or not.
+ */
+ static boolean isSupported(@Nullable int[] supportedList, int code) {
+ if (supportedList == null) {
+ return false;
+ }
+ for (int i = 0; i < supportedList.length; i++) {
+ if (supportedList[i] == code) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static boolean isPartialReadSupported(@Nullable int[] supportedList, long fileSize) {
+ return fileSize <= 0xffffffffl &&
+ isSupported(supportedList, MtpConstants.OPERATION_GET_PARTIAL_OBJECT);
+ }
+
+ static boolean isWritingSupported(@Nullable int[] supportedList) {
+ return isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT_INFO) &&
+ isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT);
+ }
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 4849978..a1c5c9b 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -201,6 +201,7 @@
final Identifier identifier = mDatabase.createIdentifier(documentId);
try {
openDevice(identifier.mDeviceId);
+ final MtpDeviceRecord device = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord;
switch (mode) {
case "r":
final long fileSize = getFileSize(documentId);
@@ -208,7 +209,8 @@
// 4GB. Fallback to non-seekable file descriptor.
// TODO: Use getPartialObject64 for MTP devices that support Android vendor
// extension.
- if (fileSize <= 0xffffffffl) {
+ if (MtpDeviceRecord.isPartialReadSupported(
+ device.operationsSupported, fileSize)) {
return mAppFuse.openFile(Integer.parseInt(documentId));
} else {
return getPipeManager(identifier).readDocument(mMtpManager, identifier);
@@ -216,8 +218,13 @@
case "w":
// TODO: Clear the parent document loader task (if exists) and call notify
// when writing is completed.
- return getPipeManager(identifier).writeDocument(
- getContext(), mMtpManager, identifier);
+ if (MtpDeviceRecord.isWritingSupported(device.operationsSupported)) {
+ return getPipeManager(identifier).writeDocument(
+ getContext(), mMtpManager, identifier);
+ } else {
+ throw new UnsupportedOperationException(
+ "The device does not support writing operation.");
+ }
case "rw":
// TODO: Add support for "rw" mode.
throw new UnsupportedOperationException(
@@ -290,6 +297,10 @@
try {
final Identifier parentId = mDatabase.createIdentifier(parentDocumentId);
openDevice(parentId.mDeviceId);
+ final MtpDeviceRecord record = getDeviceToolkit(parentId.mDeviceId).mDeviceRecord;
+ if (!MtpDeviceRecord.isWritingSupported(record.operationsSupported)) {
+ throw new UnsupportedOperationException();
+ }
final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe();
pipe[0].close(); // 0 bytes for a new document.
final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ?
@@ -323,9 +334,9 @@
if (DEBUG) {
Log.d(TAG, "Open device " + deviceId);
}
- mMtpManager.openDevice(deviceId);
+ final MtpDeviceRecord device = mMtpManager.openDevice(deviceId);
final DeviceToolkit toolkit =
- new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase);
+ new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase, device);
mDeviceToolkits.put(deviceId, toolkit);
mIntentSender.sendUpdateNotificationIntent();
try {
@@ -347,20 +358,15 @@
mIntentSender.sendUpdateNotificationIntent();
}
- int[] getOpenedDeviceIds() {
+ MtpDeviceRecord[] getOpenedDeviceRecordsCache() {
synchronized (mDeviceListLock) {
- return mMtpManager.getOpenedDeviceIds();
- }
- }
-
- String getDeviceName(int deviceId) throws IOException {
- synchronized (mDeviceListLock) {
- for (final MtpDeviceRecord device : mMtpManager.getDevices()) {
- if (device.deviceId == deviceId) {
- return device.name;
- }
+ final MtpDeviceRecord[] records = new MtpDeviceRecord[mDeviceToolkits.size()];
+ int i = 0;
+ for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
+ records[i] = toolkit.mDeviceRecord;
+ i++;
}
- throw new IOException("Not found the device: " + Integer.toString(deviceId));
+ return records;
}
}
@@ -391,7 +397,10 @@
public void shutdown() {
synchronized (mDeviceListLock) {
try {
- for (final int id : mMtpManager.getOpenedDeviceIds()) {
+ // Copy the opened key set because it will be modified when closing devices.
+ final Integer[] keySet =
+ mDeviceToolkits.keySet().toArray(new Integer[mDeviceToolkits.size()]);
+ for (final int id : keySet) {
closeDeviceInternal(id);
}
} catch (InterruptedException|IOException e) {
@@ -432,7 +441,7 @@
getDeviceToolkit(deviceId).mDocumentLoader.close();
mDeviceToolkits.remove(deviceId);
mMtpManager.closeDevice(deviceId);
- if (getOpenedDeviceIds().length == 0) {
+ if (mDeviceToolkits.size() == 0) {
mRootScanner.pause();
}
}
@@ -488,11 +497,14 @@
private static class DeviceToolkit {
public final PipeManager mPipeManager;
public final DocumentLoader mDocumentLoader;
+ public final MtpDeviceRecord mDeviceRecord;
public DeviceToolkit(
- int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database) {
+ int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database,
+ MtpDeviceRecord record) {
mPipeManager = new PipeManager(database);
mDocumentLoader = new DocumentLoader(deviceId, manager, resolver, database);
+ mDeviceRecord = record;
}
}
@@ -501,8 +513,13 @@
public long readObjectBytes(
int inode, long offset, long size, byte[] buffer) throws IOException {
final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode));
- return mMtpManager.getPartialObject(
- identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer);
+ final MtpDeviceRecord record = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord;
+ if (MtpDeviceRecord.isPartialReadSupported(record.operationsSupported, offset)) {
+ return mMtpManager.getPartialObject(
+ identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer);
+ } else {
+ throw new UnsupportedOperationException();
+ }
}
@Override
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
index 9c4952b..9b42b78 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
@@ -67,38 +67,25 @@
*/
private boolean updateForegroundState() {
final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
- final int[] deviceIds = provider.getOpenedDeviceIds();
int notificationId = 0;
Notification notification = null;
// TODO: Hide notification if the device has already been removed.
- for (final int deviceId : deviceIds) {
- try {
- final String title = getResources().getString(
- R.string.accessing_notification_title,
- provider.getDeviceName(deviceIds[0]));
- final String description = getResources().getString(
- R.string.accessing_notification_description);
- notificationId = deviceId;
- notification = new Notification.Builder(this)
- .setLocalOnly(true)
- .setContentTitle(title)
- .setContentText(description)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb)
- .setCategory(Notification.CATEGORY_SYSTEM)
- .setPriority(Notification.PRIORITY_LOW)
- .build();
- mNotificationManager.notify(deviceId, notification);
- } catch (IOException exp) {
- logErrorMessage(exp);
- // If we failed to obtain device name, it looks the device is unusable.
- // Because this is the last device we opened, we should hide the notification
- // for the case.
- try {
- provider.closeDevice(deviceIds[0]);
- } catch (IOException | InterruptedException closeError) {
- logErrorMessage(closeError);
- }
- }
+ for (final MtpDeviceRecord record : provider.getOpenedDeviceRecordsCache()) {
+ final String title = getResources().getString(
+ R.string.accessing_notification_title,
+ record.name);
+ final String description = getResources().getString(
+ R.string.accessing_notification_description);
+ notificationId = record.deviceId;
+ notification = new Notification.Builder(this)
+ .setLocalOnly(true)
+ .setContentTitle(title)
+ .setContentText(description)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setPriority(Notification.PRIORITY_LOW)
+ .build();
+ mNotificationManager.notify(record.deviceId, notification);
}
if (notification != null) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 37dc761..c49005f2 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -65,16 +65,14 @@
*/
private static final int PROTOCOL_MTP = 0;
-
private final UsbManager mManager;
- // TODO: Save and restore the set of opened device.
private final SparseArray<MtpDevice> mDevices = new SparseArray<>();
MtpManager(Context context) {
mManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
}
- synchronized void openDevice(int deviceId) throws IOException {
+ synchronized MtpDeviceRecord openDevice(int deviceId) throws IOException {
UsbDevice rawDevice = null;
for (final UsbDevice candidate : mManager.getDeviceList().values()) {
if (candidate.getDeviceId() == deviceId) {
@@ -113,6 +111,8 @@
}
mDevices.put(deviceId, device);
+
+ return createDeviceRecord(rawDevice);
}
synchronized void closeDevice(int deviceId) throws IOException {
@@ -126,45 +126,11 @@
if (!isMtpDevice(device)) {
continue;
}
- final MtpDevice mtpDevice = mDevices.get(device.getDeviceId());
- final boolean opened = mtpDevice != null;
- final String name = device.getProductName();
- MtpRoot[] roots;
- int[] operationsSupported = null;
- int[] eventsSupported = null;
- if (opened) {
- try {
- roots = getRoots(device.getDeviceId());
- } catch (IOException exp) {
- Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp);
- // If we failed to fetch roots for the device, we still returns device model
- // with an empty set of roots so that the device is shown DocumentsUI as long as
- // the device is physically connected.
- roots = new MtpRoot[0];
- }
- final MtpDeviceInfo info = mtpDevice.getDeviceInfo();
- if (info != null) {
- operationsSupported = mtpDevice.getDeviceInfo().getOperationsSupported();
- eventsSupported = mtpDevice.getDeviceInfo().getEventsSupported();
- }
- } else {
- roots = new MtpRoot[0];
- }
- devices.add(new MtpDeviceRecord(
- device.getDeviceId(), name, device.getSerialNumber(), opened, roots,
- operationsSupported, eventsSupported));
+ devices.add(createDeviceRecord(device));
}
return devices.toArray(new MtpDeviceRecord[devices.size()]);
}
- synchronized int[] getOpenedDeviceIds() {
- final int[] result = new int[mDevices.size()];
- for (int i = 0; i < result.length; i++) {
- result[i] = mDevices.keyAt(i);
- }
- return result;
- }
-
MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
throws IOException {
final MtpDevice device = getDevice(deviceId);
@@ -281,6 +247,36 @@
}
}
+ private MtpDeviceRecord createDeviceRecord(UsbDevice device) {
+ final MtpDevice mtpDevice = mDevices.get(device.getDeviceId());
+ final boolean opened = mtpDevice != null;
+ final String name = device.getProductName();
+ MtpRoot[] roots;
+ int[] operationsSupported = null;
+ int[] eventsSupported = null;
+ if (opened) {
+ try {
+ roots = getRoots(device.getDeviceId());
+ } catch (IOException exp) {
+ Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp);
+ // If we failed to fetch roots for the device, we still returns device model
+ // with an empty set of roots so that the device is shown DocumentsUI as long as
+ // the device is physically connected.
+ roots = new MtpRoot[0];
+ }
+ final MtpDeviceInfo info = mtpDevice.getDeviceInfo();
+ if (info != null) {
+ operationsSupported = mtpDevice.getDeviceInfo().getOperationsSupported();
+ eventsSupported = mtpDevice.getDeviceInfo().getEventsSupported();
+ }
+ } else {
+ roots = new MtpRoot[0];
+ }
+ return new MtpDeviceRecord(
+ device.getDeviceId(), name, device.getSerialNumber(), opened, roots,
+ operationsSupported, eventsSupported);
+ }
+
static boolean isMtpDevice(UsbDevice device) {
for (int i = 0; i < device.getInterfaceCount(); i++) {
final UsbInterface usbInterface = device.getInterface(i);
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index a48bf12..82ba21f 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -149,7 +149,8 @@
}
try {
mDatabase.getMapper().startAddingDocuments(documentId);
- if (mDatabase.getMapper().putStorageDocuments(documentId, device.roots)) {
+ if (mDatabase.getMapper().putStorageDocuments(
+ documentId, device.eventsSupported, device.roots)) {
changed = true;
}
if (mDatabase.getMapper().stopAddingDocuments(documentId)) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index b75a9e6..a000895 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -48,7 +48,7 @@
mDatabase.getMapper().stopAddingDocuments(null);
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", new int[0], new MtpRoot[] {
new MtpRoot(0, 0, "Storage", 1000, 1000, "")
});
mDatabase.getMapper().stopAddingDocuments("1");
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 05c9c57..48cde4c 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -30,10 +30,11 @@
import static android.provider.DocumentsContract.Document.*;
import static com.android.mtp.MtpDatabase.strings;
import static com.android.mtp.MtpDatabaseConstants.*;
+import static com.android.mtp.TestUtil.OPERATIONS_SUPPORTED;
@SmallTest
public class MtpDatabaseTest extends AndroidTestCase {
- private final String[] COLUMN_NAMES = new String[] {
+ private static final String[] COLUMN_NAMES = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
MtpDatabaseConstants.COLUMN_DEVICE_ID,
MtpDatabaseConstants.COLUMN_STORAGE_ID,
@@ -75,13 +76,10 @@
}
public void testPutSingleStorageDocuments() throws Exception {
- mDatabase.getMapper().startAddingDocuments(null);
- mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
- 0, "Device", null /* deviceKey */, true, new MtpRoot[0], null, null));
- mDatabase.getMapper().stopAddingDocuments(null);
+ addTestDevice();
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 1, "Storage", 1000, 2000, "")
});
mDatabase.getMapper().stopAddingDocuments("1");
@@ -143,7 +141,7 @@
addTestDevice();
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 1, "Storage", 1000, 2000, ""),
new MtpRoot(0, 2, "Storage", 2000, 4000, ""),
new MtpRoot(0, 3, "/@#%&<>Storage", 3000, 6000,"")
@@ -273,7 +271,7 @@
// Add device and two storages.
addTestDevice();
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage A", 1000, 0, ""),
new MtpRoot(0, 101, "Storage B", 1001, 0, "")
});
@@ -304,7 +302,7 @@
// Add two storages, but one's name is different from previous one.
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 200, "Storage A", 2000, 0, ""),
new MtpRoot(0, 202, "Storage C", 2002, 0, "")
});
@@ -398,10 +396,10 @@
mDatabase.getMapper().startAddingDocuments("1");
mDatabase.getMapper().startAddingDocuments("2");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage", 0, 0, "")
});
- mDatabase.getMapper().putStorageDocuments("2", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("2", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(1, 100, "Storage", 0, 0, "")
});
@@ -442,10 +440,10 @@
mDatabase.getMapper().startAddingDocuments("1");
mDatabase.getMapper().startAddingDocuments("2");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 200, "Storage", 2000, 0, "")
});
- mDatabase.getMapper().putStorageDocuments("2", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("2", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(1, 300, "Storage", 3000, 0, "")
});
mDatabase.getMapper().stopAddingDocuments("1");
@@ -562,7 +560,7 @@
addTestDevice();
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage", 0, 0, ""),
});
mDatabase.getMapper().clearMapping();
@@ -576,7 +574,7 @@
}
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 200, "Storage", 2000, 0, ""),
});
mDatabase.getMapper().clearMapping();
@@ -584,7 +582,7 @@
addTestDevice();
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 300, "Storage", 3000, 0, ""),
});
mDatabase.getMapper().stopAddingDocuments("1");
@@ -625,7 +623,7 @@
// Add a device and two storages that has same name.
addTestDevice();
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 200, "Storage", 2000, 0, ""),
new MtpRoot(0, 201, "Storage", 2001, 0, ""),
});
@@ -658,13 +656,13 @@
// The client code should be able to replace existing rows with new information.
// Add one.
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage A", 0, 0, ""),
});
mDatabase.getMapper().stopAddingDocuments("1");
// Replace it.
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage B", 1000, 1000, ""),
});
mDatabase.getMapper().stopAddingDocuments("1");
@@ -703,7 +701,7 @@
// Add one.
addTestDevice();
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage A", 0, 0, ""),
});
mDatabase.getMapper().clearMapping();
@@ -717,11 +715,11 @@
// Add one.
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 101, "Storage B", 1000, 1000, ""),
});
// Add one more before resolving unmapped documents.
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 102, "Storage B", 1000, 1000, ""),
});
mDatabase.getMapper().stopAddingDocuments("1");
@@ -763,7 +761,7 @@
}
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage A", 0, 0, "")
});
mDatabase.getMapper().stopAddingDocuments("1");
@@ -778,7 +776,7 @@
}
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage A", 0, 0, ""),
new MtpRoot(0, 101, "Storage B", 0, 0, "")
});
@@ -798,7 +796,7 @@
addTestDevice();
mDatabase.getMapper().startAddingDocuments("1");
- mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+ mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage A", 0, 0, ""),
});
mDatabase.getMapper().stopAddingDocuments("1");
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 884d132..db82bcb 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -38,6 +38,7 @@
import java.util.concurrent.TimeoutException;
import static com.android.mtp.MtpDatabase.strings;
+import static com.android.mtp.TestUtil.OPERATIONS_SUPPORTED;
@MediumTest
public class MtpDocumentsProviderTest extends AndroidTestCase {
@@ -77,7 +78,7 @@
2048 /* total space */,
"" /* no volume identifier */)
},
- null,
+ OPERATIONS_SUPPORTED,
null));
mProvider.resumeRootScanner();
@@ -98,7 +99,7 @@
} catch (Throwable error) {
assertTrue(error instanceof IOException);
}
- assertEquals(0, mProvider.getOpenedDeviceIds().length);
+ assertEquals(0, mProvider.getOpenedDeviceRecordsCache().length);
// Check if the following notification is the first one or not.
mMtpManager.addValidDevice(new MtpDeviceRecord(
@@ -115,7 +116,7 @@
2048 /* total space */,
"" /* no volume identifier */)
},
- null,
+ OPERATIONS_SUPPORTED,
null));
mProvider.resumeRootScanner();
mResolver.waitForNotification(ROOTS_URI, 1);
@@ -139,7 +140,7 @@
2048 /* total space */,
"" /* no volume identifier */)
},
- null,
+ OPERATIONS_SUPPORTED,
null));
mMtpManager.setObjectHandles(0, 1, -1, new int[0]);
mProvider.resumeRootScanner();
@@ -155,16 +156,16 @@
assertEquals(1, cursor.getLong(1));
}
{
- final int [] openedDevice = mProvider.getOpenedDeviceIds();
+ final MtpDeviceRecord[] openedDevice = mProvider.getOpenedDeviceRecordsCache();
assertEquals(0, openedDevice.length);
}
// Device is opened automatically when querying its children.
try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {}
{
- final int [] openedDevice = mProvider.getOpenedDeviceIds();
+ final MtpDeviceRecord[] openedDevice = mProvider.getOpenedDeviceRecordsCache();
assertEquals(1, openedDevice.length);
- assertEquals(0, openedDevice[0]);
+ assertEquals(0, openedDevice[0].deviceId);
}
}
@@ -184,7 +185,7 @@
2048 /* total space */,
"" /* no volume identifier */)
},
- null,
+ OPERATIONS_SUPPORTED,
null));
mMtpManager.addValidDevice(new MtpDeviceRecord(
1,
@@ -200,7 +201,7 @@
4096 /* total space */,
"Identifier B" /* no volume identifier */)
},
- null,
+ new int[0] /* No operations supported */,
null));
{
@@ -225,7 +226,7 @@
cursor.moveToNext();
cursor.moveToNext();
assertEquals("2", cursor.getString(0));
- assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
+ assertEquals(Root.FLAG_SUPPORTS_IS_CHILD, cursor.getInt(1));
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
assertEquals("Device B Storage B", cursor.getString(3));
assertEquals("2", cursor.getString(4));
@@ -241,7 +242,7 @@
"Device key A",
false /* unopened */,
new MtpRoot[0],
- null,
+ OPERATIONS_SUPPORTED,
null));
mMtpManager.addValidDevice(new MtpDeviceRecord(
1,
@@ -257,7 +258,7 @@
4096 /* total space */,
"Identifier B" /* no volume identifier */)
},
- null,
+ OPERATIONS_SUPPORTED,
null));
{
mProvider.openDevice(0);
@@ -544,14 +545,14 @@
public void testBusyDevice() throws Exception {
mMtpManager = new TestMtpManager(getContext()) {
@Override
- void openDevice(int deviceId) throws IOException {
+ MtpDeviceRecord openDevice(int deviceId) throws IOException {
throw new BusyDeviceException();
}
};
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
mMtpManager.addValidDevice(new MtpDeviceRecord(
- 0, "Device A", null /* deviceKey */, false /* unopened */, new MtpRoot[0], null,
- null));
+ 0, "Device A", null /* deviceKey */, false /* unopened */, new MtpRoot[0],
+ OPERATIONS_SUPPORTED, null));
mProvider.resumeRootScanner();
mResolver.waitForNotification(ROOTS_URI, 1);
@@ -571,7 +572,8 @@
public void testLockedDevice() throws Exception {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
mMtpManager.addValidDevice(new MtpDeviceRecord(
- 0, "Device A", null, false /* unopened */, new MtpRoot[0], null, null));
+ 0, "Device A", null, false /* unopened */, new MtpRoot[0], OPERATIONS_SUPPORTED,
+ null));
mProvider.resumeRootScanner();
mResolver.waitForNotification(ROOTS_URI, 1);
@@ -661,6 +663,60 @@
}
}
+ public void testCreateDocument_noWritingSupport() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0, "Device A", null /* deviceKey */, false /* unopened */,
+ new MtpRoot[] {
+ new MtpRoot(
+ 0 /* deviceId */,
+ 1 /* storageId */,
+ "Storage A" /* volume description */,
+ 1024 /* free space */,
+ 2048 /* total space */,
+ "" /* no volume identifier */)
+ },
+ new int[0] /* no operations supported */, null));
+ mProvider.resumeRootScanner();
+ mResolver.waitForNotification(ROOTS_URI, 1);
+ try {
+ mProvider.createDocument("1", "text/palin", "note.txt");
+ fail();
+ } catch (UnsupportedOperationException exception) {}
+ }
+
+ public void testOpenDocument_noWritingSupport() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0, "Device A", null /* deviceKey */, false /* unopened */,
+ new MtpRoot[] {
+ new MtpRoot(
+ 0 /* deviceId */,
+ 1 /* storageId */,
+ "Storage A" /* volume description */,
+ 1024 /* free space */,
+ 2048 /* total space */,
+ "" /* no volume identifier */)
+ },
+ new int[0] /* no operations supported */, null));
+ mMtpManager.setObjectHandles(
+ 0, 1, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, new int[] { 100 });
+ mMtpManager.setObjectInfo(
+ 0, new MtpObjectInfo.Builder().setObjectHandle(100).setName("note.txt").build());
+ mProvider.resumeRootScanner();
+ mResolver.waitForNotification(ROOTS_URI, 1);
+ try (final Cursor cursor = mProvider.queryChildDocuments(
+ "1", strings(Document.COLUMN_DOCUMENT_ID), null)) {
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("3", cursor.getString(0));
+ }
+ try {
+ mProvider.openDocument("3", "w", null);
+ fail();
+ } catch (UnsupportedOperationException exception) {}
+ }
+
private void setupProvider(int flag) {
mDatabase = new MtpDatabase(getContext(), flag);
mProvider = new MtpDocumentsProvider();
@@ -691,7 +747,7 @@
final int changeCount = mResolver.getChangeCount(ROOTS_URI);
mMtpManager.addValidDevice(
new MtpDeviceRecord(deviceId, "Device", null /* deviceKey */, false /* unopened */,
- roots, null, null));
+ roots, OPERATIONS_SUPPORTED, null));
mProvider.openDevice(deviceId);
mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 3043ce8..5e0ee1e 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -22,6 +22,7 @@
import android.util.SparseArray;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -84,16 +85,16 @@
}
@Override
- void openDevice(int deviceId) throws IOException {
+ MtpDeviceRecord openDevice(int deviceId) throws IOException {
final MtpDeviceRecord device = mDevices.get(deviceId);
if (device == null) {
throw new IOException();
}
- mDevices.put(
- deviceId,
- new MtpDeviceRecord(
- device.deviceId, device.name, device.deviceKey, true, device.roots, null,
- null));
+ final MtpDeviceRecord record = new MtpDeviceRecord(
+ device.deviceId, device.name, device.deviceKey, true, device.roots,
+ device.operationsSupported, device.eventsSupported);
+ mDevices.put(deviceId, record);
+ return record;
}
@Override
@@ -198,19 +199,6 @@
}
@Override
- int[] getOpenedDeviceIds() {
- final int[] result = new int[mDevices.size()];
- int count = 0;
- for (int i = 0; i < mDevices.size(); i++) {
- final MtpDeviceRecord device = mDevices.valueAt(i);
- if (device.opened) {
- result[count++] = device.deviceId;
- }
- }
- return Arrays.copyOf(result, count);
- }
-
- @Override
byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException {
return mImportFileBytes.get(pack(deviceId, objectHandle));
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
index 34dd77b..8adb68f 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
@@ -19,6 +19,7 @@
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
+import android.mtp.MtpConstants;
import android.os.SystemClock;
import java.io.FileNotFoundException;
@@ -32,6 +33,12 @@
final class TestUtil {
private TestUtil() {}
+ static final int[] OPERATIONS_SUPPORTED = new int[] {
+ MtpConstants.OPERATION_GET_PARTIAL_OBJECT,
+ MtpConstants.OPERATION_SEND_OBJECT,
+ MtpConstants.OPERATION_SEND_OBJECT_INFO,
+ };
+
/**
* Requests permission for a MTP device and returns the first MTP device that has at least one
* storage.
@@ -59,14 +66,14 @@
static void addTestDevice(MtpDatabase database) throws FileNotFoundException {
database.getMapper().startAddingDocuments(null);
database.getMapper().putDeviceDocument(new MtpDeviceRecord(
- 0, "Device", "device_key", /* opened is */ true, new MtpRoot[0], null,
- null));
+ 0, "Device", "device_key", /* opened is */ true, new MtpRoot[0],
+ OPERATIONS_SUPPORTED, null));
database.getMapper().stopAddingDocuments(null);
}
static void addTestStorage(MtpDatabase database, String parentId) throws FileNotFoundException {
database.getMapper().startAddingDocuments(parentId);
- database.getMapper().putStorageDocuments(parentId, new MtpRoot[] {
+ database.getMapper().putStorageDocuments(parentId, OPERATIONS_SUPPORTED, new MtpRoot[] {
new MtpRoot(0, 100, "Storage", 1024, 1024, ""),
});
database.getMapper().stopAddingDocuments(parentId);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 2ae3ec62..7a6aad6 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -16,6 +16,7 @@
package com.android.printspooler.model;
+import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
@@ -57,6 +58,8 @@
private static final boolean DEBUG = false;
+ private static final long FORCE_CANCEL_TIMEOUT = 1000; // ms
+
private static final int STATE_INITIAL = 0;
private static final int STATE_STARTED = 1;
private static final int STATE_UPDATING = 2;
@@ -212,7 +215,7 @@
// cancellation and start over.
if (mCurrentCommand != null && (mCurrentCommand.isRunning()
|| mCurrentCommand.isPending())) {
- mCurrentCommand.cancel();
+ mCurrentCommand.cancel(false);
}
// Schedule a layout command.
@@ -233,7 +236,7 @@
// Cancel the current write as a new one is to be scheduled.
if (mCurrentCommand instanceof WriteCommand
&& (mCurrentCommand.isPending() || mCurrentCommand.isRunning())) {
- mCurrentCommand.cancel();
+ mCurrentCommand.cancel(false);
}
// Schedule a write command.
@@ -277,9 +280,9 @@
}
}
- public void cancel() {
+ public void cancel(boolean force) {
if (DEBUG) {
- Log.i(LOG_TAG, "[CALLED] cancel()");
+ Log.i(LOG_TAG, "[CALLED] cancel(" + force + ")");
}
mNextCommand = null;
@@ -290,7 +293,7 @@
mState = STATE_CANCELING;
- mCurrentCommand.cancel();
+ mCurrentCommand.cancel(force);
}
public void destroy() {
@@ -441,8 +444,9 @@
if (mCurrentCommand != null) {
if (mCurrentCommand.isPending()) {
mCurrentCommand.run();
+
+ mState = STATE_UPDATING;
}
- mState = STATE_UPDATING;
} else {
mState = STATE_UPDATED;
}
@@ -535,14 +539,17 @@
protected final CommandDoneCallback mDoneCallback;
+ private final Handler mHandler;
+
protected ICancellationSignal mCancellation;
private CharSequence mError;
private int mState = STATE_PENDING;
- public AsyncCommand(IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document,
+ public AsyncCommand(Looper looper, IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document,
CommandDoneCallback doneCallback) {
+ mHandler = new AsyncCommandHandler(looper);
mAdapter = adapter;
mDocument = document;
mDoneCallback = doneCallback;
@@ -556,7 +563,29 @@
return mState == STATE_CANCELED;
}
- public final void cancel() {
+ /**
+ * If a force cancel is pending, remove it. This is usually called when a command returns
+ * and thereby does not need to be canceled anymore.
+ */
+ protected void removeForceCancel() {
+ if (DEBUG) {
+ if (mHandler.hasMessages(AsyncCommandHandler.MSG_FORCE_CANCEL)) {
+ Log.i(LOG_TAG, "[FORCE CANCEL] Removed");
+ }
+ }
+
+ mHandler.removeMessages(AsyncCommandHandler.MSG_FORCE_CANCEL);
+ }
+
+ /**
+ * Cancel the current command.
+ *
+ * @param force If set, does not wait for the {@link PrintDocumentAdapter} to cancel. This
+ * should only be used if this is the last command send to the as otherwise the
+ * {@link PrintDocumentAdapter adapter} might get commands while it is still
+ * running the old one.
+ */
+ public final void cancel(boolean force) {
if (isRunning()) {
canceling();
if (mCancellation != null) {
@@ -566,14 +595,25 @@
Log.w(LOG_TAG, "Error while canceling", re);
}
}
- } else if (isCanceling()) {
- // Nothing to do
- } else {
- canceled();
-
- // Done.
- mDoneCallback.onDone();
}
+
+ if (isCanceling()) {
+ if (force) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[FORCE CANCEL] queued");
+ }
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(AsyncCommandHandler.MSG_FORCE_CANCEL),
+ FORCE_CANCEL_TIMEOUT);
+ }
+
+ return;
+ }
+
+ canceled();
+
+ // Done.
+ mDoneCallback.onDone();
}
protected final void canceling() {
@@ -617,7 +657,7 @@
}
protected final void failed(CharSequence error) {
- if (mState != STATE_RUNNING) {
+ if (mState != STATE_RUNNING && mState != STATE_CANCELING) {
throw new IllegalStateException("Not running.");
}
mState = STATE_FAILED;
@@ -632,6 +672,37 @@
public CharSequence getError() {
return mError;
}
+
+ /**
+ * Handler for the async command.
+ */
+ private class AsyncCommandHandler extends Handler {
+ /** Message indicated the desire for to force cancel a command */
+ final static int MSG_FORCE_CANCEL = 0;
+
+ AsyncCommandHandler(@NonNull Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_FORCE_CANCEL:
+ if (isCanceling()) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[FORCE CANCEL] executed");
+ }
+ failed("Command did not respond to cancellation in "
+ + FORCE_CANCEL_TIMEOUT + " ms");
+
+ mDoneCallback.onDone();
+ }
+ break;
+ default:
+ // not reached;
+ }
+ }
+ }
}
private static final class LayoutCommand extends AsyncCommand {
@@ -646,7 +717,7 @@
public LayoutCommand(Looper looper, IPrintDocumentAdapter adapter,
RemotePrintDocumentInfo document, PrintAttributes oldAttributes,
PrintAttributes newAttributes, boolean preview, CommandDoneCallback callback) {
- super(adapter, document, callback);
+ super(looper, adapter, document, callback);
mHandler = new LayoutHandler(looper);
mRemoteResultCallback = new LayoutResultCallback(mHandler);
mOldAttributes.copyFrom(oldAttributes);
@@ -795,6 +866,21 @@
@Override
public void handleMessage(Message message) {
+ // The command might have been force canceled, see
+ // AsyncCommand.AsyncCommandHandler#handleMessage
+ if (isFailed()) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] on canceled layout command");
+ }
+
+ return;
+ } else {
+ if (message.what != MSG_ON_LAYOUT_STARTED) {
+ // No need to force cancel anymore if layout finished
+ removeForceCancel();
+ }
+ }
+
switch (message.what) {
case MSG_ON_LAYOUT_STARTED: {
ICancellationSignal cancellation = (ICancellationSignal) message.obj;
@@ -882,7 +968,7 @@
public WriteCommand(Context context, Looper looper, IPrintDocumentAdapter adapter,
RemotePrintDocumentInfo document, int pageCount, PageRange[] pages,
MutexFileProvider fileProvider, CommandDoneCallback callback) {
- super(adapter, document, callback);
+ super(looper, adapter, document, callback);
mContext = context;
mHandler = new WriteHandler(looper);
mRemoteResultCallback = new WriteResultCallback(mHandler);
@@ -1052,6 +1138,21 @@
@Override
public void handleMessage(Message message) {
+ // The command might have been force canceled, see
+ // AsyncCommand.AsyncCommandHandler#handleMessage
+ if (isFailed()) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] on canceled write command");
+ }
+
+ return;
+ } else {
+ if (message.what != MSG_ON_WRITE_STARTED) {
+ // No need to force cancel anymore if write finished
+ removeForceCancel();
+ }
+ }
+
switch (message.what) {
case MSG_ON_WRITE_STARTED: {
ICancellationSignal cancellation = (ICancellationSignal) message.obj;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 08cd0b6..64f5cc6 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -320,8 +320,8 @@
if (isFinishing() || (isFinalState(mState) && !mPrintedDocument.isUpdating())) {
return;
}
- mPrintedDocument.cancel();
setState(STATE_PRINT_CANCELED);
+ mPrintedDocument.cancel(true);
doFinish();
}
}, PrintActivity.this);
@@ -1013,7 +1013,7 @@
}
private void requestCreatePdfFileOrFinish() {
- mPrintedDocument.cancel();
+ mPrintedDocument.cancel(false);
if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) {
startCreateDocumentActivity();
@@ -1130,7 +1130,7 @@
private void cancelPrint() {
setState(STATE_PRINT_CANCELED);
updateOptionsUi();
- mPrintedDocument.cancel();
+ mPrintedDocument.cancel(true);
doFinish();
}
@@ -1889,7 +1889,7 @@
public void onPrinterUnavailable(PrinterInfo printer) {
if (mCurrentPrinter.getId().equals(printer.getId())) {
setState(STATE_PRINTER_UNAVAILABLE);
- mPrintedDocument.cancel();
+ mPrintedDocument.cancel(false);
ensureErrorUiShown(getString(R.string.print_error_printer_unavailable),
PrintErrorFragment.ACTION_NONE);
updateOptionsUi();
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 1c032fa..a578055 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -18,6 +18,7 @@
import android.annotation.LayoutRes;
import android.annotation.Nullable;
import android.app.Activity;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -235,20 +236,24 @@
Intent.FLAG_ACTIVITY_CLEAR_TASK));
return true;
}
- int numUserHandles = tile.userHandle.size();
- if (numUserHandles > 1) {
- ProfileSelectDialog.show(getFragmentManager(), tile);
- return false;
- } else if (numUserHandles == 1) {
- // Show menu on top level items.
- tile.intent.putExtra(EXTRA_SHOW_MENU, true);
- tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- startActivityAsUser(tile.intent, tile.userHandle.get(0));
- } else {
- // Show menu on top level items.
- tile.intent.putExtra(EXTRA_SHOW_MENU, true);
- tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- startActivity(tile.intent);
+ try {
+ int numUserHandles = tile.userHandle.size();
+ if (numUserHandles > 1) {
+ ProfileSelectDialog.show(getFragmentManager(), tile);
+ return false;
+ } else if (numUserHandles == 1) {
+ // Show menu on top level items.
+ tile.intent.putExtra(EXTRA_SHOW_MENU, true);
+ tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivityAsUser(tile.intent, tile.userHandle.get(0));
+ } else {
+ // Show menu on top level items.
+ tile.intent.putExtra(EXTRA_SHOW_MENU, true);
+ tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(tile.intent);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Couldn't find tile " + tile.intent, e);
}
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 5b8ed28..380fcd4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -110,6 +110,7 @@
private final Context mContext;
private String ssid;
+ private String bssid;
private int security;
private int networkId = WifiConfiguration.INVALID_NETWORK_ID;
@@ -335,6 +336,10 @@
return ssid;
}
+ public String getBssid() {
+ return bssid;
+ }
+
public CharSequence getSsid() {
SpannableString str = new SpannableString(ssid);
str.setSpan(new TtsSpan.VerbatimBuilder(ssid).build(), 0, ssid.length(),
@@ -657,6 +662,7 @@
else
ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
+ bssid = config.BSSID;
security = getSecurity(config);
networkId = config.networkId;
mConfig = config;
@@ -664,6 +670,7 @@
private void initWithScanResult(ScanResult result) {
ssid = result.SSID;
+ bssid = result.BSSID;
security = getSecurity(result);
if (security == SECURITY_PSK)
pskType = getPskType(result);
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 20aca0e..36097a1 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -149,6 +149,9 @@
private static final int MSG_SCREENSHOT_REQUEST = 4;
private static final int MSG_SCREENSHOT_RESPONSE = 5;
+ // Passed to Message.obtain() when msg.arg2 is not used.
+ private static final int UNUSED_ARG2 = -2;
+
/**
* Delay before a screenshot is taken.
* <p>
@@ -302,8 +305,7 @@
}
final String action = intent.getAction();
final int pid = intent.getIntExtra(EXTRA_PID, 0);
- // TODO: temporarily using pid as id until test cases and dumpstate are changed.
- final int id = intent.getIntExtra(EXTRA_ID, pid);
+ final int id = intent.getIntExtra(EXTRA_ID, 0);
final int max = intent.getIntExtra(EXTRA_MAX, -1);
final String name = intent.getStringExtra(EXTRA_NAME);
@@ -434,10 +436,12 @@
final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH);
infoIntent.putExtra(EXTRA_ID, info.id);
+ final PendingIntent infoPendingIntent =
+ PendingIntent.getService(mContext, info.id, infoIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
final Action infoAction = new Action.Builder(null,
mContext.getString(R.string.bugreport_info_action),
- PendingIntent.getService(mContext, info.id, infoIntent,
- PendingIntent.FLAG_UPDATE_CURRENT)).build();
+ infoPendingIntent).build();
final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class);
screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT);
screenshotIntent.putExtra(EXTRA_ID, info.id);
@@ -464,6 +468,7 @@
.setLocalOnly(true)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color))
+ .setContentIntent(infoPendingIntent)
.addAction(infoAction)
.addAction(screenshotAction)
.addAction(cancelAction)
@@ -474,7 +479,10 @@
+ info + ")");
return;
}
- Log.v(TAG, "Sending 'Progress' notification for id " + info.id + ": " + percentText);
+ if (DEBUG) {
+ Log.d(TAG, "Sending 'Progress' notification for id " + info.id + "(pid " + info.pid
+ + "): " + percentText);
+ }
NotificationManager.from(mContext).notify(TAG, info.id, notification);
}
@@ -541,7 +549,7 @@
final int pid = info.pid;
final int id = info.id;
if (info.finished) {
- if (DEBUG) Log.v(TAG, "Skipping finished process " + pid + "(id: " + id + ")");
+ if (DEBUG) Log.v(TAG, "Skipping finished process " + pid + " (id: " + id + ")");
continue;
}
activeProcesses++;
@@ -662,11 +670,8 @@
final String screenshotPath =
new File(mScreenshotsDir, info.getPathNextScreenshot()).getAbsolutePath();
- final Message requestMsg = new Message();
- requestMsg.what = MSG_SCREENSHOT_REQUEST;
- requestMsg.arg1 = id;
- requestMsg.obj = screenshotPath;
- mScreenshotHandler.sendMessage(requestMsg);
+ Message.obtain(mScreenshotHandler, MSG_SCREENSHOT_REQUEST, id, UNUSED_ARG2, screenshotPath)
+ .sendToTarget();
}
/**
@@ -692,12 +697,8 @@
boolean taken = takeScreenshot(mContext, screenshotFile);
setTakingScreenshot(false);
- final Message resultMsg = new Message();
- resultMsg.what = MSG_SCREENSHOT_RESPONSE;
- resultMsg.arg1 = requestMsg.arg1;
- resultMsg.arg2 = taken ? 1 : 0;
- resultMsg.obj = screenshotFile;
- mMainHandler.sendMessage(resultMsg);
+ Message.obtain(mMainHandler, MSG_SCREENSHOT_RESPONSE, requestMsg.arg1, taken ? 1 : 0,
+ screenshotFile).sendToTarget();
}
private void handleScreenshotResponse(Message resultMsg) {
@@ -708,7 +709,7 @@
}
final File screenshotFile = new File((String) resultMsg.obj);
- final int msgId;
+ final String msg;
if (taken) {
info.addScreenshot(screenshotFile);
if (info.finished) {
@@ -716,14 +717,13 @@
info.renameScreenshots(mScreenshotsDir);
sendBugreportNotification(mContext, info);
}
- msgId = R.string.bugreport_screenshot_taken;
+ msg = mContext.getString(R.string.bugreport_screenshot_taken);
} else {
// TODO: try again using Framework APIs instead of relying on screencap.
- msgId = R.string.bugreport_screenshot_failed;
+ msg = mContext.getString(R.string.bugreport_screenshot_failed);
+ Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
}
- final String msg = mContext.getString(msgId);
Log.d(TAG, msg);
- Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
}
/**
@@ -1125,7 +1125,7 @@
private static boolean setSystemProperty(String key, String value) {
try {
- if (DEBUG) Log.v(TAG, "Setting system property" + key + " to " + value);
+ if (DEBUG) Log.v(TAG, "Setting system property " + key + " to " + value);
SystemProperties.set(key, value);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Could not set property " + key + " to " + value, e);
@@ -1249,6 +1249,8 @@
* Sets its internal state and displays the dialog.
*/
private void initialize(final Context context, BugreportInfo info) {
+ final String dialogTitle =
+ context.getString(R.string.bugreport_info_dialog_title, info.id);
// First initializes singleton.
if (mDialog == null) {
@SuppressLint("InflateParams")
@@ -1272,7 +1274,7 @@
mDialog = new AlertDialog.Builder(context)
.setView(view)
- .setTitle(context.getString(R.string.bugreport_info_dialog_title, info.id))
+ .setTitle(dialogTitle)
.setCancelable(false)
.setPositiveButton(context.getString(com.android.internal.R.string.ok),
null)
@@ -1297,6 +1299,12 @@
new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG));
+ } else {
+ // Re-use view, but reset fields first.
+ mDialog.setTitle(dialogTitle);
+ mInfoName.setText(null);
+ mInfoTitle.setText(null);
+ mInfoDescription.setText(null);
}
// Then set fields.
@@ -1372,7 +1380,7 @@
// Must update system property for the cases where dumpstate finishes
// while the user is still entering other fields (like title or
// description)
- setBugreportNameProperty(mId, name);
+ setBugreportNameProperty(mPid, name);
}
/**
@@ -1517,7 +1525,7 @@
final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size());
for (File oldFile : screenshotFiles) {
final String oldName = oldFile.getName();
- final String newName = oldName.replace(Integer.toString(pid), name);
+ final String newName = oldName.replaceFirst(Integer.toString(pid), name);
final File newFile;
if (!newName.equals(oldName)) {
final File renamedFile = new File(screenshotDir, newName);
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 8c62670..d0499a5 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -19,6 +19,7 @@
import static android.test.MoreAsserts.assertContainsRegex;
import static com.android.shell.ActionSendMultipleConsumerActivity.UI_NAME;
import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
+import static com.android.shell.BugreportProgressService.EXTRA_ID;
import static com.android.shell.BugreportProgressService.EXTRA_MAX;
import static com.android.shell.BugreportProgressService.EXTRA_NAME;
import static com.android.shell.BugreportProgressService.EXTRA_PID;
@@ -60,6 +61,7 @@
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
@@ -98,24 +100,33 @@
private static final String BUGREPORTS_DIR = "bugreports";
private static final String BUGREPORT_FILE = "test_bugreport.txt";
private static final String ZIP_FILE = "test_bugreport.zip";
+ private static final String ZIP_FILE2 = "test_bugreport2.zip";
private static final String SCREENSHOT_FILE = "test_screenshot.png";
private static final String BUGREPORT_CONTENT = "Dump, might as well dump!\n";
private static final String SCREENSHOT_CONTENT = "A picture is worth a thousand words!\n";
private static final int PID = 42;
- private static final String PROGRESS_PROPERTY = "dumpstate.42.progress";
- private static final String MAX_PROPERTY = "dumpstate.42.max";
- private static final String NAME_PROPERTY = "dumpstate.42.name";
+ private static final int PID2 = 24;
+ private static final int ID = 108;
+ private static final int ID2 = 801;
+ private static final String PROGRESS_PROPERTY = "dumpstate." + PID + ".progress";
+ private static final String MAX_PROPERTY = "dumpstate." + PID + ".max";
+ private static final String NAME_PROPERTY = "dumpstate." + PID + ".name";
private static final String NAME = "BUG, Y U NO REPORT?";
+ private static final String NAME2 = "A bugreport's life";
private static final String NEW_NAME = "Bug_Forrest_Bug";
+ private static final String NEW_NAME2 = "BugsyReportsy";
private static final String TITLE = "Wimbugdom Champion 2015";
+ private static final String TITLE2 = "Master of the Universe";
+ private static final String DESCRIPTION = "One's description...";
+ private static final String DESCRIPTION2 = "...is another's treasure.";
private static final String NO_DESCRIPTION = null;
private static final String NO_NAME = null;
private static final String NO_SCREENSHOT = null;
private static final String NO_TITLE = null;
- private static final int NO_PID = 0;
+ private static final int NO_ID = 0;
private static final boolean RENAMED_SCREENSHOTS = true;
private static final boolean DIDNT_RENAME_SCREENSHOTS = false;
@@ -123,6 +134,7 @@
private String mPlainTextPath;
private String mZipPath;
+ private String mZipPath2;
private String mScreenshotPath;
private Context mContext;
@@ -141,10 +153,12 @@
mPlainTextPath = getPath(BUGREPORT_FILE);
mZipPath = getPath(ZIP_FILE);
+ mZipPath2 = getPath(ZIP_FILE2);
mScreenshotPath = getPath(SCREENSHOT_FILE);
createTextFile(mPlainTextPath, BUGREPORT_CONTENT);
createTextFile(mScreenshotPath, SCREENSHOT_CONTENT);
createZipFile(mZipPath, BUGREPORT_FILE, BUGREPORT_CONTENT);
+ createZipFile(mZipPath2, BUGREPORT_FILE, BUGREPORT_CONTENT);
// Creates a multi-line description.
StringBuilder sb = new StringBuilder();
@@ -177,13 +191,32 @@
assertProgressNotification(NAME, nf.format(0.25));
Bundle extras =
- sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
+ sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath, mScreenshotPath);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
}
+ public void testProgress_cancel() throws Exception {
+ resetProperties();
+ sendBugreportStarted(1000);
+ waitForScreenshotButtonEnabled(true);
+
+ final NumberFormat nf = NumberFormat.getPercentInstance();
+ nf.setMinimumFractionDigits(2);
+ nf.setMaximumFractionDigits(2);
+
+ assertProgressNotification(NAME, nf.format(0));
+
+ openProgressNotification(ID);
+ UiObject cancelButton = mUiBot.getVisibleObject(mContext.getString(
+ com.android.internal.R.string.cancel).toUpperCase());
+ mUiBot.click(cancelButton, "cancel_button");
+
+ waitForService(false);
+ }
+
public void testProgress_takeExtraScreenshot() throws Exception {
takeExtraScreenshotTest(false);
}
@@ -201,15 +234,15 @@
assertScreenshotButtonEnabled(false);
waitForScreenshotButtonEnabled(true);
- sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
+ sendBugreportFinished(ID, mPlainTextPath, mScreenshotPath);
if (serviceDies) {
- waitShareNotification(PID);
+ waitShareNotification(ID);
killService();
}
- Bundle extras = acceptBugreportAndGetSharedIntent(PID);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
+ Bundle extras = acceptBugreportAndGetSharedIntent(ID);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
NAME, NO_TITLE, NO_DESCRIPTION, 2, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
@@ -227,8 +260,8 @@
resetProperties();
sendBugreportStarted(1000);
- sendBugreportFinished(PID, mPlainTextPath, NO_SCREENSHOT);
- waitShareNotification(PID);
+ sendBugreportFinished(ID, mPlainTextPath, NO_SCREENSHOT);
+ waitShareNotification(ID);
// There's no indication in the UI about the screenshot finish, so just sleep like a baby...
Thread.sleep(SAFE_SCREENSHOT_DELAY * DateUtils.SECOND_IN_MILLIS);
@@ -237,8 +270,8 @@
killService();
}
- Bundle extras = acceptBugreportAndGetSharedIntent(PID);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, PID, ZIP_FILE,
+ Bundle extras = acceptBugreportAndGetSharedIntent(ID);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, ID, PID, ZIP_FILE,
NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
@@ -249,11 +282,10 @@
sendBugreportStarted(1000);
waitForScreenshotButtonEnabled(true);
- DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
+ DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
// Check initial name.
- String actualName = detailsUi.nameField.getText().toString();
- assertEquals("Wrong value on field 'name'", NAME, actualName);
+ detailsUi.assertName(NAME);
// Change name - it should have changed system property once focus is changed.
detailsUi.nameField.setText(NEW_NAME);
@@ -281,9 +313,9 @@
assertPropertyValue(NAME_PROPERTY, NEW_NAME);
assertProgressNotification(NEW_NAME, "0.00%");
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath,
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath,
mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
@@ -302,11 +334,10 @@
sendBugreportStarted(1000);
waitForScreenshotButtonEnabled(true);
- DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
+ DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
// Check initial name.
- String actualName = detailsUi.nameField.getText().toString();
- assertEquals("Wrong value on field 'name'", NAME, actualName);
+ detailsUi.assertName(NAME);
// Change fields.
detailsUi.reOpen();
@@ -319,33 +350,87 @@
assertPropertyValue(NAME_PROPERTY, NEW_NAME);
assertProgressNotification(NEW_NAME, "0.00%");
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID,
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID,
plainText? mPlainTextPath : mZipPath, mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
}
- public void testProgress_changeJustDetails() throws Exception {
+ public void testProgress_changeJustDetailsTouchingDetails() throws Exception {
+ changeJustDetailsTest(true);
+ }
+
+ public void testProgress_changeJustDetailsTouchingNotification() throws Exception {
+ changeJustDetailsTest(false);
+ }
+
+ private void changeJustDetailsTest(boolean touchDetails) throws Exception {
resetProperties();
sendBugreportStarted(1000);
waitForScreenshotButtonEnabled(true);
- DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
+ DetailsUi detailsUi = new DetailsUi(mUiBot, ID, touchDetails);
detailsUi.nameField.setText("");
detailsUi.titleField.setText("");
detailsUi.descField.setText(mDescription);
detailsUi.clickOk();
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID, mZipPath, mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
NO_NAME, NO_TITLE, mDescription, 1, DIDNT_RENAME_SCREENSHOTS);
assertServiceNotRunning();
}
+ /*
+ * TODO: this test can be flanky because it relies in the order the notifications are displayed,
+ * since mUiBot gets the first notification.
+ * Ideally, openProgressNotification() should return the whole notification, so DetailsUi
+ * could use it and find children instead, but unfortunately the notification object hierarchy
+ * is too complex and getting it from the notification text object would be to fragile
+ * (for instance, it could require navigating many parents up in the hierarchy).
+ */
+ public void testProgress_changeJustDetailsIsClearedOnSecondBugreport() throws Exception {
+ resetProperties();
+ sendBugreportStarted(ID, PID, NAME, 1000);
+ waitForScreenshotButtonEnabled(true);
+
+ DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
+ detailsUi.assertName(NAME);
+ detailsUi.assertTitle(mContext.getString(R.string.bugreport_info_title));
+ detailsUi.assertDescription(mContext.getString(R.string.bugreport_info_description));
+ detailsUi.nameField.setText(NEW_NAME);
+ detailsUi.titleField.setText(TITLE);
+ detailsUi.descField.setText(DESCRIPTION);
+ detailsUi.clickOk();
+
+ sendBugreportStarted(ID2, PID2, NAME2, 1000);
+
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
+ NEW_NAME, TITLE, DESCRIPTION, 1, RENAMED_SCREENSHOTS);
+
+ detailsUi = new DetailsUi(mUiBot, ID2);
+ detailsUi.assertName(NAME2);
+ detailsUi.assertTitle(mContext.getString(R.string.bugreport_info_title));
+ detailsUi.assertDescription(mContext.getString(R.string.bugreport_info_description));
+ detailsUi.nameField.setText(NEW_NAME2);
+ detailsUi.titleField.setText(TITLE2);
+ detailsUi.descField.setText(DESCRIPTION2);
+ detailsUi.clickOk();
+
+ // Must use a different zip file otherwise it will fail because zip already contains
+ // title.txt and description.txt entries.
+ extras = sendBugreportFinishedAndGetSharedIntent(ID2, mZipPath2, NO_SCREENSHOT);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, ID2, PID2, TITLE2,
+ NEW_NAME2, TITLE2, DESCRIPTION2, 1, RENAMED_SCREENSHOTS);
+
+ assertServiceNotRunning();
+ }
+
/**
* Tests the scenario where the initial screenshot and dumpstate are finished while the user
* is changing the info in the details screen.
@@ -369,14 +454,14 @@
waitForScreenshotButtonEnabled(true);
}
- DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
+ DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
// Finish the bugreport while user's still typing the name.
detailsUi.nameField.setText(NEW_NAME);
- sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
+ sendBugreportFinished(ID, mPlainTextPath, mScreenshotPath);
// Wait until the share notification is received...
- waitShareNotification(PID);
+ waitShareNotification(ID);
// ...then close notification bar.
mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
@@ -390,8 +475,8 @@
detailsUi.clickOk();
// Finally, share bugreport.
- Bundle extras = acceptBugreportAndGetSharedIntent(PID);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+ Bundle extras = acceptBugreportAndGetSharedIntent(ID);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
@@ -402,8 +487,8 @@
BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_SHOW);
// Send notification and click on share.
- sendBugreportFinished(NO_PID, mPlainTextPath, null);
- acceptBugreport(NO_PID);
+ sendBugreportFinished(NO_ID, mPlainTextPath, null);
+ acceptBugreport(NO_ID);
// Handle the warning
mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm));
@@ -425,9 +510,9 @@
}
public void testShareBugreportAfterServiceDies() throws Exception {
- sendBugreportFinished(NO_PID, mPlainTextPath, NO_SCREENSHOT);
+ sendBugreportFinished(NO_ID, mPlainTextPath, NO_SCREENSHOT);
killService();
- Bundle extras = acceptBugreportAndGetSharedIntent(NO_PID);
+ Bundle extras = acceptBugreportAndGetSharedIntent(NO_ID);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
}
@@ -463,32 +548,38 @@
private void assertProgressNotification(String name, String percent) {
// TODO: it currently looks for 3 distinct objects, without taking advantage of their
// relationship.
- openProgressNotification(PID);
+ openProgressNotification(ID);
Log.v(TAG, "Looking for progress notification details: '" + name + "-" + percent + "'");
mUiBot.getObject(name);
mUiBot.getObject(percent);
}
- private void openProgressNotification(int pid) {
- String title = mContext.getString(R.string.bugreport_in_progress_title, pid);
+ private UiObject openProgressNotification(int id) {
+ String title = mContext.getString(R.string.bugreport_in_progress_title, id);
Log.v(TAG, "Looking for progress notification title: '" + title + "'");
- mUiBot.getNotification(title);
+ return mUiBot.getNotification(title);
}
void resetProperties() {
// TODO: call method to remove property instead
- SystemProperties.set(PROGRESS_PROPERTY, "0");
- SystemProperties.set(MAX_PROPERTY, "0");
+ SystemProperties.set(PROGRESS_PROPERTY, "Reset");
+ SystemProperties.set(MAX_PROPERTY, "Reset");
+ SystemProperties.set(NAME_PROPERTY, "Reset");
}
/**
* Sends a "bugreport started" intent with the default values.
*/
private void sendBugreportStarted(int max) throws Exception {
+ sendBugreportStarted(ID, PID, NAME, max);
+ }
+
+ private void sendBugreportStarted(int id, int pid, String name, int max) throws Exception {
Intent intent = new Intent(INTENT_BUGREPORT_STARTED);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(EXTRA_PID, PID);
- intent.putExtra(EXTRA_NAME, NAME);
+ intent.putExtra(EXTRA_ID, id);
+ intent.putExtra(EXTRA_PID, pid);
+ intent.putExtra(EXTRA_NAME, name);
intent.putExtra(EXTRA_MAX, max);
mContext.sendBroadcast(intent);
}
@@ -500,7 +591,7 @@
*/
private Bundle sendBugreportFinishedAndGetSharedIntent(String bugreportPath,
String screenshotPath) {
- return sendBugreportFinishedAndGetSharedIntent(NO_PID, bugreportPath, screenshotPath);
+ return sendBugreportFinishedAndGetSharedIntent(NO_ID, bugreportPath, screenshotPath);
}
/**
@@ -508,10 +599,10 @@
*
* @return extras sent in the shared intent.
*/
- private Bundle sendBugreportFinishedAndGetSharedIntent(int pid, String bugreportPath,
+ private Bundle sendBugreportFinishedAndGetSharedIntent(int id, String bugreportPath,
String screenshotPath) {
- sendBugreportFinished(pid, bugreportPath, screenshotPath);
- return acceptBugreportAndGetSharedIntent(pid);
+ sendBugreportFinished(id, bugreportPath, screenshotPath);
+ return acceptBugreportAndGetSharedIntent(id);
}
/**
@@ -519,8 +610,8 @@
*
* @return extras sent in the shared intent.
*/
- private Bundle acceptBugreportAndGetSharedIntent(int pid) {
- acceptBugreport(pid);
+ private Bundle acceptBugreportAndGetSharedIntent(int id) {
+ acceptBugreport(id);
mUiBot.chooseActivity(UI_NAME);
return mListener.getExtras();
}
@@ -528,25 +619,25 @@
/**
* Waits for the notification to share the finished bugreport.
*/
- private void waitShareNotification(int pid) {
- mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title, pid));
+ private void waitShareNotification(int id) {
+ mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title, id));
}
/**
* Accepts the notification to share the finished bugreport.
*/
- private void acceptBugreport(int pid) {
- mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, pid));
+ private void acceptBugreport(int id) {
+ mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, id));
}
/**
* Sends a "bugreport finished" intent.
*/
- private void sendBugreportFinished(int pid, String bugreportPath, String screenshotPath) {
+ private void sendBugreportFinished(int id, String bugreportPath, String screenshotPath) {
Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- if (pid != NO_PID) {
- intent.putExtra(EXTRA_PID, pid);
+ if (id != NO_ID) {
+ intent.putExtra(EXTRA_ID, id);
}
if (bugreportPath != null) {
intent.putExtra(EXTRA_BUGREPORT, bugreportPath);
@@ -563,7 +654,7 @@
*/
private void assertActionSendMultiple(Bundle extras, String bugreportContent,
String screenshotContent) throws IOException {
- assertActionSendMultiple(extras, bugreportContent, screenshotContent, PID, ZIP_FILE,
+ assertActionSendMultiple(extras, bugreportContent, screenshotContent, ID, PID, ZIP_FILE,
NO_NAME, NO_TITLE, NO_DESCRIPTION, 0, DIDNT_RENAME_SCREENSHOTS);
}
@@ -573,6 +664,7 @@
* @param extras extras received in the intent
* @param bugreportContent expected content in the bugreport file
* @param screenshotContent expected content in the screenshot file (sent by dumpstate), if any
+ * @param id emulated dumpstate id
* @param pid emulated dumpstate pid
* @param name expected subject
* @param name bugreport name as provided by the user (or received by dumpstate)
@@ -582,7 +674,7 @@
* @param renamedScreenshots whether the screenshots are expected to be renamed
*/
private void assertActionSendMultiple(Bundle extras, String bugreportContent,
- String screenshotContent, int pid, String subject,
+ String screenshotContent, int id, int pid, String subject,
String name, String title, String description,
int numberScreenshots, boolean renamedScreenshots) throws IOException {
String body = extras.getString(Intent.EXTRA_TEXT);
@@ -745,7 +837,7 @@
fail("Service status didn't change to " + expectRunning);
}
- private static void createTextFile(String path, String content) throws IOException {
+ private void createTextFile(String path, String content) throws IOException {
Log.v(TAG, "createFile(" + path + ")");
try (Writer writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(path)))) {
@@ -781,7 +873,7 @@
* Gets the notification button used to take a screenshot.
*/
private UiObject getScreenshotButton() {
- openProgressNotification(PID);
+ openProgressNotification(ID);
return mUiBot.getVisibleObject(
mContext.getString(R.string.bugreport_screenshot_action).toUpperCase());
}
@@ -833,15 +925,28 @@
/**
* Gets the UI objects by opening the progress notification and clicking DETAILS.
*/
- DetailsUi(UiBot uiBot, int pid) throws UiObjectNotFoundException {
- openProgressNotification(pid);
- detailsButton = mUiBot.getVisibleObject(
- mContext.getString(R.string.bugreport_info_action).toUpperCase());
- mUiBot.click(detailsButton, "details_button");
+ DetailsUi(UiBot uiBot, int id) throws UiObjectNotFoundException {
+ this(uiBot, id, true);
+ }
+
+ /**
+ * Gets the UI objects by opening the progress notification and clicking on DETAILS or in
+ * the notification itself.
+ */
+ DetailsUi(UiBot uiBot, int id, boolean clickDetails) throws UiObjectNotFoundException {
+ UiObject notification = openProgressNotification(id);
+ detailsButton = mUiBot.getVisibleObject(mContext.getString(
+ R.string.bugreport_info_action).toUpperCase());
+
+ if (clickDetails) {
+ mUiBot.click(detailsButton, "details_button");
+ } else {
+ mUiBot.click(notification, "notification");
+ }
// TODO: unhardcode resource ids
UiObject dialogTitle = mUiBot.getVisibleObjectById("android:id/alertTitle");
assertEquals("Wrong title", mContext.getString(R.string.bugreport_info_dialog_title,
- pid), dialogTitle.getText().toString());
+ id), dialogTitle.getText().toString());
nameField = mUiBot.getVisibleObjectById("com.android.shell:id/name");
titleField = mUiBot.getVisibleObjectById("com.android.shell:id/title");
descField = mUiBot.getVisibleObjectById("com.android.shell:id/description");
@@ -849,6 +954,28 @@
cancelButton = mUiBot.getObjectById("android:id/button2");
}
+ private void assertField(String name, UiObject field, String expected) {
+ try {
+ String actual = field.getText().toString();
+ assertEquals("Wrong value on field '" + name + "'", expected, actual);
+ } catch (UiObjectNotFoundException e) {
+ // Should not happen...
+ throw new IllegalStateException("field not found: " + name, e);
+ }
+ }
+
+ void assertName(String expected) {
+ assertField("name", nameField, expected);
+ }
+
+ void assertTitle(String expected) {
+ assertField("title", titleField, expected);
+ }
+
+ void assertDescription(String expected) {
+ assertField("description", descField, expected);
+ }
+
/**
* Takes focus away from the name field so it can be validated.
*/
@@ -858,7 +985,7 @@
}
void reOpen() {
- openProgressNotification(PID);
+ openProgressNotification(ID);
mUiBot.click(detailsButton, "details_button");
}
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 88313bb..ad3c26b 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -1,10 +1,24 @@
LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := SystemUI-proto-tags
+
+LOCAL_SRC_FILES := $(call all-proto-files-under,src) \
+ src/com/android/systemui/EventLogTags.logtags
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# ------------------
+
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) $(call all-Iaidl-files-under, src) \
- src/com/android/systemui/EventLogTags.logtags
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
Keyguard \
@@ -13,13 +27,12 @@
android-support-v7-appcompat \
android-support-v14-preference \
android-support-v17-leanback \
- framework-protos
+ framework-protos \
+ SystemUI-proto-tags
LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_PACKAGE_NAME := SystemUI
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
new file mode 100644
index 0000000..5cabb77
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <size
+ android:width="36dp"
+ android:height="36dp" />
+ <solid
+ android:color="#4DFFFFFF" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/tv_pip_close_button.xml b/packages/SystemUI/res/drawable/tv_pip_close_button.xml
new file mode 100644
index 0000000..86fda0d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_close_button.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:constantSize="true">
+ <item android:state_focused="true">
+ <layer-list>
+ <item android:drawable="@drawable/tv_pip_button_focused" />
+ <item android:drawable="@drawable/ic_close_white" />
+ </layer-list>
+ </item>
+ <item android:drawable="@drawable/ic_close_white" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_full_button.xml b/packages/SystemUI/res/drawable/tv_pip_full_button.xml
new file mode 100644
index 0000000..332c669
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_full_button.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:constantSize="true">
+ <item android:state_focused="true">
+ <layer-list>
+ <item android:drawable="@drawable/tv_pip_button_focused" />
+ <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
+ </layer-list>
+ </item>
+ <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_outline.xml b/packages/SystemUI/res/drawable/tv_pip_outline.xml
new file mode 100644
index 0000000..c84438c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_outline.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <stroke android:width="2dp" android:color="#EEEEEE" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/tv_pip_pause_button.xml b/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
new file mode 100644
index 0000000..d277b07
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:constantSize="true">
+ <item android:state_focused="true">
+ <layer-list>
+ <item android:drawable="@drawable/tv_pip_button_focused" />
+ <item android:drawable="@drawable/ic_pause_white_24dp" />
+ </layer-list>
+ </item>
+ <item android:drawable="@drawable/ic_pause_white_24dp" />
+</selector>
diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml
index 7ea5027..cfaf018 100644
--- a/packages/SystemUI/res/layout/docked_stack_divider.xml
+++ b/packages/SystemUI/res/layout/docked_stack_divider.xml
@@ -27,6 +27,7 @@
<com.android.systemui.stackdivider.DividerHandleView
style="@style/DockedDividerHandle"
android:id="@+id/docked_divider_handle"
+ android:contentDescription="@string/accessibility_divider"
android:background="@null"/>
</com.android.systemui.stackdivider.DividerView>
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 4d0eb96..62f2b47 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -26,7 +26,7 @@
android:orientation="vertical"
android:paddingStart="@*android:dimen/notification_content_margin_start"
android:paddingEnd="8dp"
- android:background="@color/notification_guts_bg_color" >
+ android:background="@color/notification_guts_bg_color">
<!-- header -->
<LinearLayout
@@ -58,8 +58,38 @@
android:layout_gravity="bottom|start"
android:visibility="gone" />
</LinearLayout>
+ <!-- Importance radio buttons -->
+ <RadioGroup
+ android:id="@+id/importance_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dip"
+ android:paddingEnd="8dp" >
+ <RadioButton
+ android:id="@+id/silent_importance"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="@string/show_silently"
+ style="@style/TextAppearance.NotificationGuts.Primary"
+ android:buttonTint="#858383" />
+ <RadioButton
+ android:id="@+id/block_importance"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="@string/block"
+ style="@style/TextAppearance.NotificationGuts.Primary"
+ android:buttonTint="#858383" />
+ <RadioButton
+ android:id="@+id/reset_importance"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ style="@style/TextAppearance.NotificationGuts.Primary"
+ android:buttonTint="#858383" />
+ </RadioGroup>
<!-- Importance slider -->
<LinearLayout
+ android:id="@+id/importance_slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
@@ -67,7 +97,8 @@
android:clickable="false"
android:focusable="false"
android:paddingBottom="8dip"
- android:paddingEnd="8dp" >
+ android:paddingEnd="8dp"
+ android:visibility="gone">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 458ce2d..582ad48 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -34,7 +34,9 @@
android:id="@android:id/list"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="0dp"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ android:scrollIndicators="top"
+ android:scrollbars="vertical" />
<View
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 3ee2475..994d3c9 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -28,9 +28,7 @@
android:layout_marginTop="@dimen/status_bar_header_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="8dp"
- android:clipToPadding="false"
- android:clipChildren="false" />
+ android:paddingBottom="8dp" />
<include layout="@layout/quick_status_bar_expanded_header" />
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 2e41561..fa65758 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -30,21 +30,41 @@
android:paddingBottom="8dp"
android:paddingStart="12dp"
android:paddingEnd="16dp" />
- <TextView
- android:id="@+id/title"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:layout_marginStart="56dp"
- android:layout_marginEnd="112dp"
- android:textSize="16sp"
- android:textColor="#ffffffff"
- android:text="@string/recents_empty_message"
- android:fontFamily="sans-serif-medium"
- android:singleLine="true"
- android:maxLines="2"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal" />
+ android:layout_marginEnd="56dp"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textSize="16sp"
+ android:textColor="#ffffffff"
+ android:text="@string/recents_empty_message"
+ android:fontFamily="sans-serif-medium"
+ android:singleLine="true"
+ android:maxLines="1"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+ <TextView
+ android:id="@+id/sub_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textSize="11sp"
+ android:textColor="#ffffffff"
+ android:text="@string/recents_launch_non_dockable_task_label"
+ android:fontFamily="sans-serif-medium"
+ android:singleLine="true"
+ android:maxLines="1"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:visibility="gone" />
+ </LinearLayout>
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/move_task"
android:layout_width="@dimen/recents_task_view_header_button_width"
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index 3562c64..0b98d0e 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -18,36 +18,94 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="end"
- android:paddingStart="10dp"
- android:paddingEnd="10dp"
- android:background="#88FFFFFF"
- android:gravity="center_vertical" >
+ android:orientation="horizontal"
+ android:paddingTop="350dp"
+ android:background="#CC000000"
+ android:gravity="top|center_horizontal"
+ android:clipChildren="false">
- <Button android:id="@+id/full"
- android:layout_width="match_parent"
+ <LinearLayout
+ android:layout_width="34dp"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/pip_fullscreen"
- android:textSize="10sp"
- android:focusable="true" />
+ android:layout_marginEnd="3dp"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:clipChildren="false">
- <Button android:id="@+id/exit"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/pip_exit"
- android:textSize="10sp"
- android:focusable="true" />
+ <ImageView android:id="@+id/full"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:src="@drawable/tv_pip_full_button" />
- <Button android:id="@+id/cancel"
- android:layout_width="match_parent"
+ <TextView android:id="@+id/full_desc"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="3dp"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:text="@string/pip_fullscreen"
+ android:fontFamily="sans-serif"
+ android:textSize="12sp"
+ android:textColor="#EEEEEE"
+ android:clipChildren="false" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="34dp"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/pip_cancel"
- android:textSize="10sp"
- android:focusable="true" />
+ android:layout_marginStart="3dp"
+ android:layout_marginEnd="3dp"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:visibility="gone"
+ android:clipChildren="false">
+
+ <ImageView android:id="@+id/play_pause"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:src="@drawable/tv_pip_pause_button" />
+
+ <TextView android:id="@+id/play_pause_desc"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="3dp"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:text="@string/pip_pause"
+ android:fontFamily="sans-serif"
+ android:textSize="12sp"
+ android:textColor="#EEEEEE"
+ android:clipChildren="false" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="34dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="3dp"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:clipChildren="false">
+
+ <ImageView android:id="@+id/close"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:src="@drawable/tv_pip_close_button" />
+
+ <TextView android:id="@+id/close_desc"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="3dp"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:text="@string/pip_close"
+ android:fontFamily="sans-serif"
+ android:textSize="12sp"
+ android:textColor="#EEEEEE"
+ android:clipChildren="false" />
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_onboarding.xml b/packages/SystemUI/res/layout/tv_pip_onboarding.xml
index ef39555..f031bb4 100644
--- a/packages/SystemUI/res/layout/tv_pip_onboarding.xml
+++ b/packages/SystemUI/res/layout/tv_pip_onboarding.xml
@@ -18,34 +18,43 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pip_onboarding"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C00288D1"
- android:gravity="center"
- android:orientation="vertical" >
+ android:gravity="top|center_horizontal"
+ android:orientation="vertical">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="30sp"
- android:textColor="@android:color/white"
- android:text="@string/pip_onboarding_title" />
+ <!-- A rectangle arounds the PIP.
+ Size and positions will be programatically set up
+ to comply with config_defaultPictureInPictureBounds. -->
<ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_sysbar_home" />
+ android:id="@+id/pip_outline"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:src="@drawable/tv_pip_outline" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="30dp"
- android:textSize="13sp"
- android:textColor="@android:color/white"
+ android:layout_marginTop="24dp"
+ android:fontFamily="sans-serif"
+ android:textSize="16sp"
+ android:textColor="#EEEEEE"
+ android:lineSpacingMultiplier="1.28"
android:text="@string/pip_onboarding_description" />
<Button
android:id="@+id/close"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="15sp"
+ android:layout_height="36dp"
+ android:layout_marginTop="24dp"
+ android:gravity="center"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:fontFamily="sans-serif-condensed"
+ android:textSize="16sp"
+ android:textColor="#026089"
android:textAllCaps="true"
- android:text="@string/pip_onboarding_button" />
+ android:text="@string/pip_onboarding_button"
+ android:background="#EEEEEE"
+ android:elevation="4dp" />
</LinearLayout>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 4d7d6b5..791d761 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
- <item name="android:layout_width">480dp</item>
+ <item name="android:layout_width">@dimen/standard_notification_panel_width</item>
</style>
<style name="UserDetailView">
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index 77605bd..bf32cc7 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -31,4 +31,7 @@
<!-- Values for focus animation -->
<dimen name="recents_tv_unselected_item_z">6dp</dimen>
<dimen name="recents_tv_selected_item_z_delta">10dp</dimen>
-</resources>
\ No newline at end of file
+
+ <!-- Extra space around the PIP and its outline in PIP onboarding activity -->
+ <dimen name="tv_pip_bounds_space">3dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 87aedab..b9eee2e 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -44,6 +44,10 @@
<item type="id" name="notification_screenshot"/>
<item type="id" name="notification_hidden"/>
<item type="id" name="notification_volumeui"/>
+ <item type="id" name="transformation_start_x_tag"/>
+ <item type="id" name="transformation_start_y_tag"/>
+ <item type="id" name="transformation_start_scale_x_tag"/>
+ <item type="id" name="transformation_start_scale_y_tag"/>
<!-- Whether the icon is from a notification for which targetSdk < L -->
<item type="id" name="icon_is_pre_L"/>
@@ -56,5 +60,11 @@
<item type="id" name="image_icon_tag" />
<item type="id" name="contains_transformed_view" />
<item type="id" name="is_clicked_heads_up_tag" />
+
+ <!-- Accessibility actions for the docked stack divider -->
+ <item type="id" name="action_move_left" />
+ <item type="id" name="action_move_right" />
+ <item type="id" name="action_move_up" />
+ <item type="id" name="action_move_down" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6f6e515..1f239c3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -497,6 +497,8 @@
<string name="accessibility_quick_settings_less_time">Less time.</string>
<!-- Content description of the flashlight tile in quick settings when off (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_flashlight_off">Flashlight off.</string>
+ <!-- Content description of the flashlight tile in quick settings when unavailable (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_flashlight_unavailable">Flashlight unavailable.</string>
<!-- Content description of the flashlight tile in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_flashlight_on">Flashlight on.</string>
<!-- Announcement made when the flashlight state changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -722,6 +724,10 @@
<string name="recents_history_button_label">History</string>
<!-- Recents: History clear all string. [CHAR LIMIT=NONE] -->
<string name="recents_history_clear_all_button_label">Clear</string>
+ <!-- Recents: Non-dockable task drag message. [CHAR LIMIT=NONE] -->
+ <string name="recents_drag_non_dockable_task_message">This app does not support multi-window</string>
+ <!-- Recents: Non-dockable task launch sub header. [CHAR LIMIT=NONE] -->
+ <string name="recents_launch_non_dockable_task_label">App does not support multi-window</string>
<!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
<string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
@@ -1201,12 +1207,22 @@
<!-- Bluetooth enablement ok text [CHAR LIMIT=40] -->
<string name="enable_bluetooth_confirmation_ok">Turn on</string>
- <!-- Apply notification importance setting to a topic [CHAR LIMIT=NONE] -->
- <string name="apply_to_topic">Apply to <xliff:g id="topic_name" example="Friend Request">%1$s</xliff:g> notifications</string>
- <!-- Apply notification importance setting to an app [CHAR LIMIT=NONE] -->
- <string name="apply_to_app">Apply to all notifications from this app</string>
+ <!-- [CHAR LIMIT=100] Notification importance option -->
+ <string name="show_silently">Show notifications silently</string>
+ <!-- [CHAR LIMIT=100] Notification importance option -->
+ <string name="block">Block all notifications</string>
+ <!-- [CHAR LIMIT=100] Notification importance option -->
+ <string name="do_not_silence">Don\'t silence</string>
+ <!-- [CHAR LIMIT=100] Notification importance option -->
+ <string name="do_not_silence_block">Don\'t silence or block</string>
+
+ <!-- [CHAR LIMIT=NONE] Importance Tuner setting title -->
+ <string name="tuner_full_importance_settings">Show full importance settings</string>
+
<!-- Notification importance title, blocked status-->
<string name="blocked_importance">Blocked</string>
+ <!-- Notification importance title, min status-->
+ <string name="min_importance">Min importance</string>
<!-- Notification importance title, low status-->
<string name="low_importance">Low importance</string>
<!-- Notification importance title, normal status-->
@@ -1219,17 +1235,20 @@
<!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
<string name="notification_importance_blocked">Never show these notifications</string>
+ <!-- [CHAR LIMIT=100] Notification Importance slider: min importance level description -->
+ <string name="notification_importance_min">Silently show at the bottom of the notification list</string>
+
<!-- [CHAR LIMIT=100] Notification Importance slider: low importance level description -->
- <string name="notification_importance_low">Silently show at the bottom of the notification list</string>
+ <string name="notification_importance_low">Silently show these notifications</string>
<!-- [CHAR LIMIT=100] Notification Importance slider: normal importance level description -->
- <string name="notification_importance_default">Silently show these notifications</string>
+ <string name="notification_importance_default">Allow these notification to make sounds</string>
<!-- [CHAR LIMIT=100] Notification Importance slider: high importance level description -->
- <string name="notification_importance_high">Show at the top of the notifications list and make sound</string>
+ <string name="notification_importance_high">Peek onto the screen and allow sound and allow sound</string>
<!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
- <string name="notification_importance_max">Peek onto the screen and make sound</string>
+ <string name="notification_importance_max">Show at the top of the notifications list, peek onto the screen and allow sound</string>
<!-- Notification: Control panel: Label for button that launches notification settings. [CHAR LIMIT=NONE] -->
<string name="notification_more_settings">More settings</string>
@@ -1430,4 +1449,22 @@
<item>Don\'t show this icon</item>
</string-array>
+ <!-- SysUI Tuner: Other section -->
+ <string name="other">Other</string>
+
+ <!-- Accessibility label for the divider that separates the windows in split-screen mode [CHAR LIMIT=NONE] -->
+ <string name="accessibility_divider">Split-screen divider</string>
+
+ <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_divider_move_down">Move down</string>
+
+ <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_divider_move_up">Move up</string>
+
+ <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_divider_move_left">Move left</string>
+
+ <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_divider_move_right">Move right</string>
+
</resources>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 7c4768d..4f382ea 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -20,7 +20,7 @@
<!-- Picture-in-Picture menu -->
<eat-comment />
<!-- Button to close PIP on PIP UI -->
- <string name="pip_exit" translatable="false">Close PIP</string>
+ <string name="pip_close" translatable="false">Close PIP</string>
<!-- Button to move PIP screen to the fullscreen on PIP UI -->
<string name="pip_fullscreen" translatable="false">Full screen</string>
<!-- Button to play the current media on PIP UI -->
@@ -34,8 +34,6 @@
<!-- Picture-in-Picture onboarding screen -->
<eat-comment />
- <!-- Title for onboarding screen. -->
- <string name="pip_onboarding_title" translatable="false">Picture-in-picture</string>
<!-- Description for onboarding screen. -->
<string name="pip_onboarding_description" translatable="false">Press and hold the HOME\nbutton to close or control it</string>
<!-- Button to close onboarding screen. -->
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index f4a0cc9..ddc03a3 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -134,8 +134,8 @@
-->
<PreferenceScreen
- android:key="overview"
- android:title="@string/overview" >
+ android:key="other"
+ android:title="@string/other" >
<com.android.systemui.tuner.TunerSwitch
android:key="overview_disable_fast_toggle_via_button"
@@ -147,6 +147,11 @@
android:title="@string/overview_nav_bar_gesture"
android:summary="@string/overview_nav_bar_gesture_desc" />
+ <!-- importance -->
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="show_importance_slider"
+ android:title="@string/tuner_full_importance_settings" />
+
</PreferenceScreen>
<!-- Warning, this goes last. -->
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 1471622..9f2745b 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -48,6 +48,7 @@
Key.QS_DATA_SAVER_ADDED,
Key.QS_INVERT_COLORS_ADDED,
Key.QS_WORK_ADDED,
+ Key.QS_NIGHT_ADDED,
})
public @interface Key {
String OVERVIEW_SEARCH_APP_WIDGET_ID = "searchAppWidgetId";
@@ -68,6 +69,7 @@
String QS_DATA_SAVER_ADDED = "QsDataSaverAdded";
String QS_INVERT_COLORS_ADDED = "QsInvertColorsAdded";
String QS_WORK_ADDED = "QsWorkAdded";
+ String QS_NIGHT_ADDED = "QsNightAdded";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 0915ee1..2c5cb89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -38,7 +38,7 @@
if (mPageIndicator == null) return;
mPageIndicator.setLocation(position);
if (mPageListener != null) {
- mPageListener.onPageChanged(position);
+ mPageListener.onPageChanged(position == 0);
}
}
@@ -47,6 +47,9 @@
int positionOffsetPixels) {
if (mPageIndicator == null) return;
mPageIndicator.setLocation(position + positionOffset);
+ if (mPageListener != null) {
+ mPageListener.onPageChanged(position == 0 && positionOffsetPixels == 0);
+ }
}
@Override
@@ -57,6 +60,11 @@
}
@Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
mPageIndicator = (PageIndicator) findViewById(R.id.page_indicator);
@@ -209,6 +217,6 @@
};
public interface PageListener {
- void onPageChanged(int page);
+ void onPageChanged(boolean isFirst);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index dc64591..c31bb33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -69,11 +69,13 @@
}
@Override
- public void onPageChanged(int page) {
- mOnFirstPage = page == 0;
- if (!mOnFirstPage) {
+ public void onPageChanged(boolean isFirst) {
+ if (mOnFirstPage == isFirst) return;
+ if (!isFirst) {
+ setPosition(1);
clearAnimationState();
}
+ mOnFirstPage = isFirst;
}
private void updateAnimators() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index abe4c77..4408dbf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -183,5 +183,10 @@
// No resources here.
return false;
}
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 94d8524..026dd0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -58,7 +58,7 @@
}
if (mListener != null) {
if (mLastT == 0 || mLastT == 1) {
- if (t != 0) {
+ if (t != mLastT) {
mListener.onAnimationStarted();
}
} else if (t == 1) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 876f417..c6c497f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -107,7 +107,10 @@
mOtherTiles = new ArrayList<TileInfo>(mAllTiles);
mTiles.clear();
for (int i = 0; i < mCurrentSpecs.size(); i++) {
- mTiles.add(getAndRemoveOther(mCurrentSpecs.get(i)));
+ final TileInfo tile = getAndRemoveOther(mCurrentSpecs.get(i));
+ if (tile != null) {
+ mTiles.add(tile);
+ }
}
mTiles.add(null);
mTiles.addAll(mOtherTiles);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index c3610d9..aa85f78 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -28,11 +28,14 @@
import android.os.Handler;
import android.os.Looper;
import android.service.quicksettings.TileService;
+import com.android.systemui.Prefs;
+import com.android.systemui.Prefs.Key;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTile.DrawableIcon;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.tuner.TunerService;
import java.util.ArrayList;
import java.util.Collection;
@@ -54,7 +57,8 @@
}
private void addSystemTiles(QSTileHost host) {
- boolean hasColorMod = host.getNightModeController().isEnabled();
+ boolean hasColorMod = Prefs.getBoolean(host.getContext(), Key.QS_NIGHT_ADDED, false)
+ && TunerService.isTunerEnabled(host.getContext());
String possible = mContext.getString(R.string.quick_settings_tiles_default)
+ ",hotspot,inversion,saver,work,cast" + (hasColorMod ? ",night" : "");
String[] possibleTiles = possible.split(",");
@@ -88,7 +92,12 @@
qsHandler.post(new Runnable() {
@Override
public void run() {
- new QueryTilesTask().execute();
+ mainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ new QueryTilesTask().execute();
+ }
+ });
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index c10843a..e4e3790 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -17,9 +17,11 @@
package com.android.systemui.qs.tiles;
import android.app.ActivityManager;
-
import android.content.Intent;
+import android.graphics.drawable.Drawable;
import android.provider.MediaStore;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.R;
@@ -79,9 +81,19 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- // TODO: Flashlight available handling...
-// state.visible = mFlashlightController.isAvailable();
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
+ if (!mFlashlightController.isAvailable()) {
+ Drawable icon = mHost.getContext().getDrawable(R.drawable.ic_signal_flashlight_disable);
+ final int disabledColor = mHost.getContext().getColor(R.color.qs_tile_tint_unavailable);
+ icon.setTint(disabledColor);
+ state.icon = new DrawableIcon(icon);
+ state.label = new SpannableStringBuilder().append(state.label,
+ new ForegroundColorSpan(disabledColor),
+ SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
+ state.contentDescription = mContext.getString(
+ R.string.accessibility_quick_settings_flashlight_unavailable);
+ return;
+ }
if (arg instanceof Boolean) {
boolean value = (Boolean) arg;
if (value == state.value) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index c2a6108..c41098f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -528,17 +528,16 @@
@Override
public void onMultiWindowChanged(boolean inMultiWindow) {
super.onMultiWindowChanged(inMultiWindow);
- if (!inMultiWindow) {
- RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.loadIcons = false;
- launchOpts.loadThumbnails = false;
- launchOpts.onlyLoadForCache = true;
- RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
- loader.preloadTasks(loadPlan, -1, false);
- loader.loadTasks(this, loadPlan, launchOpts);
- EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack()));
- }
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.loadIcons = false;
+ launchOpts.loadThumbnails = false;
+ launchOpts.onlyLoadForCache = true;
+ RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
+ loader.preloadTasks(loadPlan, -1, false);
+ loader.loadTasks(this, loadPlan, launchOpts);
+ EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack(),
+ inMultiWindow));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 8de964b..28b2fae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -161,10 +161,8 @@
Handler mHandler;
TaskStackListenerImpl mTaskStackListener;
RecentsAppWidgetHost mAppWidgetHost;
- boolean mBootCompleted;
boolean mCanReuseTaskStackViews = true;
boolean mDraggingInRecents;
- boolean mReloadTasks;
boolean mLaunchedWhileDocking;
// Task launching
@@ -236,7 +234,6 @@
}
public void onBootCompleted() {
- mBootCompleted = true;
updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
}
@@ -317,23 +314,21 @@
}
public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- if (mBootCompleted) {
- if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
- // The user has released alt-tab before the trigger has run, so just show the next
- // task immediately
- showNextTask();
+ if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
+ // The user has released alt-tab before the trigger has run, so just show the next
+ // task immediately
+ showNextTask();
- // Cancel the fast alt-tab trigger
- mFastAltTabTrigger.stopDozing();
- mFastAltTabTrigger.resetTrigger();
- return;
- }
-
- // Defer to the activity to handle hiding recents, if it handles it, then it must still
- // be visible
- EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
- triggeredFromHomeKey));
+ // Cancel the fast alt-tab trigger
+ mFastAltTabTrigger.stopDozing();
+ mFastAltTabTrigger.resetTrigger();
+ return;
}
+
+ // Defer to the activity to handle hiding recents, if it handles it, then it must still
+ // be visible
+ EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
+ triggeredFromHomeKey));
}
public void toggleRecents() {
@@ -347,7 +342,6 @@
mTriggeredFromAltTab = false;
try {
- ViewConfiguration viewConfig = ViewConfiguration.get(mContext);
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
MutableBoolean isTopTaskHome = new MutableBoolean(true);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
index f87f6de..0d614e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
@@ -28,8 +28,10 @@
* A new TaskStack instance representing the latest stack state.
*/
public final TaskStack stack;
+ public final boolean inMultiWindow;
- public TaskStackUpdatedEvent(TaskStack stack) {
+ public TaskStackUpdatedEvent(TaskStack stack, boolean inMultiWindow) {
this.stack = stack;
+ this.inMultiWindow = inMultiWindow;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 824231a..6fef8a2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -198,8 +198,8 @@
// Add the task to the stack
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
thumbnail, title, contentDescription, dismissDescription, activityColor,
- backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.bounds,
- t.taskDescription);
+ backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.isDockable,
+ t.bounds, t.taskDescription);
allTasks.add(task);
affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 8ed6dd7..e5d4f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -168,6 +168,8 @@
public boolean isHistorical;
@ViewDebug.ExportedProperty(category="recents")
public boolean isSystemApp;
+ @ViewDebug.ExportedProperty(category="recents")
+ public boolean isDockable;
private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
@@ -178,8 +180,8 @@
public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
Bitmap thumbnail, String title, String contentDescription,
String dismissDescription, int colorPrimary, int colorBackground,
- boolean isHistorical, boolean isLaunchTarget, boolean isSystemApp, Rect bounds,
- ActivityManager.TaskDescription taskDescription) {
+ boolean isHistorical, boolean isLaunchTarget, boolean isSystemApp,
+ boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription) {
boolean isInAffiliationGroup = (affiliationTaskId != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
this.key = key;
@@ -199,6 +201,7 @@
this.isLaunchTarget = isLaunchTarget;
this.isHistorical = isHistorical;
this.isSystemApp = isSystemApp;
+ this.isDockable = isDockable;
}
/** Copies the other task. */
@@ -219,6 +222,7 @@
this.isLaunchTarget = o.isLaunchTarget;
this.isHistorical = o.isHistorical;
this.isSystemApp = o.isSystemApp;
+ this.isDockable = o.isDockable;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 2e45627..5dde926 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -663,8 +663,10 @@
}
public final void onBusEvent(TaskStackUpdatedEvent event) {
- mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
- mStack.createAffiliatedGroupings(getContext());
+ if (!event.inMultiWindow) {
+ mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
+ mStack.createAffiliatedGroupings(getContext());
+ }
}
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 016d937..079d7b9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -21,7 +21,9 @@
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
+import android.widget.Toast;
+import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
@@ -148,11 +150,16 @@
mVisibleDockStates.clear();
if (!ssp.hasDockedTask() && mRv.getTaskStack().getTaskCount() > 1) {
- // Add the dock state drop targets (these take priority)
- TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
- for (TaskStack.DockState dockState : dockStates) {
- registerDropTargetForCurrentDrag(dockState);
- mVisibleDockStates.add(dockState);
+ if (!event.task.isDockable) {
+ Toast.makeText(mRv.getContext(), R.string.recents_drag_non_dockable_task_message,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ // Add the dock state drop targets (these take priority)
+ TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
+ for (TaskStack.DockState dockState : dockStates) {
+ registerDropTargetForCurrentDrag(dockState);
+ mVisibleDockStates.add(dockState);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index d71d70f..e1a81c8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1573,6 +1573,9 @@
Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(launchTask),
launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */));
+
+ MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
+ launchTask.key.getComponent().toString());
}
}
@@ -1830,12 +1833,28 @@
}
public final void onBusEvent(TaskStackUpdatedEvent event) {
- // Scroll the stack to the front after it has been updated
+ if (!event.inMultiWindow) {
+ // Scroll the stack to the front after it has been updated
+ event.addPostAnimationCallback(new Runnable() {
+ @Override
+ public void run() {
+ mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP,
+ null /* postScrollRunnable */);
+ }
+ });
+ }
+ // When the multi-window state changes, rebind all task view headers again to update their
+ // dockable state
event.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
- mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP,
- null /* postScrollRunnable */);
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
+ tv.mIsDisabledInSafeMode);
+ }
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 0543dd5..05a8527 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -139,6 +139,7 @@
// Header views
ImageView mIconView;
TextView mTitleView;
+ TextView mSubTitleView;
ImageView mMoveTaskButton;
ImageView mDismissButton;
ViewStub mAppOverlayViewStub;
@@ -237,6 +238,7 @@
mIconView.setClickable(false);
mIconView.setOnLongClickListener(this);
mTitleView = (TextView) findViewById(R.id.title);
+ mSubTitleView = (TextView) findViewById(R.id.sub_title);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
if (ssp.hasFreeformWorkspaceSupport()) {
mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
@@ -367,6 +369,7 @@
/** Binds the bar view to the task */
public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
@@ -384,6 +387,13 @@
mTitleView.setContentDescription(t.contentDescription);
mTitleView.setTextColor(t.useLightOnPrimaryColor ?
mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
+ if (!t.isDockable && ssp.hasDockedTask()) {
+ mSubTitleView.setVisibility(View.VISIBLE);
+ mSubTitleView.setTextColor(t.useLightOnPrimaryColor ?
+ mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
+ } else {
+ mSubTitleView.setVisibility(View.GONE);
+ }
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
mLightDismissDrawable : mDarkDismissDrawable);
mDismissButton.setContentDescription(t.dismissDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
index 9118e9c..d5f7b39 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -26,7 +26,7 @@
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Property;
-import android.widget.ImageButton;
+import android.view.View;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -34,7 +34,7 @@
/**
* View for the handle in the docked stack divider.
*/
-public class DividerHandleView extends ImageButton {
+public class DividerHandleView extends View {
private final static Property<DividerHandleView, Integer> WIDTH_PROPERTY
= new Property<DividerHandleView, Integer>(Integer.class, "width") {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 65a2f8f..da5cbe7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -27,6 +27,7 @@
import android.graphics.Rect;
import android.graphics.Region.Op;
import android.hardware.display.DisplayManager;
+import android.os.Bundle;
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayInfo;
@@ -40,6 +41,8 @@
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
@@ -132,6 +135,42 @@
private boolean mGrowRecents;
private Animator mCurrentAnimator;
+ private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ if (isHorizontalDivision()) {
+ info.addAction(new AccessibilityAction(R.id.action_move_up,
+ mContext.getString(R.string.accessibility_action_divider_move_up)));
+ info.addAction(new AccessibilityAction(R.id.action_move_down,
+ mContext.getString(R.string.accessibility_action_divider_move_down)));
+ } else {
+ info.addAction(new AccessibilityAction(R.id.action_move_left,
+ mContext.getString(R.string.accessibility_action_divider_move_left)));
+ info.addAction(new AccessibilityAction(R.id.action_move_right,
+ mContext.getString(R.string.accessibility_action_divider_move_right)));
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (action == R.id.action_move_up || action == R.id.action_move_down
+ || action == R.id.action_move_left || action == R.id.action_move_right) {
+ int position = getCurrentPosition();
+ SnapTarget currentTarget = mSnapAlgorithm.calculateSnapTarget(
+ position, 0 /* velocity */);
+ SnapTarget nextTarget =
+ action == R.id.action_move_up || action == R.id.action_move_left
+ ? mSnapAlgorithm.getPreviousTarget(currentTarget)
+ : mSnapAlgorithm.getNextTarget(currentTarget);
+ startDragging(true /* animate */, false /* touching */);
+ stopDragging(getCurrentPosition(), nextTarget, 250, Interpolators.FAST_OUT_SLOW_IN);
+ return true;
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+ };
+
public DividerView(Context context) {
super(context);
}
@@ -171,6 +210,7 @@
mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW));
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ mHandle.setAccessibilityDelegate(mHandleDelegate);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a0c63be..9bd645d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -110,7 +110,7 @@
import java.util.List;
import java.util.Locale;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
public abstract class BaseStatusBar extends SystemUI implements
@@ -1007,7 +1007,7 @@
}
});
- guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey()));
+ guts.bindImportance(pmUser, sbn, row, mNotificationData.getImportance(sbn.getKey()));
}
protected GearDisplayedListener getGearDisplayedListener() {
@@ -1044,9 +1044,9 @@
MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS);
- // ensure that it's layouted but not visible until actually laid out
+ // ensure that it's laid but not visible until actually laid out
guts.setVisibility(View.INVISIBLE);
- // Post to ensure the the guts are properly layed out.
+ // Post to ensure the the guts are properly laid out.
guts.post(new Runnable() {
public void run() {
dismissPopups();
@@ -1142,6 +1142,11 @@
}
@Override
+ public void toggleSplitScreen() {
+ toggleSplitScreenMode();
+ }
+
+ @Override
public void preloadRecentApps() {
int msg = MSG_PRELOAD_RECENT_APPS;
mHandler.removeMessages(msg);
@@ -1211,6 +1216,13 @@
}
};
+ /**
+ * Toggle docking the app window
+ *
+ * @return {@code true} if the app window is docked after the toggle, {@code false} otherwise.
+ */
+ protected abstract boolean toggleSplitScreenMode();
+
/** Proxy for RecentsComponent */
protected void showRecents(boolean triggeredFromAltTab) {
@@ -1872,7 +1884,7 @@
return entry;
}
- protected StatusBarIconView createIcon(StatusBarNotification sbn) {
+ public StatusBarIconView createIcon(StatusBarNotification sbn) {
// Construct the icon.
Notification n = sbn.getNotification();
final StatusBarIconView iconView = new StatusBarIconView(mContext,
@@ -2203,6 +2215,16 @@
return false;
}
+ if (isSnoozedPackage(sbn)) {
+ if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+ return false;
+ }
+
+ if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) {
+ if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+ return false;
+ }
+
if (sbn.getNotification().fullScreenIntent != null) {
if (mAccessibilityManager.isTouchExplorationEnabled()) {
if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
@@ -2212,16 +2234,6 @@
}
}
- if (isSnoozedPackage(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_MAX) {
- if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
- return false;
- }
-
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 3b960ee..6a98488 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -72,6 +72,7 @@
private static final int MSG_ADD_QS_TILE = 27 << MSG_SHIFT;
private static final int MSG_REMOVE_QS_TILE = 28 << MSG_SHIFT;
private static final int MSG_CLICK_QS_TILE = 29 << MSG_SHIFT;
+ private static final int MSG_TOGGLE_APP_SPLIT_SCREEN = 30 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -104,6 +105,7 @@
public void showRecentApps(boolean triggeredFromAltTab);
public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
public void toggleRecentApps();
+ public void toggleSplitScreen();
public void preloadRecentApps();
public void toggleKeyboardShortcutsMenu();
public void cancelPreloadRecentApps();
@@ -223,6 +225,13 @@
}
}
+ public void toggleSplitScreen() {
+ synchronized (mLock) {
+ mHandler.removeMessages(MSG_TOGGLE_APP_SPLIT_SCREEN);
+ mHandler.obtainMessage(MSG_TOGGLE_APP_SPLIT_SCREEN, 0, 0, null).sendToTarget();
+ }
+ }
+
public void toggleRecentApps() {
synchronized (mLock) {
mHandler.removeMessages(MSG_TOGGLE_RECENT_APPS);
@@ -464,6 +473,9 @@
case MSG_CLICK_QS_TILE:
mCallbacks.clickTile((ComponentName) msg.obj);
break;
+ case MSG_TOGGLE_APP_SPLIT_SCREEN:
+ mCallbacks.toggleSplitScreen();
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 212d290..123dc69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -19,6 +19,7 @@
import android.view.View;
import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
* A helper to fade views in and out.
@@ -44,7 +45,34 @@
if (view.hasOverlappingRendering()) {
view.animate().withLayer();
}
+ }
+ public static void fadeOut(View view, float fadeOutAmount) {
+ view.animate().cancel();
+ if (fadeOutAmount == 1.0f) {
+ view.setVisibility(View.INVISIBLE);
+ } else if (view.getVisibility() == View.INVISIBLE) {
+ view.setVisibility(View.VISIBLE);
+ }
+ fadeOutAmount = mapToFadeDuration(fadeOutAmount);
+ float alpha = Interpolators.ALPHA_OUT.getInterpolation(1.0f - fadeOutAmount);
+ view.setAlpha(alpha);
+ updateLayerType(view, alpha);
+ }
+
+ private static float mapToFadeDuration(float fadeOutAmount) {
+ // Assuming a linear interpolator, we can easily map it to our new duration
+ float endPoint = (float) ANIMATION_DURATION_LENGTH
+ / (float) StackStateAnimator.ANIMATION_DURATION_STANDARD;
+ return Math.min(fadeOutAmount / endPoint, 1.0f);
+ }
+
+ private static void updateLayerType(View view, float alpha) {
+ if (view.hasOverlappingRendering() && alpha > 0.0f && alpha < 1.0f) {
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ } else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE) {
+ view.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
}
public static void fadeIn(final View view) {
@@ -62,4 +90,15 @@
view.animate().withLayer();
}
}
+
+ public static void fadeIn(View view, float fadeInAmount) {
+ view.animate().cancel();
+ if (view.getVisibility() == View.INVISIBLE) {
+ view.setVisibility(View.VISIBLE);
+ }
+ fadeInAmount = mapToFadeDuration(fadeInAmount);
+ float alpha = Interpolators.ALPHA_IN.getInterpolation(fadeInAmount);
+ view.setAlpha(alpha);
+ updateLayerType(view, alpha);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index c72cec3..da125d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -868,6 +868,10 @@
public void setUserLocked(boolean userLocked) {
mUserLocked = userLocked;
+ mPrivateLayout.setUserExpanding(userLocked);
+ if (mIsSummaryWithChildren) {
+ mChildrenContainer.setUserLocked(userLocked);
+ }
}
/**
@@ -1174,6 +1178,9 @@
int contentHeight = Math.max(getMinHeight(), height);
mPrivateLayout.setContentHeight(contentHeight);
mPublicLayout.setContentHeight(contentHeight);
+ if (mIsSummaryWithChildren) {
+ mChildrenContainer.setActualHeight(height);
+ }
if (mGuts != null) {
mGuts.setActualHeight(height);
}
@@ -1259,7 +1266,7 @@
return mMaxExpandHeight != 0;
}
- private NotificationContentView getShowingLayout() {
+ public NotificationContentView getShowingLayout() {
return mShowingPublic ? mPublicLayout : mPrivateLayout;
}
@@ -1295,8 +1302,15 @@
}
@Override
- public boolean needsIncreasedPadding() {
- return mIsSummaryWithChildren && isGroupExpanded();
+ public float getIncreasedPaddingAmount() {
+ if (mIsSummaryWithChildren) {
+ if (isGroupExpanded()) {
+ return 1.0f;
+ } else if (isUserLocked()) {
+ return mChildrenContainer.getChildExpandFraction();
+ }
+ }
+ return 0.0f;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index c9e1cff..1ff87f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -391,8 +391,11 @@
public void setShadowAlpha(float shadowAlpha) {
}
- public boolean needsIncreasedPadding() {
- return false;
+ /**
+ * @return an amount between 0 and 1 of increased padding that this child needs
+ */
+ public float getIncreasedPaddingAmount() {
+ return 0.0f;
}
public boolean mustStayOnScreen() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index bc85922..977b37e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -48,6 +48,7 @@
private static final int VISIBLE_TYPE_EXPANDED = 1;
private static final int VISIBLE_TYPE_HEADSUP = 2;
private static final int VISIBLE_TYPE_SINGLELINE = 3;
+ private static final int UNDEFINED = -1;
private final Rect mClipBounds = new Rect();
private final int mMinContractedHeight;
@@ -102,6 +103,8 @@
private boolean mExpandable;
private boolean mClipToActualHeight = true;
private ExpandableNotificationRow mContainingNotification;
+ private int mTransformationStartVisibleType;
+ private boolean mUserExpanding;
public NotificationContentView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -349,6 +352,41 @@
invalidateOutline();
}
+ private void updateContentTransformation() {
+ int visibleType = calculateVisibleType();
+ if (visibleType != mVisibleType) {
+ // A new transformation starts
+ mTransformationStartVisibleType = mVisibleType;
+ final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
+ final TransformableView hiddenView = getTransformableViewForVisibleType(
+ mTransformationStartVisibleType);
+ shownView.transformFrom(hiddenView, 0.0f);
+ getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
+ hiddenView.transformTo(shownView, 0.0f);
+ mVisibleType = visibleType;
+ }
+ if (mTransformationStartVisibleType != UNDEFINED
+ && mVisibleType != mTransformationStartVisibleType) {
+ final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
+ final TransformableView hiddenView = getTransformableViewForVisibleType(
+ mTransformationStartVisibleType);
+ float transformationAmount = calculateTransformationAmount();
+ shownView.transformFrom(hiddenView, transformationAmount);
+ hiddenView.transformTo(shownView, transformationAmount);
+ } else {
+ updateViewVisibilities(visibleType);
+ }
+ }
+
+ private float calculateTransformationAmount() {
+ int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
+ int endHeight = getViewForVisibleType(mVisibleType).getHeight();
+ int progress = Math.abs(mContentHeight - startHeight);
+ int totalDistance = Math.abs(endHeight - startHeight);
+ float amount = (float) progress / (float) totalDistance;
+ return Math.min(1.0f, amount);
+ }
+
public int getContentHeight() {
return mContentHeight;
}
@@ -363,10 +401,14 @@
}
public int getMinHeight() {
- if (mIsChildInGroup && !isGroupExpanded()) {
- return mSingleLineView.getHeight();
- } else {
+ return getMinHeight(false /* likeGroupExpanded */);
+ }
+
+ public int getMinHeight(boolean likeGroupExpanded) {
+ if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
return mContractedChild.getHeight();
+ } else {
+ return mSingleLineView.getHeight();
}
}
@@ -397,6 +439,10 @@
if (mContractedChild == null) {
return;
}
+ if (mUserExpanding) {
+ updateContentTransformation();
+ return;
+ }
int visibleType = calculateVisibleType();
if (visibleType != mVisibleType || force) {
if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
@@ -492,13 +538,28 @@
* @return one of the static enum types in this view, calculated form the current state
*/
private int calculateVisibleType() {
- boolean noExpandedChild = mExpandedChild == null;
-
+ if (mUserExpanding) {
+ int height = !mIsChildInGroup || isGroupExpanded()
+ || mContainingNotification.isExpanded()
+ ? mContainingNotification.getMaxContentHeight()
+ : mContainingNotification.getShowingLayout().getMinHeight();
+ int expandedVisualType = getVisualTypeForHeight(height);
+ int collapsedVisualType = getVisualTypeForHeight(
+ mContainingNotification.getMinExpandHeight());
+ return mTransformationStartVisibleType == collapsedVisualType
+ ? expandedVisualType
+ : collapsedVisualType;
+ }
int viewHeight = Math.min(mContentHeight, mContainingNotification.getIntrinsicHeight());
+ return getVisualTypeForHeight(viewHeight);
+ }
+
+ private int getVisualTypeForHeight(float viewHeight) {
+ boolean noExpandedChild = mExpandedChild == null;
if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
return VISIBLE_TYPE_EXPANDED;
}
- if (mIsChildInGroup && !isGroupExpanded()) {
+ if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
return VISIBLE_TYPE_SINGLELINE;
}
@@ -723,4 +784,15 @@
updateSingleLineView();
}
}
+
+ public void setUserExpanding(boolean userExpanding) {
+ mUserExpanding = userExpanding;
+ if (userExpanding) {
+ mTransformationStartVisibleType = mVisibleType;
+ } else {
+ mTransformationStartVisibleType = UNDEFINED;
+ mVisibleType = calculateVisibleType();
+ updateViewVisibilities(mVisibleType);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index ccd0ad8..e300614 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -203,11 +203,11 @@
String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();
- // PRIORITY_MIN media streams are allowed to drift to the bottom
+ // IMPORTANCE_MIN media streams are allowed to drift to the bottom
final boolean aMedia = a.key.equals(mediaNotification)
- && aImportance > Ranking.IMPORTANCE_LOW;
+ && aImportance > Ranking.IMPORTANCE_MIN;
final boolean bMedia = b.key.equals(mediaNotification)
- && bImportance > Ranking.IMPORTANCE_LOW;
+ && bImportance > Ranking.IMPORTANCE_MIN;
boolean aSystemMax = aImportance >= Ranking.IMPORTANCE_MAX &&
isSystemNotification(na);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index fe84d813..1c16bdc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar;
import android.app.INotificationManager;
-import android.app.Notification;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -39,23 +38,31 @@
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.systemui.R;
+import com.android.systemui.tuner.TunerService;
/**
* The guts of a notification revealed when performing a long press.
*/
-public class NotificationGuts extends LinearLayout {
+public class NotificationGuts extends LinearLayout implements TunerService.Tunable {
+ public static final String SHOW_SLIDER = "show_importance_slider";
private Drawable mBackground;
private int mClipTopAmount;
private int mActualHeight;
private boolean mExposed;
- private SeekBar mSeekBar;
private INotificationManager mINotificationManager;
private int mStartingImportance;
+ private boolean mShowSlider;
+
+ private SeekBar mSeekBar;
+ private RadioButton mBlock;
+ private RadioButton mSilent;
+ private RadioButton mReset;
public NotificationGuts(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
+ TunerService.get(mContext).addTunable(this, SHOW_SLIDER);
}
@Override
@@ -102,31 +109,81 @@
}
}
- void bindImportance(final StatusBarNotification sbn, final ExpandableNotificationRow row,
- final int importance) {
+ void bindImportance(final PackageManager pm, final StatusBarNotification sbn,
+ final ExpandableNotificationRow row, final int importance) {
mStartingImportance = importance;
mINotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-
- final TextView importanceSummary = ((TextView) row.findViewById(R.id.summary));
- final TextView importanceTitle = ((TextView) row.findViewById(R.id.title));
- mSeekBar = (SeekBar) row.findViewById(R.id.seekbar);
boolean systemApp = false;
try {
- final PackageManager pm = BaseStatusBar.getPackageManagerForUser(
- getContext(), sbn.getUser().getIdentifier());
final PackageInfo info =
pm.getPackageInfo(sbn.getPackageName(), PackageManager.GET_SIGNATURES);
systemApp = Utils.isSystemPackage(pm, info);
} catch (PackageManager.NameNotFoundException e) {
// unlikely.
}
+
+ final View importanceSlider = row.findViewById(R.id.importance_slider);
+ final View importanceButtons = row.findViewById(R.id.importance_buttons);
+ if (mShowSlider) {
+ bindSlider(importanceSlider, sbn, systemApp);
+ importanceSlider.setVisibility(View.VISIBLE);
+ importanceButtons.setVisibility(View.GONE);
+ } else {
+ bindToggles(importanceButtons, sbn, systemApp);
+ importanceButtons.setVisibility(View.VISIBLE);
+ importanceSlider.setVisibility(View.GONE);
+ }
+ }
+
+ void saveImportance(final StatusBarNotification sbn) {
+ int progress;
+ if (mSeekBar!= null && mSeekBar.isShown()) {
+ progress = mSeekBar.getProgress();
+ } else {
+ if (mBlock.isChecked()) {
+ progress = NotificationListenerService.Ranking.IMPORTANCE_NONE;
+ } else if (mSilent.isChecked()) {
+ progress = NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
+ } else {
+ progress = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ }
+ }
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
+ progress - mStartingImportance);
+ try {
+ mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(), progress);
+ } catch (RemoteException e) {
+ // :(
+ }
+ }
+
+ private void bindToggles(final View importanceButtons, final StatusBarNotification sbn,
+ final boolean systemApp) {
+ mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance);
+ mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance);
+ mReset = (RadioButton) importanceButtons.findViewById(R.id.reset_importance);
if (systemApp) {
- ((ImageView) row.findViewById(R.id.low_importance)).getDrawable().setTint(
+ mBlock.setVisibility(View.GONE);
+ mReset.setText(mContext.getString(R.string.do_not_silence));
+ } else {
+ mReset.setText(mContext.getString(R.string.do_not_silence_block));
+ }
+ mReset.setChecked(true);
+ }
+
+ private void bindSlider(final View importanceSlider, final StatusBarNotification sbn,
+ final boolean systemApp) {
+ final TextView importanceSummary = ((TextView) importanceSlider.findViewById(R.id.summary));
+ final TextView importanceTitle = ((TextView) importanceSlider.findViewById(R.id.title));
+ mSeekBar = (SeekBar) importanceSlider.findViewById(R.id.seekbar);
+
+ if (systemApp) {
+ ((ImageView) importanceSlider.findViewById(R.id.low_importance)).getDrawable().setTint(
mContext.getColor(R.color.notification_guts_disabled_icon_tint));
}
final int minProgress = systemApp ?
- NotificationListenerService.Ranking.IMPORTANCE_LOW
+ NotificationListenerService.Ranking.IMPORTANCE_MIN
: NotificationListenerService.Ranking.IMPORTANCE_NONE;
mSeekBar.setMax(NotificationListenerService.Ranking.IMPORTANCE_MAX);
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@@ -159,6 +216,11 @@
R.string.notification_importance_blocked));
importanceTitle.setText(mContext.getString(R.string.blocked_importance));
break;
+ case NotificationListenerService.Ranking.IMPORTANCE_MIN:
+ importanceSummary.setText(mContext.getString(
+ R.string.notification_importance_min));
+ importanceTitle.setText(mContext.getString(R.string.min_importance));
+ break;
case NotificationListenerService.Ranking.IMPORTANCE_LOW:
importanceSummary.setText(mContext.getString(
R.string.notification_importance_low));
@@ -182,18 +244,7 @@
}
}
});
- mSeekBar.setProgress(importance);
- }
-
- void saveImportance(final StatusBarNotification sbn) {
- int progress = mSeekBar.getProgress();
- MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
- progress - mStartingImportance);
- try {
- mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(), progress);
- } catch (RemoteException e) {
- // :(
- }
+ mSeekBar.setProgress(mStartingImportance);
}
public void setActualHeight(int actualHeight) {
@@ -224,4 +275,11 @@
public boolean areGutsExposed() {
return mExposed;
}
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (SHOW_SLIDER.equals(key)) {
+ mShowSlider = newValue != null && Integer.parseInt(newValue) != 0;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java
index 38b6497..009eed7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java
@@ -30,6 +30,7 @@
/**
* Get the current state of a view in a transform animation
+ *
* @param fadingView which view we are interested in
* @return the current transform state of this viewtype
*/
@@ -37,18 +38,37 @@
/**
* Transform to the given view
+ *
* @param notification the view to transform to
*/
void transformTo(TransformableView notification, Runnable endRunnable);
/**
+ * Transform to the given view by a specified amount.
+ *
+ * @param notification the view to transform to
+ * @param transformationAmount how much transformation should be done
+ */
+ void transformTo(TransformableView notification, float transformationAmount);
+
+ /**
* Transform to this view from the given view
+ *
* @param notification the view to transform from
*/
void transformFrom(TransformableView notification);
/**
+ * Transform to this view from the given view by a specified amount.
+ *
+ * @param notification the view to transform from
+ * @param transformationAmount how much transformation should be done
+ */
+ void transformFrom(TransformableView notification, float transformationAmount);
+
+ /**
* Set this view to be fully visible or gone
+ *
* @param visible
*/
void setVisible(boolean visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 63ff5aa..bf05d1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -16,13 +16,17 @@
package com.android.systemui.statusbar;
-import android.os.Handler;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.util.ArrayMap;
import android.view.View;
import android.view.ViewGroup;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.TransformState;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.util.Stack;
@@ -33,9 +37,9 @@
private static final int TAG_CONTAINS_TRANSFORMED_VIEW = R.id.contains_transformed_view;
- private final Handler mHandler = new Handler();
private ArrayMap<Integer, View> mTransformedViews = new ArrayMap<>();
private ArrayMap<Integer, CustomTransformation> mCustomTransformations = new ArrayMap<>();
+ private ValueAnimator mViewTransformationAnimation;
public void addTransformedView(int key, View transformedView) {
mTransformedViews.put(key, transformedView);
@@ -59,61 +63,123 @@
}
@Override
- public void transformTo(TransformableView notification, Runnable endRunnable) {
- Runnable runnable = endRunnable;
+ public void transformTo(final TransformableView notification, final Runnable endRunnable) {
+ if (mViewTransformationAnimation != null) {
+ mViewTransformationAnimation.cancel();
+ }
+ mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ transformTo(notification, animation.getAnimatedFraction());
+ }
+ });
+ mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
+ mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ if (endRunnable != null) {
+ mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
+ public boolean mCancelled;
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endRunnable.run();
+ if (!mCancelled) {
+ setVisible(false);
+ } else {
+ abortTransformations();
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+ });
+ }
+ mViewTransformationAnimation.start();
+ }
+
+ @Override
+ public void transformTo(TransformableView notification, float transformationAmount) {
for (Integer viewType : mTransformedViews.keySet()) {
TransformState ownState = getCurrentState(viewType);
if (ownState != null) {
CustomTransformation customTransformation = mCustomTransformations.get(viewType);
if (customTransformation != null && customTransformation.transformTo(
- ownState, notification, runnable)) {
+ ownState, notification, transformationAmount)) {
ownState.recycle();
- runnable = null;
continue;
}
TransformState otherState = notification.getCurrentState(viewType);
if (otherState != null) {
- boolean run = ownState.transformViewTo(otherState, runnable);
+ ownState.transformViewTo(otherState, transformationAmount);
otherState.recycle();
- if (run) {
- runnable = null;
- }
} else {
// there's no other view available
- CrossFadeHelper.fadeOut(mTransformedViews.get(viewType), runnable);
- runnable = null;
+ CrossFadeHelper.fadeOut(mTransformedViews.get(viewType), transformationAmount);
}
ownState.recycle();
}
}
- if (runnable != null) {
- // We need to post, since the visible type is only set after the transformation is
- // started
- mHandler.post(runnable);
- }
}
@Override
- public void transformFrom(TransformableView notification) {
+ public void transformFrom(final TransformableView notification) {
+ if (mViewTransformationAnimation != null) {
+ mViewTransformationAnimation.cancel();
+ }
+ mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ transformFrom(notification, animation.getAnimatedFraction());
+ }
+ });
+ mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
+ public boolean mCancelled;
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ setVisible(true);
+ } else {
+ abortTransformations();
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+ });
+ mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
+ mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mViewTransformationAnimation.start();
+ }
+
+ @Override
+ public void transformFrom(TransformableView notification, float transformationAmount) {
for (Integer viewType : mTransformedViews.keySet()) {
TransformState ownState = getCurrentState(viewType);
if (ownState != null) {
CustomTransformation customTransformation = mCustomTransformations.get(viewType);
if (customTransformation != null && customTransformation.transformFrom(
- ownState, notification)) {
+ ownState, notification, transformationAmount)) {
ownState.recycle();
continue;
}
TransformState otherState = notification.getCurrentState(viewType);
if (otherState != null) {
- ownState.transformViewFrom(otherState);
+ ownState.transformViewFrom(otherState, transformationAmount);
otherState.recycle();
} else {
// There's no other view, lets fade us in
// Certain views need to prepare the fade in and make sure its children are
// completely visible. An example is the notification header.
- ownState.prepareFadeIn();
- CrossFadeHelper.fadeIn(mTransformedViews.get(viewType));
+ if (transformationAmount == 0.0f) {
+ ownState.prepareFadeIn();
+ }
+ CrossFadeHelper.fadeIn(mTransformedViews.get(viewType), transformationAmount);
}
ownState.recycle();
}
@@ -131,6 +197,16 @@
}
}
+ private void abortTransformations() {
+ for (Integer viewType : mTransformedViews.keySet()) {
+ TransformState ownState = getCurrentState(viewType);
+ if (ownState != null) {
+ ownState.abortTransformation();
+ ownState.recycle();
+ }
+ }
+ }
+
/**
* Add the remaining transformation views such that all views are being transformed correctly
* @param viewRoot the root below which all elements need to be transformed
@@ -173,22 +249,44 @@
}
}
- public interface CustomTransformation {
+ public static abstract class CustomTransformation {
/**
* Transform a state to the given view
* @param ownState the state to transform
* @param notification the view to transform to
+ * @param transformationAmount how much transformation should be done
* @return whether a custom transformation is performed
*/
- boolean transformTo(TransformState ownState, TransformableView notification,
- Runnable endRunnable);
+ public abstract boolean transformTo(TransformState ownState,
+ TransformableView notification,
+ float transformationAmount);
/**
* Transform to this state from the given view
* @param ownState the state to transform to
* @param notification the view to transform from
+ * @param transformationAmount how much transformation should be done
* @return whether a custom transformation is performed
*/
- boolean transformFrom(TransformState ownState, TransformableView notification);
+ public abstract boolean transformFrom(TransformState ownState,
+ TransformableView notification,
+ float transformationAmount);
+
+ /**
+ * Perform a custom initialisation before transforming.
+ *
+ * @param ownState our own state
+ * @param otherState the other state
+ * @return whether a custom initialization is done
+ */
+ public boolean initTransformation(TransformState ownState,
+ TransformState otherState) {
+ return false;
+ }
+
+ public boolean customTransformTarget(TransformState ownState,
+ TransformState otherState) {
+ return false;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
index 81483c6..b66e9f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
@@ -46,7 +46,7 @@
}
@Override
- public boolean transformViewTo(TransformState otherState, Runnable endRunnable) {
+ public boolean transformViewTo(TransformState otherState, float transformationAmount) {
// if the transforming notification has a header, we have ensured that it looks the same
// but the expand button, so lets fade just that one and transform the work profile icon.
if (!(mTransformedView instanceof NotificationHeaderView)) {
@@ -62,14 +62,14 @@
if (headerChild != mExpandButton) {
headerChild.setVisibility(View.INVISIBLE);
} else {
- CrossFadeHelper.fadeOut(mExpandButton, endRunnable);
+ CrossFadeHelper.fadeOut(mExpandButton, transformationAmount);
}
}
return true;
}
@Override
- public void transformViewFrom(TransformState otherState) {
+ public void transformViewFrom(TransformState otherState, float transformationAmount) {
// if the transforming notification has a header, we have ensured that it looks the same
// but the expand button, so lets fade just that one and transform the work profile icon.
if (!(mTransformedView instanceof NotificationHeaderView)) {
@@ -85,12 +85,13 @@
continue;
}
if (headerChild == mExpandButton) {
- CrossFadeHelper.fadeIn(mExpandButton);
+ CrossFadeHelper.fadeIn(mExpandButton, transformationAmount);
} else {
headerChild.setVisibility(View.VISIBLE);
if (headerChild == mWorkProfileIcon) {
- mWorkProfileState.animateViewFrom(
- ((HeaderTransformState) otherState).mWorkProfileState);
+ mWorkProfileState.transformViewFullyFrom(
+ ((HeaderTransformState) otherState).mWorkProfileState,
+ transformationAmount);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
index 81144d5..c80cad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
@@ -71,13 +71,13 @@
new ViewTransformationHelper.CustomTransformation() {
@Override
public boolean transformTo(TransformState ownState, TransformableView notification,
- Runnable endRunnable) {
+ float transformationAmount) {
// We want to transform to the same y location as the title
TransformState otherState = notification.getCurrentState(
TRANSFORMING_VIEW_TITLE);
- CrossFadeHelper.fadeOut(mTextView, endRunnable);
+ CrossFadeHelper.fadeOut(mTextView, transformationAmount);
if (otherState != null) {
- ownState.animateViewVerticalTo(otherState, endRunnable);
+ ownState.transformViewVerticalTo(otherState, transformationAmount);
otherState.recycle();
}
return true;
@@ -85,13 +85,13 @@
@Override
public boolean transformFrom(TransformState ownState,
- TransformableView notification) {
+ TransformableView notification, float transformationAmount) {
// We want to transform from the same y location as the title
TransformState otherState = notification.getCurrentState(
TRANSFORMING_VIEW_TITLE);
- CrossFadeHelper.fadeIn(mTextView);
+ CrossFadeHelper.fadeIn(mTextView, transformationAmount);
if (otherState != null) {
- ownState.animateViewVerticalFrom(otherState);
+ ownState.transformViewVerticalFrom(otherState, transformationAmount);
otherState.recycle();
}
return true;
@@ -133,11 +133,21 @@
}
@Override
+ public void transformTo(TransformableView notification, float transformationAmount) {
+ mTransformationHelper.transformTo(notification, transformationAmount);
+ }
+
+ @Override
public void transformFrom(TransformableView notification) {
mTransformationHelper.transformFrom(notification);
}
@Override
+ public void transformFrom(TransformableView notification, float transformationAmount) {
+ mTransformationHelper.transformFrom(notification, transformationAmount);
+ }
+
+ @Override
public void setVisible(boolean visible) {
setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
mTransformationHelper.setVisible(visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index e891a97..45027c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
@@ -62,7 +62,7 @@
}
@Override
- protected boolean animateScale() {
+ protected boolean transformScale() {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 5a71caf..000f957 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -142,7 +142,8 @@
protected void updateTransformedTypes() {
mTransformationHelper.reset();
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_HEADER, mNotificationHeader);
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_HEADER,
+ mNotificationHeader);
}
@Override
@@ -299,11 +300,21 @@
}
@Override
+ public void transformTo(TransformableView notification, float transformationAmount) {
+ mTransformationHelper.transformTo(notification, transformationAmount);
+ }
+
+ @Override
public void transformFrom(TransformableView notification) {
mTransformationHelper.transformFrom(notification);
}
@Override
+ public void transformFrom(TransformableView notification, float transformationAmount) {
+ mTransformationHelper.transformFrom(notification, transformationAmount);
+ }
+
+ @Override
public void setVisible(boolean visible) {
super.setVisible(visible);
mTransformationHelper.setVisible(visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 0c21f0b..fd4eca8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -49,76 +49,65 @@
new ViewTransformationHelper.CustomTransformation() {
@Override
public boolean transformTo(TransformState ownState,
- TransformableView notification, final Runnable endRunnable) {
+ TransformableView notification, final float transformationAmount) {
if (!(notification instanceof HybridNotificationView)) {
return false;
}
TransformState otherState = notification.getCurrentState(
TRANSFORMING_VIEW_TITLE);
final View text = ownState.getTransformedView();
- CrossFadeHelper.fadeOut(text, endRunnable);
+ CrossFadeHelper.fadeOut(text, transformationAmount);
if (otherState != null) {
- int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
- int[] ownPosition = ownState.getLaidOutLocationOnScreen();
- text.animate()
- .translationY((otherStablePosition[1]
- + otherState.getTransformedView().getHeight()
- - ownPosition[1]) * 0.33f)
- .setDuration(
- StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- if (endRunnable != null) {
- endRunnable.run();
- }
- TransformState.setClippingDeactivated(text,
- false);
- }
- });
- TransformState.setClippingDeactivated(text, true);
+ ownState.transformViewVerticalTo(otherState, this,
+ transformationAmount);
otherState.recycle();
}
return true;
}
@Override
+ public boolean customTransformTarget(TransformState ownState,
+ TransformState otherState) {
+ float endY = getTransformationY(ownState, otherState);
+ ownState.setTransformationEndY(endY);
+ return true;
+ }
+
+ @Override
public boolean transformFrom(TransformState ownState,
- TransformableView notification) {
+ TransformableView notification, float transformationAmount) {
if (!(notification instanceof HybridNotificationView)) {
return false;
}
TransformState otherState = notification.getCurrentState(
TRANSFORMING_VIEW_TITLE);
final View text = ownState.getTransformedView();
- boolean isVisible = text.getVisibility() == View.VISIBLE;
- CrossFadeHelper.fadeIn(text);
+ CrossFadeHelper.fadeIn(text, transformationAmount);
if (otherState != null) {
- int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
- int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
- if (!isVisible) {
- text.setTranslationY((otherStablePosition[1]
- + otherState.getTransformedView().getHeight()
- - ownStablePosition[1]) * 0.33f);
- }
- text.animate()
- .translationY(0)
- .setDuration(
- StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- TransformState.setClippingDeactivated(text,
- false);
- }
- });
- TransformState.setClippingDeactivated(text, true);
+ ownState.transformViewVerticalFrom(otherState, this,
+ transformationAmount);
otherState.recycle();
}
return true;
}
+
+ @Override
+ public boolean initTransformation(TransformState ownState,
+ TransformState otherState) {
+ float startY = getTransformationY(ownState, otherState);
+ ownState.setTransformationStartY(startY);
+ return true;
+ }
+
+ private float getTransformationY(TransformState ownState,
+ TransformState otherState) {
+ int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
+ int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
+ return (otherStablePosition[1]
+ + otherState.getTransformedView().getHeight()
+ - ownStablePosition[1]) * 0.33f;
+ }
+
}, TRANSFORMING_VIEW_TEXT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 7089b78..4738657 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -34,4 +34,8 @@
v.setTag(R.id.icon_is_grayscale, grayscale);
return grayscale;
}
+
+ public static float interpolate(float start, float end, float amount) {
+ return start * (1.0f - amount) + end * amount;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 328f8b5..d3503e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -98,12 +98,22 @@
}
@Override
+ public void transformTo(TransformableView notification, float transformationAmount) {
+ CrossFadeHelper.fadeOut(mView, transformationAmount);
+ }
+
+ @Override
public void transformFrom(TransformableView notification) {
// By default we are fading in completely
CrossFadeHelper.fadeIn(mView);
}
@Override
+ public void transformFrom(TransformableView notification, float transformationAmount) {
+ CrossFadeHelper.fadeIn(mView, transformationAmount);
+ }
+
+ @Override
public void setVisible(boolean visible) {
mView.animate().cancel();
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 67d31be..f04fe5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -30,23 +30,30 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.ViewTransformationHelper;
/**
* A transform state of a view.
*/
public class TransformState {
- private static final int ANIMATE_X = 0x1;
- private static final int ANIMATE_Y = 0x10;
- private static final int ANIMATE_ALL = ANIMATE_X | ANIMATE_Y;
+ private static final float UNDEFINED = -1f;
+ private static final int TRANSOFORM_X = 0x1;
+ private static final int TRANSOFORM_Y = 0x10;
+ private static final int TRANSOFORM_ALL = TRANSOFORM_X | TRANSOFORM_Y;
private static final int CLIP_CLIPPING_SET = R.id.clip_children_set_tag;
private static final int CLIP_CHILDREN_TAG = R.id.clip_children_tag;
private static final int CLIP_TO_PADDING = R.id.clip_to_padding_tag;
+ private static final int TRANSFORMATION_START_X = R.id.transformation_start_x_tag;
+ private static final int TRANSFORMATION_START_Y = R.id.transformation_start_y_tag;
+ private static final int TRANSFORMATION_START_SCLALE_X = R.id.transformation_start_scale_x_tag;
+ private static final int TRANSFORMATION_START_SCLALE_Y = R.id.transformation_start_scale_y_tag;
private static Pools.SimplePool<TransformState> sInstancePool = new Pools.SimplePool<>(40);
protected View mTransformedView;
private int[] mOwnPosition = new int[2];
+ private float mTransformationEndY = UNDEFINED;
+ private float mTransformationEndX = UNDEFINED;
public void initFrom(View view) {
mTransformedView = view;
@@ -55,129 +62,233 @@
/**
* Transforms the {@link #mTransformedView} from the given transformviewstate
* @param otherState the state to transform from
+ * @param transformationAmount how much to transform
*/
- public void transformViewFrom(TransformState otherState) {
+ public void transformViewFrom(TransformState otherState, float transformationAmount) {
mTransformedView.animate().cancel();
if (sameAs(otherState)) {
- // We have the same content, lets show ourselves
- mTransformedView.setAlpha(1.0f);
- mTransformedView.setVisibility(View.VISIBLE);
+ if (mTransformedView.getVisibility() == View.INVISIBLE) {
+ // We have the same content, lets show ourselves
+ mTransformedView.setAlpha(1.0f);
+ mTransformedView.setVisibility(View.VISIBLE);
+ }
} else {
- CrossFadeHelper.fadeIn(mTransformedView);
+ CrossFadeHelper.fadeIn(mTransformedView, transformationAmount);
}
- animateViewFrom(otherState);
+ transformViewFullyFrom(otherState, transformationAmount);
}
- public void animateViewFrom(TransformState otherState) {
- animateViewFrom(otherState, ANIMATE_ALL);
+ public void transformViewFullyFrom(TransformState otherState, float transformationAmount) {
+ transformViewFrom(otherState, TRANSOFORM_ALL, null, transformationAmount);
}
- public void animateViewVerticalFrom(TransformState otherState) {
- animateViewFrom(otherState, ANIMATE_Y);
+ public void transformViewVerticalFrom(TransformState otherState,
+ ViewTransformationHelper.CustomTransformation customTransformation,
+ float transformationAmount) {
+ transformViewFrom(otherState, TRANSOFORM_Y, customTransformation, transformationAmount);
}
- private void animateViewFrom(TransformState otherState, int animationFlags) {
+ public void transformViewVerticalFrom(TransformState otherState, float transformationAmount) {
+ transformViewFrom(otherState, TRANSOFORM_Y, null, transformationAmount);
+ }
+
+ private void transformViewFrom(TransformState otherState, int transformationFlags,
+ ViewTransformationHelper.CustomTransformation customTransformation,
+ float transformationAmount) {
final View transformedView = mTransformedView;
+ boolean transformX = (transformationFlags & TRANSOFORM_X) != 0;
+ boolean transformY = (transformationFlags & TRANSOFORM_Y) != 0;
+ boolean transformScale = transformScale();
// lets animate the positions correctly
- int[] otherPosition = otherState.getLocationOnScreen();
- int[] ownStablePosition = getLaidOutLocationOnScreen();
- if ((animationFlags & ANIMATE_X) != 0) {
- transformedView.setTranslationX(otherPosition[0] - ownStablePosition[0]);
- transformedView.animate().translationX(0);
- }
- if ((animationFlags & ANIMATE_Y) != 0) {
- transformedView.setTranslationY(otherPosition[1] - ownStablePosition[1]);
- transformedView.animate().translationY(0);
- }
- if (animateScale()) {
- // we also want to animate the scale if we're the same
- View otherView = otherState.getTransformedView();
- if (otherView.getWidth() != transformedView.getWidth()) {
- float scaleX = (otherView.getWidth() * otherView.getScaleX()
- / (float) transformedView.getWidth());
- transformedView.setScaleX(scaleX);
- transformedView.setPivotX(0);
- transformedView.animate().scaleX(1.0f);
+ if (transformationAmount == 0.0f) {
+ int[] otherPosition = otherState.getLocationOnScreen();
+ int[] ownStablePosition = getLaidOutLocationOnScreen();
+ if (customTransformation == null
+ || !customTransformation.initTransformation(this, otherState)) {
+ if (transformX) {
+ setTransformationStartX(otherPosition[0] - ownStablePosition[0]);
+ }
+ if (transformY) {
+ setTransformationStartY(otherPosition[1] - ownStablePosition[1]);
+ }
+ // we also want to animate the scale if we're the same
+ View otherView = otherState.getTransformedView();
+ if (transformScale && otherView.getWidth() != transformedView.getWidth()) {
+ setTransformationStartScaleX(otherView.getWidth() * otherView.getScaleX()
+ / (float) transformedView.getWidth());
+ transformedView.setPivotX(0);
+ } else {
+ setTransformationStartScaleX(UNDEFINED);
+ }
+ if (transformScale && otherView.getHeight() != transformedView.getHeight()) {
+ setTransformationStartScaleY(otherView.getHeight() * otherView.getScaleY()
+ / (float) transformedView.getHeight());
+ transformedView.setPivotY(0);
+ } else {
+ setTransformationStartScaleY(UNDEFINED);
+ }
}
- if (otherView.getHeight() != transformedView.getHeight()) {
- float scaleY = (otherView.getHeight() * otherView.getScaleY()
- / (float) transformedView.getHeight());
- transformedView.setScaleY(scaleY);
- transformedView.setPivotY(0);
- transformedView.animate().scaleY(1.0f);
+ if (!transformX) {
+ setTransformationStartX(UNDEFINED);
+ }
+ if (!transformY) {
+ setTransformationStartY(UNDEFINED);
+ }
+ if (!transformScale) {
+ setTransformationStartScaleX(UNDEFINED);
+ setTransformationStartScaleY(UNDEFINED);
+ }
+ setClippingDeactivated(transformedView, true);
+ }
+ float interpolatedValue = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ transformationAmount);
+ if (transformX) {
+ transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
+ 0.0f,
+ interpolatedValue));
+ }
+ if (transformY) {
+ transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
+ 0.0f,
+ interpolatedValue));
+ }
+ if (transformScale) {
+ float transformationStartScaleX = getTransformationStartScaleX();
+ if (transformationStartScaleX != UNDEFINED) {
+ transformedView.setScaleX(
+ NotificationUtils.interpolate(transformationStartScaleX,
+ 1.0f,
+ interpolatedValue));
+ }
+ float transformationStartScaleY = getTransformationStartScaleY();
+ if (transformationStartScaleY != UNDEFINED) {
+ transformedView.setScaleY(
+ NotificationUtils.interpolate(transformationStartScaleY,
+ 1.0f,
+ interpolatedValue));
}
}
- transformedView.animate()
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- setClippingDeactivated(transformedView, false);
- }
- });
- setClippingDeactivated(transformedView, true);
}
- protected boolean animateScale() {
+ protected boolean transformScale() {
return false;
}
/**
* Transforms the {@link #mTransformedView} to the given transformviewstate
* @param otherState the state to transform from
- * @param endRunnable a runnable to run at the end of the animation
+ * @param transformationAmount how much to transform
* @return whether an animation was started
*/
- public boolean transformViewTo(TransformState otherState, final Runnable endRunnable) {
+ public boolean transformViewTo(TransformState otherState, float transformationAmount) {
mTransformedView.animate().cancel();
if (sameAs(otherState)) {
// We have the same text, lets show ourselfs
- mTransformedView.setAlpha(0.0f);
- mTransformedView.setVisibility(View.INVISIBLE);
+ if (mTransformedView.getVisibility() == View.VISIBLE) {
+ mTransformedView.setAlpha(0.0f);
+ mTransformedView.setVisibility(View.INVISIBLE);
+ }
return false;
} else {
- CrossFadeHelper.fadeOut(mTransformedView, endRunnable);
+ CrossFadeHelper.fadeOut(mTransformedView, transformationAmount);
}
- animateViewTo(otherState, endRunnable);
+ transformViewFullyTo(otherState, transformationAmount);
return true;
}
- public void animateViewTo(TransformState otherState, Runnable endRunnable) {
- animateViewTo(otherState, endRunnable, ANIMATE_ALL);
+ public void transformViewFullyTo(TransformState otherState, float transformationAmount) {
+ transformViewTo(otherState, TRANSOFORM_ALL, null, transformationAmount);
}
- public void animateViewVerticalTo(TransformState otherState, Runnable endRunnable) {
- animateViewTo(otherState, endRunnable, ANIMATE_Y);
+ public void transformViewVerticalTo(TransformState otherState,
+ ViewTransformationHelper.CustomTransformation customTransformation,
+ float transformationAmount) {
+ transformViewTo(otherState, TRANSOFORM_Y, customTransformation, transformationAmount);
}
- private void animateViewTo(TransformState otherState, final Runnable endRunnable,
- int animationFlags) {
+ public void transformViewVerticalTo(TransformState otherState, float transformationAmount) {
+ transformViewTo(otherState, TRANSOFORM_Y, null, transformationAmount);
+ }
+
+ private void transformViewTo(TransformState otherState, int transformationFlags,
+ ViewTransformationHelper.CustomTransformation customTransformation,
+ float transformationAmount) {
// lets animate the positions correctly
+
+ final View transformedView = mTransformedView;
+ boolean transformX = (transformationFlags & TRANSOFORM_X) != 0;
+ boolean transformY = (transformationFlags & TRANSOFORM_Y) != 0;
+ boolean transformScale = transformScale();
+ // lets animate the positions correctly
+ if (transformationAmount == 0.0f) {
+ if (transformX) {
+ float transformationStartX = getTransformationStartX();
+ float start = transformationStartX != UNDEFINED ? transformationStartX
+ : transformedView.getTranslationX();
+ setTransformationStartX(start);
+ }
+ if (transformY) {
+ float transformationStartY = getTransformationStartY();
+ float start = transformationStartY != UNDEFINED ? transformationStartY
+ : transformedView.getTranslationY();
+ setTransformationStartY(start);
+ }
+ View otherView = otherState.getTransformedView();
+ if (transformScale && otherView.getWidth() != transformedView.getWidth()) {
+ setTransformationStartScaleX(transformedView.getScaleX());
+ transformedView.setPivotX(0);
+ } else {
+ setTransformationStartScaleX(UNDEFINED);
+ }
+ if (transformScale && otherView.getHeight() != transformedView.getHeight()) {
+ setTransformationStartScaleY(transformedView.getScaleY());
+ transformedView.setPivotY(0);
+ } else {
+ setTransformationStartScaleY(UNDEFINED);
+ }
+ setClippingDeactivated(transformedView, true);
+ }
+ float interpolatedValue = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ transformationAmount);
int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
int[] ownPosition = getLaidOutLocationOnScreen();
- final View transformedView = mTransformedView;
- if ((animationFlags & ANIMATE_X) != 0) {
- transformedView.animate()
- .translationX(otherStablePosition[0] - ownPosition[0]);
+ if (transformX) {
+ float endX = otherStablePosition[0] - ownPosition[0];
+ if (customTransformation != null
+ && customTransformation.customTransformTarget(this, otherState)) {
+ endX = mTransformationEndX;
+ }
+ transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
+ endX,
+ interpolatedValue));
}
- if ((animationFlags & ANIMATE_Y) != 0) {
- transformedView.animate()
- .translationY(otherStablePosition[1] - ownPosition[1]);
+ if (transformY) {
+ float endY = otherStablePosition[1] - ownPosition[1];
+ if (customTransformation != null
+ && customTransformation.customTransformTarget(this, otherState)) {
+ endY = mTransformationEndY;
+ }
+ transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
+ endY,
+ interpolatedValue));
}
- transformedView.animate()
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- if (endRunnable != null) {
- endRunnable.run();
- }
- setClippingDeactivated(transformedView, false);
- }
- });
- setClippingDeactivated(transformedView, true);
+ if (transformScale) {
+ View otherView = otherState.getTransformedView();
+ float transformationStartScaleX = getTransformationStartScaleX();
+ if (transformationStartScaleX != UNDEFINED) {
+ transformedView.setScaleX(
+ NotificationUtils.interpolate(transformationStartScaleX,
+ (otherView.getWidth() / (float) transformedView.getWidth()),
+ interpolatedValue));
+ }
+ float transformationStartScaleY = getTransformationStartScaleY();
+ if (transformationStartScaleY != UNDEFINED) {
+ transformedView.setScaleY(
+ NotificationUtils.interpolate(transformationStartScaleY,
+ (otherView.getHeight() / (float) transformedView.getHeight()),
+ interpolatedValue));
+ }
+ }
}
public static void setClippingDeactivated(final View transformedView, boolean deactivated) {
@@ -281,8 +392,54 @@
}
}
+ public void setTransformationEndY(float transformationEndY) {
+ mTransformationEndY = transformationEndY;
+ }
+
+ public void setTransformationEndX(float transformationEndX) {
+ mTransformationEndX = transformationEndX;
+ }
+
+ public float getTransformationStartX() {
+ Object tag = mTransformedView.getTag(TRANSFORMATION_START_X);
+ return tag == null ? UNDEFINED : (float) tag;
+ }
+
+ public float getTransformationStartY() {
+ Object tag = mTransformedView.getTag(TRANSFORMATION_START_Y);
+ return tag == null ? UNDEFINED : (float) tag;
+ }
+
+ public float getTransformationStartScaleX() {
+ Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_X);
+ return tag == null ? UNDEFINED : (float) tag;
+ }
+
+ public float getTransformationStartScaleY() {
+ Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_Y);
+ return tag == null ? UNDEFINED : (float) tag;
+ }
+
+ public void setTransformationStartX(float transformationStartX) {
+ mTransformedView.setTag(TRANSFORMATION_START_X, transformationStartX);
+ }
+
+ public void setTransformationStartY(float transformationStartY) {
+ mTransformedView.setTag(TRANSFORMATION_START_Y, transformationStartY);
+ }
+
+ private void setTransformationStartScaleX(float startScaleX) {
+ mTransformedView.setTag(TRANSFORMATION_START_SCLALE_X, startScaleX);
+ }
+
+ private void setTransformationStartScaleY(float startScaleY) {
+ mTransformedView.setTag(TRANSFORMATION_START_SCLALE_Y, startScaleY);
+ }
+
protected void reset() {
mTransformedView = null;
+ mTransformationEndX = UNDEFINED;
+ mTransformationEndY = UNDEFINED;
}
public void setVisible(boolean visible) {
@@ -306,6 +463,15 @@
mTransformedView.setTranslationY(0);
mTransformedView.setScaleX(1.0f);
mTransformedView.setScaleY(1.0f);
+ setClippingDeactivated(mTransformedView, false);
+ abortTransformation();
+ }
+
+ public void abortTransformation() {
+ mTransformedView.setTag(TRANSFORMATION_START_X, UNDEFINED);
+ mTransformedView.setTag(TRANSFORMATION_START_Y, UNDEFINED);
+ mTransformedView.setTag(TRANSFORMATION_START_SCLALE_X, UNDEFINED);
+ mTransformedView.setTag(TRANSFORMATION_START_SCLALE_Y, UNDEFINED);
}
public static TransformState obtain() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index b742479..6d0fbb15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
+import com.android.systemui.statusbar.policy.NightModeController;
/**
* Manages which tiles should be automatically added to QS.
@@ -66,12 +67,33 @@
if (!Prefs.getBoolean(context, Key.QS_WORK_ADDED, false)) {
host.getManagedProfileController().addCallback(mProfileCallback);
}
+ if (!Prefs.getBoolean(context, Key.QS_NIGHT_ADDED, false)) {
+ host.getNightModeController().addListener(mNightModeListener);
+ }
}
public void destroy() {
// TODO: Remove any registered listeners.
}
+ private final NightModeController.Listener mNightModeListener =
+ new NightModeController.Listener() {
+ @Override
+ public void onNightModeChanged() {
+ mHost.addTile("night");
+ Prefs.putBoolean(mContext, Key.QS_NIGHT_ADDED, true);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mHost.getNightModeController().removeListener(mNightModeListener);
+ }
+ });
+ }
+
+ @Override
+ public void onTwilightAutoChanged() { }
+ };
+
private final ManagedProfileController.Callback mProfileCallback =
new ManagedProfileController.Callback() {
@Override
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 d25e99b..9b87a8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1144,31 +1144,42 @@
@Override
public boolean onLongClick(View v) {
- if (mRecents != null) {
- int dockSide = WindowManagerProxy.getInstance().getDockSide();
- if (dockSide == WindowManager.DOCKED_INVALID) {
- Point realSize = new Point();
- mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
- .getRealSize(realSize);
- Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y);
- boolean docked = mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
- ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
- initialBounds);
- if (docked) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS);
- return true;
- }
- } else {
- EventBus.getDefault().send(new UndockingTaskEvent());
- MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
- return true;
- }
-
+ if (mRecents == null) {
+ return false;
+ }
+ boolean initiallyDocked = WindowManagerProxy.getInstance().getDockSide()
+ == WindowManager.DOCKED_INVALID;
+ boolean dockedAtEnd = toggleSplitScreenMode();
+ if (dockedAtEnd != initiallyDocked) {
+ int logAction = dockedAtEnd ? MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS
+ : MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS;
+ MetricsLogger.action(mContext, logAction);
+ return true;
}
return false;
}
};
+ @Override
+ protected boolean toggleSplitScreenMode() {
+ if (mRecents == null) {
+ return false;
+ }
+ int dockSide = WindowManagerProxy.getInstance().getDockSide();
+ if (dockSide == WindowManager.DOCKED_INVALID) {
+ Point realSize = new Point();
+ mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
+ .getRealSize(realSize);
+ Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y);
+ return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
+ ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+ initialBounds);
+ } else {
+ EventBus.getDefault().send(new UndockingTaskEvent());
+ return false;
+ }
+ }
+
private final View.OnLongClickListener mLongPressHomeListener
= new View.OnLongClickListener() {
@Override
@@ -1308,6 +1319,12 @@
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
}
+ } else if (mNotificationData.getImportance(notification.getKey())
+ < NotificationListenerService.Ranking.IMPORTANCE_MAX) {
+ if (DEBUG) {
+ Log.d(TAG, "No Fullscreen intent: not important enough: "
+ + notification.getKey());
+ }
} else {
// Stop screensaver if the notification has a full-screen intent.
// (like an incoming phone call)
@@ -4089,7 +4106,7 @@
ExpandableNotificationRow row = null;
if (expandView instanceof ExpandableNotificationRow) {
row = (ExpandableNotificationRow) expandView;
- row.setUserExpanded(true);
+ row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
}
boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
|| !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
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 3bd68a9..ab34768 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -80,13 +80,13 @@
// Original is slightly larger than the mirror, so make sure to use the center for the
// positioning.
- int originalX = mInt2Cache[0] + original.getWidth()/2;
- int originalY = mInt2Cache[1];
+ int originalX = mInt2Cache[0] + original.getWidth() / 2;
+ int originalY = mInt2Cache[1] + original.getHeight() / 2;
mBrightnessMirror.setTranslationX(0);
mBrightnessMirror.setTranslationY(0);
mBrightnessMirror.getLocationInWindow(mInt2Cache);
- int mirrorX = mInt2Cache[0] + mBrightnessMirror.getWidth()/2;
- int mirrorY = mInt2Cache[1];
+ int mirrorX = mInt2Cache[0] + mBrightnessMirror.getWidth() / 2;
+ int mirrorY = mInt2Cache[1] + mBrightnessMirror.getHeight() / 2;
mBrightnessMirror.setTranslationX(originalX - mirrorX);
mBrightnessMirror.setTranslationY(originalY - mirrorY);
}
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 49aec42..030c8b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
+import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import java.util.ArrayList;
@@ -60,6 +61,8 @@
private ViewState mGroupOverFlowState;
private int mRealHeight;
private int mLayoutDirection = LAYOUT_DIRECTION_UNDEFINED;
+ private boolean mUserLocked;
+ private int mActualHeight;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -281,27 +284,45 @@
*/
private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
int intrinsicHeight = mNotificationHeaderHeight;
- if (mChildrenExpanded) {
- intrinsicHeight += mNotificatonTopPadding;
- }
int visibleChildren = 0;
int childCount = mChildren.size();
+ boolean firstChild = true;
+ float expandFactor = 0;
+ if (mUserLocked) {
+ expandFactor = getChildExpandFraction();
+ }
for (int i = 0; i < childCount; i++) {
if (visibleChildren >= maxAllowedVisibleChildren) {
break;
}
+ if (!firstChild) {
+ if (mUserLocked) {
+ intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
+ expandFactor);
+ } else {
+ intrinsicHeight += mChildrenExpanded ? mDividerHeight : mChildPadding;
+ }
+ } else {
+ if (mUserLocked) {
+ intrinsicHeight += NotificationUtils.interpolate(
+ 0,
+ mNotificatonTopPadding + mDividerHeight,
+ expandFactor);
+ } else {
+ intrinsicHeight += mChildrenExpanded
+ ? mNotificatonTopPadding + mDividerHeight
+ : 0;
+ }
+ firstChild = false;
+ }
ExpandableNotificationRow child = mChildren.get(i);
intrinsicHeight += child.getIntrinsicHeight();
visibleChildren++;
}
- if (visibleChildren > 0) {
- if (mChildrenExpanded) {
- intrinsicHeight += visibleChildren * mDividerHeight;
- } else {
- intrinsicHeight += (visibleChildren - 1) * mChildPadding;
- }
- }
- if (!mChildrenExpanded) {
+ if (mUserLocked) {
+ intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
+ expandFactor);
+ } else if (!mChildrenExpanded) {
intrinsicHeight += mCollapsedBottompadding;
}
return intrinsicHeight;
@@ -323,12 +344,28 @@
int lastVisibleIndex = hasOverflow
? maxAllowedVisibleChildren - 2
: maxAllowedVisibleChildren - 1;
+ float expandFactor = 0;
+ if (mUserLocked) {
+ expandFactor = getChildExpandFraction();
+ }
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
if (!firstChild) {
- yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
+ if (mUserLocked) {
+ yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
+ expandFactor);
+ } else {
+ yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
+ }
} else {
- yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
+ if (mUserLocked) {
+ yPosition += NotificationUtils.interpolate(
+ 0,
+ mNotificatonTopPadding + mDividerHeight,
+ expandFactor);
+ } else {
+ yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
+ }
firstChild = false;
}
StackViewState childState = resultState.getViewStateForView(child);
@@ -375,6 +412,7 @@
public void applyState(StackScrollState state) {
int childCount = mChildren.size();
ViewState tmpState = new ViewState();
+ float expandFraction = getChildExpandFraction();
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
StackViewState viewState = state.getViewStateForView(child);
@@ -384,7 +422,11 @@
View divider = mDividers.get(i);
tmpState.initFrom(divider);
tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
- tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+ float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+ if (mUserLocked && viewState.alpha != 0) {
+ alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
+ }
+ tmpState.alpha = alpha;
state.applyViewState(divider, tmpState);
}
if (mGroupOverflowContainer != null) {
@@ -407,6 +449,7 @@
long baseDelay, long duration) {
int childCount = mChildren.size();
ViewState tmpState = new ViewState();
+ float expandFraction = getChildExpandFraction();
for (int i = childCount - 1; i >= 0; i--) {
ExpandableNotificationRow child = mChildren.get(i);
StackViewState viewState = state.getViewStateForView(child);
@@ -416,7 +459,11 @@
View divider = mDividers.get(i);
tmpState.initFrom(divider);
tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
- tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+ float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+ if (mUserLocked && viewState.alpha != 0) {
+ alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
+ }
+ tmpState.alpha = alpha;
stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration);
}
if (mGroupOverflowContainer != null) {
@@ -449,7 +496,70 @@
}
public int getMaxContentHeight() {
- return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+ int maxContentHeight = mNotificationHeaderHeight + mNotificatonTopPadding;
+ int visibleChildren = 0;
+ int childCount = mChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
+ break;
+ }
+ ExpandableNotificationRow child = mChildren.get(i);
+ float childHeight = child.isExpanded()
+ ? child.getMaxExpandHeight()
+ : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
+ maxContentHeight += childHeight;
+ visibleChildren++;
+ }
+ if (visibleChildren > 0) {
+ maxContentHeight += visibleChildren * mDividerHeight;
+ }
+ return maxContentHeight;
+ }
+
+ public void setActualHeight(int actualHeight) {
+ if (!mUserLocked) {
+ return;
+ }
+ mActualHeight = actualHeight;
+ float fraction = getChildExpandFraction();
+ int childCount = mChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ ExpandableNotificationRow child = mChildren.get(i);
+ float childHeight = child.isExpanded()
+ ? child.getMaxExpandHeight()
+ : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
+ float singleLineHeight = child.getShowingLayout().getMinHeight(
+ false /* likeGroupExpanded */);
+ child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight, childHeight,
+ fraction), false);
+ }
+ }
+
+ public float getChildExpandFraction() {
+ int allChildrenVisibleHeight = getChildrenExpandStartHeight();
+ int maxContentHeight = getMaxContentHeight();
+ float factor = (mActualHeight - allChildrenVisibleHeight)
+ / (float) (maxContentHeight - allChildrenVisibleHeight);
+ return Math.max(0.0f, Math.min(1.0f, factor));
+ }
+
+ private int getChildrenExpandStartHeight() {
+ int intrinsicHeight = mNotificationHeaderHeight;
+ int visibleChildren = 0;
+ int childCount = mChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
+ break;
+ }
+ ExpandableNotificationRow child = mChildren.get(i);
+ intrinsicHeight += child.getMinHeight();
+ visibleChildren++;
+ }
+ if (visibleChildren > 0) {
+ intrinsicHeight += (visibleChildren - 1) * mChildPadding;
+ }
+ intrinsicHeight += mCollapsedBottompadding;
+ return intrinsicHeight;
}
public int getMinHeight() {
@@ -477,4 +587,13 @@
mDividers.set(i, divider);
}
}
+
+ public void setUserLocked(boolean userLocked) {
+ mUserLocked = userLocked;
+ int childCount = mChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ ExpandableNotificationRow child = mChildren.get(i);
+ child.setUserLocked(userLocked);
+ }
+ }
}
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 fe06c3a..340ebb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -64,6 +64,7 @@
import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -538,7 +539,7 @@
ExpandableView child = (ExpandableView) getChildAt(i);
if (mChildrenToAddAnimated.contains(child)) {
int startingPosition = getPositionInLinearLayout(child);
- int padding = child.needsIncreasedPadding()
+ int padding = child.getIncreasedPaddingAmount() == 1.0f
? mIncreasedPaddingBetweenElements :
mPaddingBetweenElements;
int childHeight = getIntrinsicHeight(child) + padding;
@@ -1531,18 +1532,18 @@
private void updateContentHeight() {
int height = 0;
- boolean previousNeedsIncreasedPaddings = false;
+ float previousIncreasedAmount = 0.0f;
for (int i = 0; i < getChildCount(); i++) {
ExpandableView expandableView = (ExpandableView) getChildAt(i);
if (expandableView.getVisibility() != View.GONE) {
- boolean needsIncreasedPaddings = expandableView.needsIncreasedPadding();
+ float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
if (height != 0) {
- int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings
- ? mIncreasedPaddingBetweenElements
- : mPaddingBetweenElements;
- height += padding;
+ height += (int) NotificationUtils.interpolate(
+ mPaddingBetweenElements,
+ mIncreasedPaddingBetweenElements,
+ Math.max(previousIncreasedAmount, increasedPaddingAmount));
}
- previousNeedsIncreasedPaddings = needsIncreasedPaddings;
+ previousIncreasedAmount = increasedPaddingAmount;
height += expandableView.getIntrinsicHeight();
}
}
@@ -1813,8 +1814,12 @@
// it will be set once we reach the boundary
mMaxOverScroll = 0.0f;
}
+ int minScrollY = Math.max(0, scrollRange);
+ if (mExpandedInThisMotion) {
+ minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
+ }
mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0,
- Math.max(0, scrollRange), 0, Integer.MAX_VALUE / 2);
+ minScrollY, 0, mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
postInvalidateOnAnimation();
}
@@ -2097,9 +2102,10 @@
*/
private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
int startingPosition = getPositionInLinearLayout(removedChild);
- int padding = removedChild.needsIncreasedPadding()
- ? mIncreasedPaddingBetweenElements :
- mPaddingBetweenElements;
+ int padding = (int) NotificationUtils.interpolate(
+ mPaddingBetweenElements,
+ mIncreasedPaddingBetweenElements,
+ removedChild.getIncreasedPaddingAmount());
int childHeight = getIntrinsicHeight(removedChild) + padding;
int endPosition = startingPosition + childHeight;
if (endPosition <= mOwnScrollY) {
@@ -2123,19 +2129,19 @@
private int getPositionInLinearLayout(View requestedChild) {
int position = 0;
- boolean previousNeedsIncreasedPaddings = false;
+ float previousIncreasedAmount = 0.0f;
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
boolean notGone = child.getVisibility() != View.GONE;
if (notGone) {
- boolean needsIncreasedPaddings = child.needsIncreasedPadding();
+ float increasedPaddingAmount = child.getIncreasedPaddingAmount();
if (position != 0) {
- int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings
- ? mIncreasedPaddingBetweenElements :
- mPaddingBetweenElements;
- position += padding;
+ position += (int) NotificationUtils.interpolate(
+ mPaddingBetweenElements,
+ mIncreasedPaddingBetweenElements,
+ Math.max(previousIncreasedAmount, increasedPaddingAmount));
}
- previousNeedsIncreasedPaddings = needsIncreasedPaddings;
+ previousIncreasedAmount = increasedPaddingAmount;
}
if (child == requestedChild) {
return position;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index d78d626..eea923f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -25,9 +25,10 @@
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.List;
/**
@@ -297,18 +298,22 @@
int childCount = hostView.getChildCount();
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
- state.increasedPaddingSet.clear();
+ state.increasedPaddingMap.clear();
int notGoneIndex = 0;
ExpandableView lastView = null;
for (int i = 0; i < childCount; i++) {
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
if (v.getVisibility() != View.GONE) {
notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
- boolean needsIncreasedPadding = v.needsIncreasedPadding();
- if (needsIncreasedPadding) {
- state.increasedPaddingSet.add(v);
+ float increasedPadding = v.getIncreasedPaddingAmount();
+ if (increasedPadding != 0.0f) {
+ state.increasedPaddingMap.put(v, increasedPadding);
if (lastView != null) {
- state.increasedPaddingSet.add(lastView);
+ Float prevValue = state.increasedPaddingMap.get(lastView);
+ float newValue = prevValue != null
+ ? Math.max(prevValue, increasedPadding)
+ : increasedPadding;
+ state.increasedPaddingMap.put(lastView, newValue);
}
}
if (v instanceof ExpandableNotificationRow) {
@@ -423,9 +428,12 @@
private int getPaddingAfterChild(StackScrollAlgorithmState algorithmState,
ExpandableView child) {
- return algorithmState.increasedPaddingSet.contains(child)
- ? mIncreasedPaddingBetweenElements
- : mPaddingBetweenElements;
+ Float paddingValue = algorithmState.increasedPaddingMap.get(child);
+ return paddingValue == null
+ ? mPaddingBetweenElements
+ : (int) NotificationUtils.interpolate(mPaddingBetweenElements,
+ mIncreasedPaddingBetweenElements,
+ paddingValue);
}
private void updateHeadsUpStates(StackScrollState resultState,
@@ -765,9 +773,10 @@
public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
/**
- * The children from the host that need an increased padding after them.
+ * The children from the host that need an increased padding after them. A value of 0 means
+ * no increased padding, a value of 1 means full padding.
*/
- public final HashSet<ExpandableView> increasedPaddingSet = new HashSet<>();
+ public final HashMap<ExpandableView, Float> increasedPaddingMap = new HashMap<>();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 110258c..0ed6ef8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -116,6 +116,11 @@
}
@Override
+ protected boolean toggleSplitScreenMode() {
+ return false;
+ }
+
+ @Override
public void maybeEscalateHeadsUp() {
}
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 ec49256..0925638 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -424,9 +424,7 @@
}
Intent intent = new Intent(mContext, PipMenuActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchStackId(PINNED_STACK_ID);
- mContext.startActivity(intent, options.toBundle());
+ mContext.startActivity(intent);
}
public void addListener(Listener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index 7e229d4..a392bec 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -33,33 +33,77 @@
private final PipManager mPipManager = PipManager.getInstance();
private MediaController mMediaController;
+ private View mFullButtonView;
+ private View mFullDescriptionView;
+ private View mPlayPauseButtonView;
+ private View mPlayPauseDescriptionView;
+ private View mCloseButtonView;
+ private View mCloseDescriptionView;
+
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.tv_pip_menu);
mPipManager.addListener(this);
- findViewById(R.id.full).setOnClickListener(new View.OnClickListener() {
+ mFullButtonView = findViewById(R.id.full);
+ mFullDescriptionView = findViewById(R.id.full_desc);
+ mFullButtonView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPipManager.movePipToFullscreen();
+ finish();
}
});
- findViewById(R.id.exit).setOnClickListener(new View.OnClickListener() {
+ mFullButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mFullDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+ }
+ });
+
+ mPlayPauseButtonView = findViewById(R.id.play_pause);
+ mPlayPauseDescriptionView = findViewById(R.id.play_pause_desc);
+ mPlayPauseButtonView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // TODO: Implement play/pause.
+ }
+ });
+ mPlayPauseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPlayPauseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+ }
+ });
+
+ mCloseButtonView = findViewById(R.id.close);
+ mCloseDescriptionView = findViewById(R.id.close_desc);
+ mCloseButtonView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPipManager.closePip();
finish();
}
});
- findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
+ mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
- public void onClick(View v) {
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
- finish();
+ public void onFocusChange(View v, boolean hasFocus) {
+ mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
}
+ private void restorePipAndFinish() {
+ mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
+ finish();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ restorePipAndFinish();
+ }
+
@Override
protected void onDestroy() {
super.onDestroy();
@@ -70,8 +114,7 @@
@Override
public void onBackPressed() {
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
- finish();
+ restorePipAndFinish();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
index 6f71c92..e5c07d2 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
@@ -17,9 +17,11 @@
package com.android.systemui.tv.pip;
import android.app.Activity;
+import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup.LayoutParams;
import com.android.systemui.R;
@@ -33,6 +35,8 @@
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.tv_pip_onboarding);
+ View pipOnboardingView = findViewById(R.id.pip_onboarding);
+ View pipOutlineView = findViewById(R.id.pip_outline);
mPipManager.addListener(this);
findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
@Override
@@ -40,6 +44,20 @@
finish();
}
});
+
+ int pipOutlineSpace = getResources().getDimensionPixelSize(R.dimen.tv_pip_bounds_space);
+ int screenWidth = getResources().getDisplayMetrics().widthPixels;
+ Rect pipBounds = mPipManager.getPipBounds();
+ pipOnboardingView.setPadding(
+ pipBounds.left - pipOutlineSpace,
+ pipBounds.top - pipOutlineSpace,
+ screenWidth - pipBounds.right - pipOutlineSpace, 0);
+
+ // Set width and height for outline view to enclose the PIP.
+ LayoutParams lp = pipOutlineView.getLayoutParams();
+ lp.width = pipBounds.width() + pipOutlineSpace * 2;
+ lp.height = pipBounds.height() + pipOutlineSpace * 2;
+ pipOutlineView.setLayoutParams(lp);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index 6f24691..cfeab6d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -19,8 +19,8 @@
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
-
import android.view.View;
+
import com.android.systemui.R;
/**
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 964688b..c9c5805 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -29,9 +29,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-Iaidl-files-under, src) \
- $(call all-java-files-under, ../src) \
- $(call all-proto-files-under, ../src) \
- src/com/android/systemui/EventLogTags.logtags
+ $(call all-java-files-under, ../src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
frameworks/support/v7/preference/res \
@@ -53,7 +51,8 @@
android-support-v7-preference \
android-support-v7-appcompat \
android-support-v14-preference \
- android-support-v17-leanback
+ android-support-v17-leanback \
+ SystemUI-proto-tags
# sign this with platform cert, so this test is allowed to inject key events into
# UI it doesn't own. This is necessary to allow screenshots to be taken
diff --git a/packages/SystemUI/tests/src/com/android/systemui/EventLogTags.logtags b/packages/SystemUI/tests/src/com/android/systemui/EventLogTags.logtags
deleted file mode 120000
index 2f243d7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/EventLogTags.logtags
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../src/com/android/systemui/EventLogTags.logtags
\ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index cd026a8..471feef 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -441,5 +441,42 @@
// Tuner: Change do not disturb volume buttons shortcut.
ACTION_TUNER_DO_NOT_DISTURB_VOLUME_SHORTCUT = 315;
+
+ // Logs the action the user takes when an app crashed.
+ ACTION_APP_CRASH = 316;
+
+ // Logs the action the user takes when an app ANR'd.
+ ACTION_APP_ANR = 317;
+
+ // Logged when a user double taps the overview button to launch the previous task
+ OVERVIEW_LAUNCH_PREVIOUS_TASK = 318;
+
+ // Logged when we execute an app transition. This indicates the total delay from startActivity
+ // until the app transition is starting to animate, in milliseconds.
+ APP_TRANSITION_DELAY_MS = 319;
+
+ // Logged when we execute an app transition. This indicates the reason why the transition
+ // started. Must be one of ActivityManagerInternal.APP_TRANSITION_* reasons.
+ APP_TRANSITION_REASON = 320;
+
+ // Logged when we execute an app transition and we drew a starting window. This indicates the
+ // delay from startActivity until the starting window was drawn.
+ APP_TRANSITION_STARTING_WINDOW_DELAY_MS = 321;
+
+ // Logged when we execute an app transition and all windows of the app got drawn. This indicates
+ // the delay from startActivity until all windows have been drawn.
+ APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS = 322;
+
+ // Logged when we execute an app transition. This indicates the component name of the current
+ // transition.
+ APP_TRANSITION_COMPONENT_NAME = 323;
+
+ // Logged when we execute an app transition. This indicates whether the process was already
+ // running.
+ APP_TRANSITION_PROCESS_RUNNING = 324;
+
+ // Logged when we execute an app transition. This indicates the device uptime in seconds when
+ // the transition was executed.
+ APP_TRANSITION_DEVICE_UPTIME_SECONDS = 325;
}
}
diff --git a/services/accessibility/Android.mk b/services/accessibility/Android.mk
index d98fc28..ce89aa7 100644
--- a/services/accessibility/Android.mk
+++ b/services/accessibility/Android.mk
@@ -7,4 +7,6 @@
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES := services.core
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 0dbc5be..2b682c5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -94,6 +94,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
@@ -2778,6 +2779,9 @@
case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
showGlobalActions();
} return true;
+ case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: {
+ toggleSplitScreen();
+ } return true;
}
return false;
} finally {
@@ -3249,6 +3253,10 @@
mWindowManagerService.showGlobalActions();
}
+ private void toggleSplitScreen() {
+ LocalServices.getService(StatusBarManagerInternal.class).toggleSplitScreen();
+ }
+
private IAccessibilityInteractionConnection getConnectionLocked(int windowId) {
if (DEBUG) {
Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
@@ -3465,11 +3473,14 @@
case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
- case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
- case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: {
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: {
return AccessibilityWindowInfo.TYPE_SYSTEM;
}
+ case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: {
+ return AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER;
+ }
+
case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY: {
return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY;
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e32d89c..f1a9c44 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -171,6 +171,10 @@
static final boolean MORE_DEBUG = false;
static final boolean DEBUG_SCHEDULING = MORE_DEBUG || true;
+ // File containing backup-enabled state. Contains a single byte;
+ // nonzero == enabled. File missing or contains a zero byte == disabled.
+ static final String BACKUP_ENABLE_FILE = "backup_enabled";
+
// System-private key used for backing up an app's widget state. Must
// begin with U+FFxx by convention (we reserve all keys starting
// with U+FF00 or higher for system use).
@@ -354,11 +358,30 @@
if (userId == UserHandle.USER_SYSTEM) {
sInstance.initialize(userId);
- ContentResolver r = sInstance.mContext.getContentResolver();
- boolean areEnabled = Settings.Secure.getIntForUser(r,
- Settings.Secure.BACKUP_ENABLED, 0, userId) != 0;
+ // Migrate legacy setting
+ if (!backupSettingMigrated(userId)) {
+ if (DEBUG) {
+ Slog.i(TAG, "Backup enable apparently not migrated");
+ }
+ final ContentResolver r = sInstance.mContext.getContentResolver();
+ final int enableState = Settings.Secure.getIntForUser(r,
+ Settings.Secure.BACKUP_ENABLED, -1, userId);
+ if (enableState >= 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+ }
+ writeBackupEnableState(enableState != 0, userId);
+ Settings.Secure.putStringForUser(r,
+ Settings.Secure.BACKUP_ENABLED, null, userId);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+ }
+ }
+ }
+
try {
- sInstance.setBackupEnabled(areEnabled);
+ sInstance.setBackupEnabled(readBackupEnableState(userId));
} catch (RemoteException e) {
// can't happen; it's a local object
}
@@ -9314,6 +9337,58 @@
}
}
+ private static boolean backupSettingMigrated(int userId) {
+ File base = new File(Environment.getDataDirectory(), "backup");
+ File enableFile = new File(base, BACKUP_ENABLE_FILE);
+ return enableFile.exists();
+ }
+
+ private static boolean readBackupEnableState(int userId) {
+ File base = new File(Environment.getDataDirectory(), "backup");
+ File enableFile = new File(base, BACKUP_ENABLE_FILE);
+ if (enableFile.exists()) {
+ try (FileInputStream fin = new FileInputStream(enableFile)) {
+ int state = fin.read();
+ return state != 0;
+ } catch (IOException e) {
+ // can't read the file; fall through to assume disabled
+ Slog.e(TAG, "Cannot read enable state; assuming disabled");
+ }
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
+ }
+ }
+ return false;
+ }
+
+ private static void writeBackupEnableState(boolean enable, int userId) {
+ File base = new File(Environment.getDataDirectory(), "backup");
+ File enableFile = new File(base, BACKUP_ENABLE_FILE);
+ File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
+ FileOutputStream fout = null;
+ try {
+ fout = new FileOutputStream(stage);
+ fout.write(enable ? 1 : 0);
+ fout.close();
+ stage.renameTo(enableFile);
+ // will be synced immediately by the try-with-resources call to close()
+ } catch (IOException|RuntimeException e) {
+ // Whoops; looks like we're doomed. Roll everything out, disabled,
+ // including the legacy state.
+ Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
+ + e.getMessage());
+
+ final ContentResolver r = sInstance.mContext.getContentResolver();
+ Settings.Secure.putStringForUser(r,
+ Settings.Secure.BACKUP_ENABLED, null, userId);
+ enableFile.delete();
+ stage.delete();
+ } finally {
+ IoUtils.closeQuietly(fout);
+ }
+ }
+
// Enable/disable backups
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -9325,8 +9400,7 @@
try {
boolean wasEnabled = mEnabled;
synchronized (this) {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
+ writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
mEnabled = enable;
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index d14364d..caeb0c6 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -660,12 +660,7 @@
long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(userId);
- synchronized (accounts.cacheLock) {
- if (!accountExistsCacheLocked(accounts, account)) {
- return null;
- }
- return readUserDataInternalLocked(accounts, account, key);
- }
+ return readUserDataInternal(accounts, account, key);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -1717,58 +1712,44 @@
long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(userId);
- synchronized (accounts.cacheLock) {
- if (!accountExistsCacheLocked(accounts, account)) {
- return;
- }
- setUserdataInternalLocked(accounts, account, key, value);
- }
+ setUserdataInternal(accounts, account, key, value);
} finally {
restoreCallingIdentity(identityToken);
}
}
- private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
- if (accounts.accountCache.containsKey(account.type)) {
- for (Account acc : accounts.accountCache.get(account.type)) {
- if (acc.name.equals(account.name)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
+ private void setUserdataInternal(UserAccounts accounts, Account account, String key,
String value) {
if (account == null || key == null) {
return;
}
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
- db.beginTransaction();
- try {
- long accountId = getAccountIdLocked(db, account);
- if (accountId < 0) {
- return;
- }
- long extrasId = getExtrasIdLocked(db, accountId, key);
- if (extrasId < 0) {
- extrasId = insertExtraLocked(db, accountId, key, value);
- if (extrasId < 0) {
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ long accountId = getAccountIdLocked(db, account);
+ if (accountId < 0) {
return;
}
- } else {
- ContentValues values = new ContentValues();
- values.put(EXTRAS_VALUE, value);
- if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
- return;
- }
+ long extrasId = getExtrasIdLocked(db, accountId, key);
+ if (extrasId < 0 ) {
+ extrasId = insertExtraLocked(db, accountId, key, value);
+ if (extrasId < 0) {
+ return;
+ }
+ } else {
+ ContentValues values = new ContentValues();
+ values.put(EXTRAS_VALUE, value);
+ if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
+ return;
+ }
+ }
+ writeUserDataIntoCacheLocked(accounts, db, account, key, value);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
}
- writeUserDataIntoCacheLocked(accounts, db, account, key, value);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
}
}
@@ -4803,16 +4784,17 @@
}
}
- protected String readUserDataInternalLocked(
- UserAccounts accounts, Account account, String key) {
- HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
- if (userDataForAccount == null) {
- // need to populate the cache for this account
- final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
- userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
- accounts.userDataCache.put(account, userDataForAccount);
+ protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
+ synchronized (accounts.cacheLock) {
+ HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
+ if (userDataForAccount == null) {
+ // need to populate the cache for this account
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
+ accounts.userDataCache.put(account, userDataForAccount);
+ }
+ return userDataForAccount.get(key);
}
- return userDataForAccount.get(key);
}
protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e3a0b5c..b594ae8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1371,7 +1371,6 @@
int mProcessLimitOverride = -1;
WindowManagerService mWindowManager;
-
final ActivityThread mSystemThread;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -2487,7 +2486,6 @@
mActivityStarter = new ActivityStarter(this, mStackSupervisor);
mRecentTasks = new RecentTasks(this, mStackSupervisor);
-
mProcessCpuThread = new Thread("CpuTracker") {
@Override
public void run() {
@@ -13395,6 +13393,14 @@
}
@Override
+ public int getMemoryTrimLevel() {
+ enforceNotIsolatedCaller("getMyMemoryState");
+ synchronized (this) {
+ return mLastMemoryLevel;
+ }
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
(new ActivityManagerShellCommand(this, false)).exec(
@@ -20757,6 +20763,20 @@
voiceSession, voiceInteractor);
}
}
+
+ @Override
+ public void notifyStartingWindowDrawn() {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn();
+ }
+ }
+
+ @Override
+ public void notifyAppTransitionStarting(int reason) {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reason);
+ }
+ }
}
private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index ffb2fc4..0e6dd28 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -7,11 +7,13 @@
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
+import android.annotation.Nullable;
import android.app.ActivityManager.StackId;
import android.content.Context;
import android.os.SystemClock;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
/**
* Handles logging into Tron.
@@ -24,6 +26,8 @@
private static final int WINDOW_STATE_FREEFORM = 2;
private static final int WINDOW_STATE_INVALID = -1;
+ private static final long INVALID_START_TIME = -1;
+
// Preallocated strings we are sending to tron, so we don't have to allocate a new one every
// time we log.
private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
@@ -34,6 +38,11 @@
private final ActivityStackSupervisor mSupervisor;
private final Context mContext;
+ private long mCurrentTransitionStartTime = INVALID_START_TIME;
+ private boolean mLoggedWindowsDrawn;
+ private boolean mLoggedStartingWindowDrawn;
+ private boolean mLoggedTransitionStarting;
+
ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) {
mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
mSupervisor = supervisor;
@@ -73,4 +82,101 @@
throw new IllegalStateException("Unknown stack=" + stack);
}
}
+
+ /**
+ * Notifies the tracker at the earliest possible point when we are starting to launch an
+ * activity.
+ */
+ void notifyActivityLaunching() {
+ mCurrentTransitionStartTime = System.currentTimeMillis();
+ }
+
+ /**
+ * Notifies the tracker the the activity is actually launching.
+ *
+ * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
+ * launch
+ * @param componentName the component name of the activity being launched
+ * @param processRunning whether the process that will contains the activity is already running
+ */
+ void notifyActivityLaunched(int resultCode, @Nullable String componentName,
+ boolean processRunning) {
+
+ if (resultCode < 0 || componentName == null) {
+
+ // Failed to launch, don't track anything.
+ reset();
+ return;
+ }
+
+ MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_COMPONENT_NAME,
+ componentName);
+ MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_PROCESS_RUNNING,
+ processRunning);
+ MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
+ (int) (SystemClock.uptimeMillis() / 1000));
+ }
+
+ /**
+ * Notifies the tracker that all windows of the app have been drawn.
+ */
+ void notifyWindowsDrawn() {
+ if (!isTransitionActive() || mLoggedWindowsDrawn) {
+ return;
+ }
+ MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS,
+ calculateCurrentDelay());
+ mLoggedWindowsDrawn = true;
+ if (mLoggedTransitionStarting) {
+ reset();
+ }
+ }
+
+ /**
+ * Notifies the tracker that the starting window was drawn.
+ */
+ void notifyStartingWindowDrawn() {
+ if (!isTransitionActive() || mLoggedStartingWindowDrawn) {
+ return;
+ }
+ mLoggedStartingWindowDrawn = true;
+ MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
+ calculateCurrentDelay());
+ }
+
+ /**
+ * Notifies the tracker that the app transition is starting.
+ *
+ * @param reason The reason why we started it. Must be on of
+ * ActivityManagerInternal.APP_TRANSITION_* reasons.
+ */
+ void notifyTransitionStarting(int reason) {
+ if (!isTransitionActive() || mLoggedTransitionStarting) {
+ return;
+ }
+ MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_REASON, reason);
+ MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DELAY_MS,
+ calculateCurrentDelay());
+ mLoggedTransitionStarting = true;
+ if (mLoggedWindowsDrawn) {
+ reset();
+ }
+ }
+
+ private boolean isTransitionActive() {
+ return mCurrentTransitionStartTime != INVALID_START_TIME;
+ }
+
+ private void reset() {
+ mCurrentTransitionStartTime = INVALID_START_TIME;
+ mLoggedWindowsDrawn = false;
+ mLoggedTransitionStarting = false;
+ mLoggedStartingWindowDrawn = false;
+ }
+
+ private int calculateCurrentDelay() {
+
+ // Shouldn't take more than 25 days to launch an app, so int is fine here.
+ return (int) (System.currentTimeMillis() - mCurrentTransitionStartTime);
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 90a7da9..e2ea1f1 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -321,6 +321,7 @@
pw.print(" visible="); pw.print(visible);
pw.print(" sleeping="); pw.print(sleeping);
pw.print(" idle="); pw.println(idle);
+ pw.print(" mStartingWindowShown="); pw.println(mStartingWindowShown);
pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
pw.print(" noDisplay="); pw.print(noDisplay);
pw.print(" immersive="); pw.print(immersive);
@@ -1166,6 +1167,7 @@
}
void windowsDrawnLocked() {
+ mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn();
if (displayStartTime != 0) {
reportLaunchTimeLocked(SystemClock.uptimeMillis());
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index acde10f..ec37667 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2263,6 +2263,7 @@
mService.compatibilityInfoForPackageLocked(next.info.applicationInfo),
next.nonLocalizedLabel, next.labelRes, next.icon, next.logo,
next.windowFlags, null, true);
+ next.mStartingWindowShown = true;
}
mStackSupervisor.startSpecificActivityLocked(next, true, false);
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2295,6 +2296,7 @@
next.nonLocalizedLabel,
next.labelRes, next.icon, next.logo, next.windowFlags,
null, true);
+ next.mStartingWindowShown = true;
}
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index af5f6b3..20f8285 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -406,7 +406,7 @@
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
- private final ActivityMetricsLogger mActivityMetricsLogger;
+ final ActivityMetricsLogger mActivityMetricsLogger;
private final ResizeDockedStackTimeout mResizeDockedStackTimeout;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 9c7eb95..0181640 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -593,6 +593,7 @@
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ mSupervisor.mActivityMetricsLogger.notifyActivityLaunching();
boolean componentSpecified = intent.getComponent() != null;
// Save a copy in case ephemeral needs it
@@ -722,11 +723,13 @@
}
}
+ final ActivityRecord[] outRecord = new ActivityRecord[1];
int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
aInfo, rInfo, voiceSession, voiceInteractor,
resultTo, resultWho, requestCode, callingPid,
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
- options, ignoreTargetSecurity, componentSpecified, null, container, inTask);
+ options, ignoreTargetSecurity, componentSpecified, outRecord, container,
+ inTask);
Binder.restoreCallingIdentity(origId);
@@ -773,6 +776,13 @@
}
}
+ final String componentName = outRecord[0] != null ? outRecord[0].shortComponentName
+ : null;
+ final boolean processRunning = outRecord[0] != null &&
+ mService.mProcessNames.get(outRecord[0].processName,
+ outRecord[0].appInfo.uid) != null;
+ mSupervisor.mActivityMetricsLogger.notifyActivityLaunched(res, componentName,
+ processRunning);
return res;
}
}
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index b746a4b..86cdbcc 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -37,6 +37,7 @@
import static com.android.server.am.ActivityManagerService.IS_USER_BUILD;
final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener {
+
private final ActivityManagerService mService;
private final AppErrorResult mResult;
private final ProcessRecord mProc;
@@ -44,12 +45,17 @@
private CharSequence mName;
+ static int CANT_SHOW = -1;
+ static int BACKGROUND_USER = -2;
+ static int ALREADY_SHOWING = -3;
+
// Event 'what' codes
static final int FORCE_QUIT = 1;
static final int FORCE_QUIT_AND_REPORT = 2;
static final int RESTART = 3;
static final int RESET = 4;
static final int MUTE = 5;
+ static final int TIMEOUT = 6;
// 5-minute timeout, then we automatically dismiss the crash dialog
static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
@@ -89,7 +95,7 @@
// After the timeout, pretend the user clicked the quit button
mHandler.sendMessageDelayed(
- mHandler.obtainMessage(FORCE_QUIT),
+ mHandler.obtainMessage(TIMEOUT),
DISMISS_TIMEOUT);
}
@@ -132,7 +138,7 @@
mResult.set(result);
// Make sure we don't have time timeout still hanging around.
- removeMessages(FORCE_QUIT);
+ removeMessages(TIMEOUT);
dismiss();
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 190e9e1..055935d 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -17,6 +17,8 @@
package com.android.server.am;
import com.android.internal.app.ProcessMap;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto;
import com.android.internal.os.ProcessCpuTracker;
import com.android.server.Watchdog;
@@ -403,6 +405,10 @@
Intent appErrorIntent = null;
final long ident = Binder.clearCallingIdentity();
try {
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
+ if (res == AppErrorDialog.TIMEOUT) {
+ res = AppErrorDialog.FORCE_QUIT;
+ }
if (res == AppErrorDialog.RESET) {
String[] packageList = r.getPackageList();
if (packageList != null) {
@@ -697,7 +703,7 @@
if (proc != null && proc.crashDialog != null) {
Slog.e(TAG, "App already has crash dialog: " + proc);
if (res != null) {
- res.set(0);
+ res.set(AppErrorDialog.ALREADY_SHOWING);
}
return;
}
@@ -710,7 +716,7 @@
if (isBackground && !showBackground) {
Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
if (res != null) {
- res.set(0);
+ res.set(AppErrorDialog.BACKGROUND_USER);
}
return;
}
@@ -724,7 +730,7 @@
// The device is asleep, so just pretend that the user
// saw a crash dialog and hit "force quit".
if (res != null) {
- res.set(0);
+ res.set(AppErrorDialog.CANT_SHOW);
}
}
}
@@ -920,6 +926,8 @@
ProcessRecord proc = (ProcessRecord)data.get("app");
if (proc != null && proc.anrDialog != null) {
Slog.e(TAG, "App already has anr dialog: " + proc);
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
+ AppNotRespondingDialog.ALREADY_SHOWING);
return;
}
@@ -939,6 +947,8 @@
d.show();
proc.anrDialog = d;
} else {
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
+ AppNotRespondingDialog.CANT_SHOW);
// Just kill the app if there is no dialog to be shown.
mService.killAppAtUsersRequest(proc, null);
}
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 9875887..6d1d9f3 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto;
+
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
@@ -41,6 +44,9 @@
static final int WAIT = 2;
static final int WAIT_AND_REPORT = 3;
+ public static final int CANT_SHOW = -1;
+ public static final int ALREADY_SHOWING = -2;
+
private final ActivityManagerService mService;
private final ProcessRecord mProc;
@@ -132,6 +138,10 @@
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
Intent appErrorIntent = null;
+
+ MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_APP_ANR,
+ msg.what);
+
switch (msg.what) {
case FORCE_CLOSE:
// Kill the application.
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index e9d684a..5d81dae 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -505,7 +505,7 @@
}
public boolean hasEnrolledFingerprints(int userId) {
- if (userId != Binder.getCallingUid()) {
+ if (userId != UserHandle.getCallingUserId()) {
checkPermission(INTERACT_ACROSS_USERS);
}
return mFingerprintUtils.getFingerprintsForUser(mContext, userId).size() > 0;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index f1d7b36..bd888d8 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -19,6 +19,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -52,8 +53,11 @@
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.app.ProcessStats;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
import com.android.server.job.JobStore.JobStatusFunctor;
@@ -66,6 +70,8 @@
import com.android.server.job.controllers.StateController;
import com.android.server.job.controllers.TimeController;
+import libcore.util.EmptyArray;
+
/**
* Responsible for taking jobs representing work to be performed by a client app, and determining
* based on the criteria specified when that job should be run against the client application's
@@ -83,9 +89,8 @@
static final String TAG = "JobSchedulerService";
public static final boolean DEBUG = false;
- /** The number of concurrent jobs we run at one time. */
- private static final int MAX_JOB_CONTEXTS_COUNT
- = ActivityManager.isLowRamDeviceStatic() ? 3 : 6;
+ /** The maximum number of concurrent jobs we run at one time. */
+ private static final int MAX_JOB_CONTEXTS_COUNT = 8;
/** Enforce a per-app limit on scheduled jobs? */
private static final boolean ENFORCE_MAX_JOBS = false;
/** The maximum number of jobs that we allow an unprivileged app to schedule */
@@ -142,7 +147,7 @@
*/
final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
- final ArrayList<Integer> mStartedUsers = new ArrayList<>();
+ int[] mStartedUsers = EmptyArray.INT;
final JobHandler mHandler;
final JobSchedulerStub mJobSchedulerStub;
@@ -167,9 +172,33 @@
boolean mReportedActive;
/**
+ * Current limit on the number of concurrent JobServiceContext entries we want to
+ * keep actively running a job.
+ */
+ int mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;
+
+ /**
* Which uids are currently in the foreground.
*/
- final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+ final SparseIntArray mUidPriorityOverride = new SparseIntArray();
+
+ // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
+
+ /**
+ * This array essentially stores the state of mActiveServices array.
+ * The ith index stores the job present on the ith JobServiceContext.
+ * We manipulate this array until we arrive at what jobs should be running on
+ * what JobServiceContext.
+ */
+ JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+ /**
+ * Indicates whether we need to act on this jobContext id
+ */
+ boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
+ /**
+ * The uid whose jobs we would like to assign to a context.
+ */
+ int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
/**
* Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
@@ -228,14 +257,20 @@
@Override
public void onStartUser(int userHandle) {
- mStartedUsers.add(userHandle);
+ mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
+ // Let's kick any outstanding jobs for this user.
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ }
+
+ @Override
+ public void onUnlockUser(int userHandle) {
// Let's kick any outstanding jobs for this user.
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
@Override
public void onStopUser(int userHandle) {
- mStartedUsers.remove(Integer.valueOf(userHandle));
+ mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
}
/**
@@ -366,19 +401,15 @@
void updateUidState(int uid, int procState) {
synchronized (mLock) {
- boolean foreground = procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
- boolean changed = false;
- if (foreground) {
- if (!mForegroundUids.get(uid)) {
- changed = true;
- mForegroundUids.put(uid, true);
- }
+ if (procState == ActivityManager.PROCESS_STATE_TOP) {
+ // Only use this if we are exactly the top app. All others can live
+ // with just the foreground priority. This means that persistent processes
+ // can never be the top app priority... that is fine.
+ mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
+ } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
} else {
- int index = mForegroundUids.indexOfKey(uid);
- if (index >= 0) {
- mForegroundUids.removeAt(index);
- changed = true;
- }
+ mUidPriorityOverride.delete(uid);
}
}
}
@@ -936,18 +967,31 @@
* - It's not pending.
* - It's not already running on a JSC.
* - The user that requested the job is running.
+ * - The component is enabled and runnable.
*/
private boolean isReadyToBeExecutedLocked(JobStatus job) {
final boolean jobReady = job.isReady();
final boolean jobPending = mPendingJobs.contains(job);
final boolean jobActive = isCurrentlyActiveLocked(job);
- final boolean userRunning = mStartedUsers.contains(job.getUserId());
+
+ final int userId = job.getUserId();
+ final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
+ final boolean componentPresent;
+ try {
+ componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
+ job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ userId) != null);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ " ready=" + jobReady + " pending=" + jobPending
- + " active=" + jobActive + " userRunning=" + userRunning);
+ + " active=" + jobActive + " userStarted=" + userStarted
+ + " componentPresent=" + componentPresent);
}
- return userRunning && jobReady && !jobPending && !jobActive;
+ return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
}
/**
@@ -984,8 +1028,9 @@
if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
return priority;
}
- if (mForegroundUids.get(job.getSourceUid())) {
- return JobInfo.PRIORITY_FOREGROUND_APP;
+ int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
+ if (override != 0) {
+ return override;
}
return priority;
}
@@ -1001,24 +1046,44 @@
Slog.d(TAG, printPendingQueue());
}
- // This array essentially stores the state of mActiveServices array.
- // ith index stores the job present on the ith JobServiceContext.
- // We manipulate this array until we arrive at what jobs should be running on
- // what JobServiceContext.
- JobStatus[] contextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
- // Indicates whether we need to act on this jobContext id
- boolean[] act = new boolean[MAX_JOB_CONTEXTS_COUNT];
- int[] preferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
- for (int i=0; i<mActiveServices.size(); i++) {
- contextIdToJobMap[i] = mActiveServices.get(i).getRunningJob();
- preferredUidForContext[i] = mActiveServices.get(i).getPreferredUid();
+ int memLevel;
+ try {
+ memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
+ } catch (RemoteException e) {
+ memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+ }
+ switch (memLevel) {
+ case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+ mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - 2) * 2) / 3;
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_LOW:
+ mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - 2) / 3;
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+ mMaxActiveJobs = 1;
+ break;
+ default:
+ mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;
+ break;
+ }
+
+ JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
+ boolean[] act = mTmpAssignAct;
+ int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
+ int numActive = 0;
+ for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
+ final JobServiceContext js = mActiveServices.get(i);
+ if ((contextIdToJobMap[i] = js.getRunningJob()) != null) {
+ numActive++;
+ }
+ act[i] = false;
+ preferredUidForContext[i] = js.getPreferredUid();
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
}
- Iterator<JobStatus> it = mPendingJobs.iterator();
- while (it.hasNext()) {
- JobStatus nextPending = it.next();
+ for (int i=0; i<mPendingJobs.size(); i++) {
+ JobStatus nextPending = mPendingJobs.get(i);
// If job is already running, go to next job.
int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
@@ -1026,24 +1091,30 @@
continue;
}
- nextPending.lastEvaluatedPriority = evaluateJobPriorityLocked(nextPending);
+ final int priority = evaluateJobPriorityLocked(nextPending);
+ nextPending.lastEvaluatedPriority = priority;
// Find a context for nextPending. The context should be available OR
// it should have lowest priority among all running jobs
// (sharing the same Uid as nextPending)
int minPriority = Integer.MAX_VALUE;
int minPriorityContextId = -1;
- for (int i=0; i<mActiveServices.size(); i++) {
- JobStatus job = contextIdToJobMap[i];
- int preferredUid = preferredUidForContext[i];
- if (job == null && (preferredUid == nextPending.getUid() ||
- preferredUid == JobServiceContext.NO_PREFERRED_UID) ) {
- minPriorityContextId = i;
- break;
- }
+ for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
+ JobStatus job = contextIdToJobMap[j];
+ int preferredUid = preferredUidForContext[j];
if (job == null) {
+ if ((numActive < mMaxActiveJobs || priority >= JobInfo.PRIORITY_TOP_APP) &&
+ (preferredUid == nextPending.getUid() ||
+ preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
+ // This slot is free, and we haven't yet hit the limit on
+ // concurrent jobs... we can just throw the job in to here.
+ minPriorityContextId = j;
+ numActive++;
+ break;
+ }
// No job on this context, but nextPending can't run here because
- // the context has a preferred Uid.
+ // the context has a preferred Uid or we have reached the limit on
+ // concurrent jobs.
continue;
}
if (job.getUid() != nextPending.getUid()) {
@@ -1054,7 +1125,7 @@
}
if (minPriority > nextPending.lastEvaluatedPriority) {
minPriority = nextPending.lastEvaluatedPriority;
- minPriorityContextId = i;
+ minPriorityContextId = j;
}
}
if (minPriorityContextId != -1) {
@@ -1065,7 +1136,7 @@
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
}
- for (int i=0; i<mActiveServices.size(); i++) {
+ for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
boolean preservePreferredUid = false;
if (act[i]) {
JobStatus js = mActiveServices.get(i).getRunningJob();
@@ -1123,7 +1194,8 @@
final ComponentName service = job.getService();
try {
ServiceInfo si = pm.getServiceInfo(service,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(uid));
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
+ UserHandle.getUserId(uid));
if (si == null) {
throw new IllegalArgumentException("No such service " + service);
}
@@ -1295,11 +1367,7 @@
void dumpInternal(final PrintWriter pw) {
final long now = SystemClock.elapsedRealtime();
synchronized (mLock) {
- pw.print("Started users: ");
- for (int i=0; i<mStartedUsers.size(); i++) {
- pw.print("u" + mStartedUsers.get(i) + " ");
- }
- pw.println();
+ pw.println("Started users: " + Arrays.toString(mStartedUsers));
pw.println("Registered jobs:");
if (mJobs.size() > 0) {
mJobs.forEachJob(new JobStatusFunctor() {
@@ -1309,7 +1377,7 @@
public void process(JobStatus job) {
pw.print(" Job #"); pw.print(index++); pw.print(": ");
pw.println(job.toShortString());
- job.dump(pw, " ");
+ job.dump(pw, " ", true);
pw.print(" Ready: ");
pw.print(mHandler.isReadyToBeExecutedLocked(job));
pw.print(" (job=");
@@ -1319,7 +1387,7 @@
pw.print(" active=");
pw.print(isCurrentlyActiveLocked(job));
pw.print(" user=");
- pw.print(mStartedUsers.contains(job.getUserId()));
+ pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
pw.println(")");
}
});
@@ -1331,9 +1399,10 @@
mControllers.get(i).dumpControllerStateLocked(pw);
}
pw.println();
- pw.println("Foreground uids:");
- for (int i=0; i<mForegroundUids.size(); i++) {
- pw.print(" "); pw.println(UserHandle.formatUid(mForegroundUids.keyAt(i)));
+ pw.println("Uid priority overrides:");
+ for (int i=0; i< mUidPriorityOverride.size(); i++) {
+ pw.print(" "); pw.print(UserHandle.formatUid(mUidPriorityOverride.keyAt(i)));
+ pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
}
pw.println();
pw.println("Pending queue:");
@@ -1341,6 +1410,7 @@
JobStatus job = mPendingJobs.get(i);
pw.print(" Pending #"); pw.print(i); pw.print(": ");
pw.println(job.toShortString());
+ job.dump(pw, " ", false);
int priority = evaluateJobPriorityLocked(job);
if (priority != JobInfo.PRIORITY_DEFAULT) {
pw.print(" Evaluated priority: "); pw.println(priority);
@@ -1351,20 +1421,21 @@
pw.println("Active jobs:");
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
+ pw.print(" Slot #"); pw.print(i); pw.print(": ");
if (jsc.getRunningJob() == null) {
+ pw.println("inactive");
continue;
} else {
- final long timeout = jsc.getTimeoutElapsed();
- pw.print("Running for: ");
- pw.print((now - jsc.getExecutionStartTimeElapsed())/1000);
- pw.print("s timeout=");
- pw.print(timeout);
- pw.print(" fromnow=");
- pw.println(timeout-now);
- jsc.getRunningJob().dump(pw, " ");
+ pw.println(jsc.getRunningJob().toShortString());
+ pw.print(" Running for: ");
+ TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
+ pw.print(", timeout at: ");
+ TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
+ pw.println();
+ jsc.getRunningJob().dump(pw, " ", false);
int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
if (priority != JobInfo.PRIORITY_DEFAULT) {
- pw.print(" Evaluated priority: "); pw.println(priority);
+ pw.print(" Evaluated priority: "); pw.println(priority);
}
}
}
@@ -1372,6 +1443,7 @@
pw.print("mReadyToRock="); pw.println(mReadyToRock);
pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode);
pw.print("mReportedActive="); pw.println(mReportedActive);
+ pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
}
pw.println();
}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index e6fbc39..98bf8a9 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -134,6 +134,10 @@
sb.append(job.getService().getPackageName());
sb.append('/');
sb.append(this.sourceTag);
+ if (sourcePackageName != null) {
+ sb.append('/');
+ sb.append(this.sourcePackageName);
+ }
this.batteryName = sb.toString();
} else {
this.batteryName = job.getService().flattenToShortString();
@@ -433,10 +437,10 @@
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" jId=");
sb.append(job.getId());
- sb.append(" uid=");
+ sb.append(' ');
UserHandle.formatUid(sb, callingUid);
sb.append(' ');
- sb.append(job.getService().flattenToShortString());
+ sb.append(batteryName);
return sb.toString();
}
@@ -468,75 +472,71 @@
}
// Dumpsys infrastructure
- public void dump(PrintWriter pw, String prefix) {
+ public void dump(PrintWriter pw, String prefix, boolean full) {
pw.print(prefix); UserHandle.formatUid(pw, callingUid);
pw.print(" tag="); pw.println(tag);
pw.print(prefix);
pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
pw.print(" user="); pw.print(getSourceUserId());
pw.print(" pkg="); pw.println(getSourcePackageName());
- pw.print(prefix); pw.println("JobInfo:");
- pw.print(prefix); pw.print(" Service: ");
- pw.println(job.getService().flattenToShortString());
- if (job.isPeriodic()) {
- pw.print(prefix); pw.print(" PERIODIC: interval=");
- TimeUtils.formatDuration(job.getIntervalMillis(), pw);
- pw.print(" flex=");
- TimeUtils.formatDuration(job.getFlexMillis(), pw);
- pw.println();
- }
- if (job.isPersisted()) {
- pw.print(prefix); pw.println(" PERSISTED");
- }
- if (job.getPriority() != 0) {
- pw.print(prefix); pw.print(" Priority: ");
- pw.println(job.getPriority());
- }
- pw.print(prefix); pw.print(" Requires: charging=");
- pw.print(job.isRequireCharging());
- pw.print(" deviceIdle=");
- pw.println(job.isRequireDeviceIdle());
- if (job.getTriggerContentUris() != null) {
- pw.print(prefix); pw.println(" Trigger content URIs:");
- for (int i=0; i<job.getTriggerContentUris().length; i++) {
- JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
- pw.print(prefix); pw.print(" ");
- pw.print(Integer.toHexString(trig.getFlags()));
- pw.print(' ' );
- pw.println(trig.getUri());
+ if (full) {
+ pw.print(prefix); pw.println("JobInfo:"); pw.print(prefix);
+ pw.print(" Service: "); pw.println(job.getService().flattenToShortString());
+ if (job.isPeriodic()) {
+ pw.print(prefix); pw.print(" PERIODIC: interval=");
+ TimeUtils.formatDuration(job.getIntervalMillis(), pw);
+ pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw);
+ pw.println();
}
- }
- if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
- pw.print(prefix); pw.print(" Network type: ");
- pw.println(job.getNetworkType());
- }
- if (job.getMinLatencyMillis() != 0) {
- pw.print(prefix); pw.print(" Minimum latency: ");
- TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
+ if (job.isPersisted()) {
+ pw.print(prefix); pw.println(" PERSISTED");
+ }
+ if (job.getPriority() != 0) {
+ pw.print(prefix); pw.print(" Priority: "); pw.println(job.getPriority());
+ }
+ pw.print(prefix); pw.print(" Requires: charging=");
+ pw.print(job.isRequireCharging()); pw.print(" deviceIdle=");
+ pw.println(job.isRequireDeviceIdle());
+ if (job.getTriggerContentUris() != null) {
+ pw.print(prefix); pw.println(" Trigger content URIs:");
+ for (int i = 0; i < job.getTriggerContentUris().length; i++) {
+ JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
+ pw.print(prefix); pw.print(" ");
+ pw.print(Integer.toHexString(trig.getFlags()));
+ pw.print(' '); pw.println(trig.getUri());
+ }
+ }
+ if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
+ pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType());
+ }
+ if (job.getMinLatencyMillis() != 0) {
+ pw.print(prefix); pw.print(" Minimum latency: ");
+ TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
+ pw.println();
+ }
+ if (job.getMaxExecutionDelayMillis() != 0) {
+ pw.print(prefix); pw.print(" Max execution delay: ");
+ TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
+ pw.println();
+ }
+ pw.print(prefix); pw.print(" Backoff: policy="); pw.print(job.getBackoffPolicy());
+ pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
pw.println();
- }
- if (job.getMaxExecutionDelayMillis() != 0) {
- pw.print(prefix); pw.print(" Max execution delay: ");
- TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
- pw.println();
- }
- pw.print(prefix); pw.print(" Backoff: policy=");
- pw.print(job.getBackoffPolicy());
- pw.print(" initial=");
- TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
- pw.println();
- if (job.hasEarlyConstraint()) {
- pw.print(prefix); pw.println(" Has early constraint");
- }
- if (job.hasLateConstraint()) {
- pw.print(prefix); pw.println(" Has late constraint");
+ if (job.hasEarlyConstraint()) {
+ pw.print(prefix); pw.println(" Has early constraint");
+ }
+ if (job.hasLateConstraint()) {
+ pw.print(prefix); pw.println(" Has late constraint");
+ }
}
pw.print(prefix); pw.print("Required constraints:");
dumpConstraints(pw, requiredConstraints);
pw.println();
- pw.print(prefix); pw.print("Satisfied constraints:");
- dumpConstraints(pw, satisfiedConstraints);
- pw.println();
+ if (full) {
+ pw.print(prefix); pw.print("Satisfied constraints:");
+ dumpConstraints(pw, satisfiedConstraints);
+ pw.println();
+ }
if (changedAuthorities != null) {
pw.print(prefix); pw.println("Changed authorities:");
for (int i=0; i<changedAuthorities.size(); i++) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c337c573..692e610 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -36,7 +36,7 @@
import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.service.notification.NotificationListenerService.TRIM_FULL;
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -2524,7 +2524,7 @@
final Notification notification = record.sbn.getNotification();
// Should this notification make noise, vibe, or use the LED?
- final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_HIGH;
+ final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
if (DBG || record.isIntercepted())
Slog.v(TAG,
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 6c5685d..fd893fa 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -15,6 +15,7 @@
*/
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MIN;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
@@ -33,6 +34,8 @@
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.EventLogTags;
@@ -55,6 +58,8 @@
* {@hide}
*/
public final class NotificationRecord {
+ static final String TAG = "NotificationRecord";
+ static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
final StatusBarNotification sbn;
final int mOriginalFlags;
private final Context mContext;
@@ -123,6 +128,8 @@
switch (n.priority) {
case Notification.PRIORITY_MIN:
+ importance = IMPORTANCE_MIN;
+ break;
case Notification.PRIORITY_LOW:
importance = IMPORTANCE_LOW;
break;
@@ -143,25 +150,15 @@
|| n.sound != null
|| n.vibrate != null;
stats.isNoisy = isNoisy;
- if (!isNoisy && importance > IMPORTANCE_DEFAULT) {
- importance = IMPORTANCE_DEFAULT;
+
+ if (!isNoisy && importance > IMPORTANCE_LOW) {
+ importance = IMPORTANCE_LOW;
}
- try {
- final ApplicationInfo applicationInfo =
- mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(),
- 0, sbn.getUser().getIdentifier());
- if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.N) {
- if (isNoisy) {
- if (importance >= IMPORTANCE_HIGH) {
- importance = IMPORTANCE_MAX;
- } else {
- importance = IMPORTANCE_HIGH;
- }
- }
+ if (isNoisy) {
+ if (importance < IMPORTANCE_DEFAULT) {
+ importance = IMPORTANCE_DEFAULT;
}
- } catch (NameNotFoundException e) {
- // oh well.
}
if (n.fullScreenIntent != null) {
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 0272850..538f951 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -597,8 +597,9 @@
private static class ImportanceHistogram {
// TODO define these somewhere else
- private static final int NUM_IMPORTANCES = 5;
- private static final String[] IMPORTANCE_NAMES = {"none", "low", "default", "high", "max"};
+ private static final int NUM_IMPORTANCES = 6;
+ private static final String[] IMPORTANCE_NAMES =
+ {"none", "min", "low", "default", "high", "max"};
private final Context mContext;
private final String[] mCounterNames;
private final String mPrefix;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0f67a05..3f06c78 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10668,14 +10668,14 @@
pkgSetting.setInstalled(true, userId);
pkgSetting.setHidden(false, userId);
mSettings.writePackageRestrictionsLPr(userId);
- if (pkgSetting.pkg != null) {
- prepareAppDataAfterInstall(pkgSetting.pkg);
- }
installed = true;
}
}
if (installed) {
+ if (pkgSetting.pkg != null) {
+ prepareAppDataAfterInstall(pkgSetting.pkg);
+ }
sendPackageAddedForUser(packageName, pkgSetting, userId);
}
} finally {
@@ -18047,6 +18047,8 @@
* correct for all installed apps. If there is an ownership mismatch, it
* will try recovering system apps by wiping data; third-party app data is
* left intact.
+ * <p>
+ * <em>Note: To avoid a deadlock, do not call this method with {@code mPackages} lock held</em>
*/
private void prepareAppDataAfterInstall(PackageParser.Package pkg) {
prepareAppDataAfterInstallInternal(pkg);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index dd13809..1652185 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2387,10 +2387,7 @@
if (DEBUG_KERNEL) Slog.d(TAG, "Dropping mapping " + name);
mKernelMapping.remove(name);
-
- final File dir = new File(mKernelMappingFilename, name);
- FileUtils.deleteContents(dir);
- dir.delete();
+ new File(mKernelMappingFilename, name).delete();
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index cbbcdae..6bda4ed 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -33,4 +33,5 @@
void topAppWindowChanged(boolean menuVisible);
void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
Rect fullscreenBounds, Rect dockedBounds, String cause);
+ void toggleSplitScreen();
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 6eab8d4..d24e1af 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -204,6 +204,16 @@
StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
}
+
+ @Override
+ public void toggleSplitScreen() {
+ enforceStatusBarService();
+ if (mBar != null) {
+ try {
+ mBar.toggleSplitScreen();
+ } catch (RemoteException ex) {}
+ }
+ }
};
// ================================================================================
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 77db275..ba0d340 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -231,11 +231,16 @@
}
if (lockWallpaperChanged
|| (wallpaper.whichPending & FLAG_SET_LOCK) != 0) {
- // either a lock-only wallpaper commit or a system+lock event,
- // so tell keyguard about it
if (DEBUG) {
- Slog.i(TAG, "Lock-relevant wallpaper changed; telling listener");
+ Slog.i(TAG, "Lock-relevant wallpaper changed");
}
+ // either a lock-only wallpaper commit or a system+lock event.
+ // if it's system-plus-lock we need to wipe the lock bookkeeping;
+ // we're falling back to displaying the system wallpaper there.
+ if (!lockWallpaperChanged) {
+ mLockWallpaperMap.remove(wallpaper.userId);
+ }
+ // and in any case, tell keyguard about it
final IWallpaperManagerCallback cb = mKeyguardListener;
if (cb != null) {
try {
@@ -245,7 +250,7 @@
}
}
}
- saveSettingsLocked(wallpaper);
+ saveSettingsLocked(wallpaper.userId);
}
}
}
@@ -479,7 +484,7 @@
// when we have an engine, but I'm not sure about
// locking there and anyway we always need to be able to
// recover if there is something wrong.
- saveSettingsLocked(mWallpaper);
+ saveSettingsLocked(mWallpaper.userId);
}
}
}
@@ -995,7 +1000,7 @@
if (width != wallpaper.width || height != wallpaper.height) {
wallpaper.width = width;
wallpaper.height = height;
- saveSettingsLocked(wallpaper);
+ saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
if (wallpaper.connection.mEngine != null) {
@@ -1052,7 +1057,7 @@
if (!padding.equals(wallpaper.padding)) {
wallpaper.padding.set(padding);
- saveSettingsLocked(wallpaper);
+ saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
if (wallpaper.connection.mEngine != null) {
@@ -1488,50 +1493,33 @@
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
- private void saveSettingsLocked(WallpaperData wallpaper) {
- JournaledFile journal = makeJournaledFile(wallpaper.userId);
- FileOutputStream stream = null;
+ private void saveSettingsLocked(int userId) {
+ JournaledFile journal = makeJournaledFile(userId);
+ FileOutputStream fstream = null;
+ BufferedOutputStream stream = null;
try {
- stream = new FileOutputStream(journal.chooseForWrite(), false);
XmlSerializer out = new FastXmlSerializer();
+ fstream = new FileOutputStream(journal.chooseForWrite(), false);
+ stream = new BufferedOutputStream(fstream);
out.setOutput(stream, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
- out.startTag(null, "wp");
- out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
- out.attribute(null, "width", Integer.toString(wallpaper.width));
- out.attribute(null, "height", Integer.toString(wallpaper.height));
+ WallpaperData wallpaper;
- out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
- out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
- out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
- out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
-
- if (wallpaper.padding.left != 0) {
- out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+ wallpaper = mWallpaperMap.get(userId);
+ if (wallpaper != null) {
+ writeWallpaperAttributes(out, "wp", wallpaper);
}
- if (wallpaper.padding.top != 0) {
- out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+ wallpaper = mLockWallpaperMap.get(userId);
+ if (wallpaper != null) {
+ writeWallpaperAttributes(out, "kwp", wallpaper);
}
- if (wallpaper.padding.right != 0) {
- out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
- }
- if (wallpaper.padding.bottom != 0) {
- out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
- }
-
- out.attribute(null, "name", wallpaper.name);
- if (wallpaper.wallpaperComponent != null
- && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
- out.attribute(null, "component",
- wallpaper.wallpaperComponent.flattenToShortString());
- }
- out.endTag(null, "wp");
out.endDocument();
- stream.flush();
- FileUtils.sync(stream);
- stream.close();
+
+ stream.flush(); // also flushes fstream
+ FileUtils.sync(fstream);
+ stream.close(); // also closes fstream
journal.commit();
} catch (IOException e) {
IoUtils.closeQuietly(stream);
@@ -1539,6 +1527,40 @@
}
}
+ private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ out.startTag(null, tag);
+ out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
+ out.attribute(null, "width", Integer.toString(wallpaper.width));
+ out.attribute(null, "height", Integer.toString(wallpaper.height));
+
+ out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
+ out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
+ out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
+ out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
+
+ if (wallpaper.padding.left != 0) {
+ out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+ }
+ if (wallpaper.padding.top != 0) {
+ out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+ }
+ if (wallpaper.padding.right != 0) {
+ out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
+ }
+ if (wallpaper.padding.bottom != 0) {
+ out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
+ }
+
+ out.attribute(null, "name", wallpaper.name);
+ if (wallpaper.wallpaperComponent != null
+ && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
+ out.attribute(null, "component",
+ wallpaper.wallpaperComponent.flattenToShortString());
+ }
+ out.endTag(null, tag);
+ }
+
private void migrateFromOld() {
File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
@@ -1753,8 +1775,8 @@
WallpaperData wallpaper = null;
boolean success = false;
synchronized (mLock) {
- loadSettingsLocked(0);
- wallpaper = mWallpaperMap.get(0);
+ loadSettingsLocked(UserHandle.USER_SYSTEM);
+ wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
wallpaper.wallpaperId = makeWallpaperIdLocked(); // always bump id at restore
if (wallpaper.nextWallpaperComponent != null
&& !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
@@ -1788,11 +1810,11 @@
if (!success) {
Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
wallpaper.name = "";
- getWallpaperDir(0).delete();
+ getWallpaperDir(UserHandle.USER_SYSTEM).delete();
}
synchronized (mLock) {
- saveSettingsLocked(wallpaper);
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 12c62bd..f9e258d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -159,6 +160,25 @@
}
}
+ void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
+ firstWindowDrawn = true;
+
+ // We now have a good window to show, remove dead placeholders
+ removeAllDeadWindows();
+
+ if (startingData != null) {
+ if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, "Finish starting "
+ + win.mToken + ": first real window is shown, no animation");
+ // If this initial window is animating, stop it -- we will do an animation to reveal
+ // it from behind the starting window, so there is no need for it to also be doing its
+ // own stuff.
+ winAnimator.clearAnimation();
+ winAnimator.mService.mFinishedStarting.add(this);
+ winAnimator.mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+ }
+ updateReportedVisibilityLocked();
+ }
+
void updateReportedVisibilityLocked() {
if (appToken == null) {
return;
@@ -357,6 +377,9 @@
void notifyAppStopped() {
mAppStopped = true;
destroySurfaces();
+
+ // Remove any starting window that was added for this app if they are still around.
+ mTask.mService.scheduleRemoveStartingWindowLocked(this);
}
/**
@@ -594,6 +617,9 @@
if (paused) {
pw.print(prefix); pw.print("paused="); pw.println(paused);
}
+ if (mAppStopped) {
+ pw.print(prefix); pw.print("mAppStopped="); pw.println(mAppStopped);
+ }
if (numInterestingWindows != 0 || numDrawnWindows != 0
|| allDrawn || mAppAnimator.allDrawn) {
pw.print(prefix); pw.print("numInterestingWindows=");
@@ -619,7 +645,7 @@
pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
pw.print(" startingView="); pw.print(startingView);
pw.print(" startingDisplayed="); pw.print(startingDisplayed);
- pw.print(" startingMoved"); pw.println(startingMoved);
+ pw.print(" startingMoved="); pw.println(startingMoved);
}
if (!mFrozenBounds.isEmpty()) {
pw.print(prefix); pw.print("mFrozenBounds="); pw.print(mFrozenBounds);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index daeecfc..72621c2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -20,6 +20,7 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IActivityManager;
@@ -362,6 +363,7 @@
final WindowManagerPolicy mPolicy = new PhoneWindowManager();
final IActivityManager mActivityManager;
+ final ActivityManagerInternal mAmInternal;
final AppOpsManager mAppOps;
@@ -902,6 +904,7 @@
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
mActivityManager = ActivityManagerNative.getDefault();
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
AppOpsManager.OnOpChangedInternalListener opListener =
new AppOpsManager.OnOpChangedInternalListener() {
@@ -7606,6 +7609,9 @@
public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
+ public static final int NOTIFY_APP_TRANSITION_STARTING = 47;
+ public static final int NOTIFY_STARTING_WINDOW_DRAWN = 48;
+
/**
* Used to denote that an integer field in a message will not be used.
*/
@@ -8203,6 +8209,13 @@
token.clearTimedoutReplacesLocked();
}
}
+ case NOTIFY_APP_TRANSITION_STARTING: {
+ mAmInternal.notifyAppTransitionStarting(msg.arg1);
+ }
+ break;
+ case NOTIFY_STARTING_WINDOW_DRAWN: {
+ mAmInternal.notifyStartingWindowDrawn();
+ }
break;
}
if (DEBUG_WINDOW_TRACE) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 44ed7e2..40b6b50 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2347,7 +2347,8 @@
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
- pw.print(" hasSavedSurface()="); pw.println(hasSavedSurface());
+ pw.print(" hasSavedSurface()="); pw.print(hasSavedSurface());
+ pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed);
if (dumpAll) {
pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
pw.print(" last="); mLastFrame.printShortString(pw);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 2d8a4c9..c623047 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1522,23 +1522,7 @@
}
if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING && mWin.mAppToken != null) {
- mWin.mAppToken.firstWindowDrawn = true;
-
- // We now have a good window to show, remove dead placeholders
- mWin.mAppToken.removeAllDeadWindows();
-
- if (mWin.mAppToken.startingData != null) {
- if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, "Finish starting "
- + mWin.mToken + ": first real window is shown, no animation");
- // If this initial window is animating, stop it -- we
- // will do an animation to reveal it from behind the
- // starting window, so there is no need for it to also
- // be doing its own stuff.
- clearAnimation();
- mService.mFinishedStarting.add(mWin.mAppToken);
- mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
- }
- mWin.mAppToken.updateReportedVisibilityLocked();
+ mWin.mAppToken.onFirstWindowDrawn(mWin, this);
}
return true;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 0db6f3a..2972a24 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -62,7 +62,9 @@
// However, we need to somehow handle the situation where the cropping would completely hide
// the window. We achieve this by explicitly hiding the surface and not letting it be shown.
private boolean mHiddenForCrop = false;
- private boolean mHiddenForOtherReasons = false;
+
+ // Initially a surface is hidden after just being created.
+ private boolean mHiddenForOtherReasons = true;
private final String title;
public WindowSurfaceController(SurfaceSession s,
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index f705df8..856d30a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1,5 +1,9 @@
package com.android.server.wm;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_SAVED_SURFACE;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_STARTING_WINDOW;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
@@ -42,6 +46,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Debug;
+import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -782,6 +787,7 @@
}
}
} else if (w.isDrawnLw()) {
+ mService.mH.sendEmptyMessage(NOTIFY_STARTING_WINDOW_DRAWN);
atoken.startingDisplayed = true;
}
}
@@ -1262,6 +1268,7 @@
"Checking " + appsCount + " opening apps (frozen="
+ mService.mDisplayFrozen + " timeout="
+ mService.mAppTransition.isTimeout() + ")...");
+ int reason = APP_TRANSITION_TIMEOUT;
if (!mService.mAppTransition.isTimeout()) {
for (int i = 0; i < appsCount; i++) {
AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -1271,11 +1278,18 @@
+ wtoken.startingDisplayed + " startingMoved="
+ wtoken.startingMoved);
+ final boolean drawnBeforeRestoring = wtoken.allDrawn;
wtoken.restoreSavedSurfaces();
if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
return false;
}
+ if (wtoken.allDrawn) {
+ reason = drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
+ : APP_TRANSITION_SAVED_SURFACE;
+ } else {
+ reason = APP_TRANSITION_STARTING_WINDOW;
+ }
}
// We also need to wait for the specs to be fetched, if needed.
@@ -1285,9 +1299,15 @@
}
// If the wallpaper is visible, we need to check it's ready too.
- return !mWallpaperControllerLocked.isWallpaperVisible() ||
+ boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
mWallpaperControllerLocked.wallpaperTransitionReady();
+ if (wallpaperReady) {
+ mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reason, 0).sendToTarget();
+ return true;
+ }
+ return false;
}
+ mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reason, 0).sendToTarget();
return true;
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e39445a..c97323c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -29,10 +29,11 @@
#include "android_runtime/Log.h"
#include <arpa/inet.h>
-#include <string.h>
-#include <pthread.h>
+#include <limits>
#include <linux/in.h>
#include <linux/in6.h>
+#include <pthread.h>
+#include <string.h>
static jobject mCallbacksObj = NULL;
@@ -1090,11 +1091,37 @@
if (flags & (flag)) object.callSetter("set" # setter, (value))
static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
+ static uint32_t discontinuity_count_to_handle_old_lock_type = 0;
JavaObject object(env, "android/location/GnssClock");
GpsClockFlags flags = clock->flags;
SET_IF(GNSS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
- SET(Type, clock->type);
+
+ // GnssClock only supports the more effective HW_CLOCK type, so type
+ // handling and documentation complexity has been removed. To convert the
+ // old GPS_CLOCK types (active only in a limited number of older devices),
+ // the GPS time information is handled as an always discontinuous HW clock,
+ // with the GPS time information put into the full_bias_ns instead - so that
+ // time_ns + full_bias_ns = local estimate of GPS time (as remains true, in
+ // the new GnssClock struct.)
+ switch (clock->type) {
+ case GPS_CLOCK_TYPE_UNKNOWN:
+ // Clock type unsupported.
+ ALOGE("Unknown clock type provided.");
+ break;
+ case GPS_CLOCK_TYPE_LOCAL_HW_TIME:
+ // Already local hardware time. No need to do anything.
+ break;
+ case GPS_CLOCK_TYPE_GPS_TIME:
+ // GPS time, need to convert.
+ flags |= GNSS_CLOCK_HAS_FULL_BIAS;
+ clock->full_bias_ns = clock->time_ns;
+ clock->time_ns = 0;
+ SET(HardwareClockDiscontinuityCount,
+ discontinuity_count_to_handle_old_lock_type++);
+ break;
+ }
+
SET(TimeInNs, clock->time_ns);
SET_IF(GNSS_CLOCK_HAS_TIME_UNCERTAINTY,
TimeUncertaintyInNs,
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index f1fe346..32501ad 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -15,12 +15,6 @@
*/
package com.android.server.notification;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_LOW;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
-
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3ad7d34..1278c07 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -733,9 +733,28 @@
* @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
* @return A {@link PersistableBundle} containing the config for the given subId, or default
* values for an invalid subId.
+ *
+ * @deprecated use getConfig.
*/
@Nullable
public PersistableBundle getConfigForSubId(int subId) {
+ return getConfig(subId);
+ }
+
+ /**
+ * Gets the configuration values for a particular subscription, which is associated with a
+ * specific SIM card. If an invalid subId is used, the returned config will contain default
+ * values.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ *
+ * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+ * @return A {@link PersistableBundle} containing the config for the given subId, or default
+ * values for an invalid subId.
+ */
+ @Nullable
+ public PersistableBundle getConfig(int subId) {
try {
ICarrierConfigLoader loader = getICarrierConfigLoader();
if (loader == null) {
@@ -757,11 +776,32 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*
- * @see #getConfigForSubId
+ * @return A {@link PersistableBundle} containing the config for the default subscription.
*/
@Nullable
public PersistableBundle getConfig() {
- return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
+ return getConfig(SubscriptionManager.getDefaultSubscriptionId());
+ }
+
+ /**
+ * Calling this method triggers telephony services to fetch the current carrier configuration.
+ * <p>
+ * Normally this does not need to be called because the platform reloads config on its own.
+ * This should be called by a carrier service app if it wants to update config at an arbitrary
+ * moment.
+ * </p>
+ * <p>Requires that the calling app has carrier privileges.
+ * @see #hasCarrierPrivileges
+ * <p>
+ * This method returns before the reload has completed, and
+ * {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an
+ * arbitrary thread.
+ * </p>
+ *
+ * @deprecated use notifyConfigChanged.
+ */
+ public void notifyConfigChangedForSubId(int subId) {
+ notifyConfigChanged(subId);
}
/**
@@ -779,7 +819,7 @@
* arbitrary thread.
* </p>
*/
- public void notifyConfigChangedForSubId(int subId) {
+ public void notifyConfigChanged(int subId) {
try {
ICarrierConfigLoader loader = getICarrierConfigLoader();
if (loader == null) {
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 4aa5b98..558c1dc 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -241,6 +241,16 @@
public static final int CODE_ANSWERED_ELSEWHERE = 1014;
/**
+ * Call pull request failure from the network.
+ */
+ public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
+
+ /**
+ * Call ended due to being pulled onto another device.
+ */
+ public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
+
+ /**
* Supplementary services (HOLD/RESUME) failure error codes.
* Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
*/
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 69259d0..04cb1f2 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -18,6 +18,8 @@
import com.android.ims.ImsReasonInfo;
+import android.net.Uri;
+
/**
* A listener type for receiving notifications about the changes to
* the IMS connection(registration).
@@ -100,4 +102,9 @@
* @param count The number of waiting voice messages.
*/
void voiceMessageCountUpdate(int count);
+
+ /**
+ * Notifies the application when the list of URIs associated with IMS client is updated.
+ */
+ void registrationAssociatedUriChanged(in Uri[] uris);
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 8b8d604..4bed941 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -60,6 +60,8 @@
// optional parameter: comma separated list of required account types before proceeding
// with the app launch
private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
+ private static final String WEARABLE_ACTION_GOOGLE =
+ "com.google.android.wearable.action.GOOGLE";
private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 7500; //7.5s to allow app to idle
private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps
@@ -183,6 +185,13 @@
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
+ resolveLoop(ris, intentToResolve, pm);
+ intentToResolve = new Intent(WEARABLE_ACTION_GOOGLE);
+ ris = pm.queryIntentActivities(intentToResolve, 0);
+ resolveLoop(ris, intentToResolve, pm);
+ }
+
+ private void resolveLoop(List<ResolveInfo> ris, Intent intentToResolve, PackageManager pm) {
if (ris == null || ris.isEmpty()) {
Log.i(TAG, "Could not find any apps");
} else {
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 9ac4dbf..2a3f143 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -86,6 +86,122 @@
}
private Test[] mTests = new Test[] {
+ new Test("Min priority") {
+ public void run()
+ {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle("Min priority")
+ .setLights(0xff0000ff, 1, 0)
+ .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+ .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+ getPackageName() + "/raw/ringer"))
+ .setPriority(Notification.PRIORITY_MIN)
+ .setFullScreenIntent(makeIntent2(), false)
+ .build();
+ mNM.notify(7000, n);
+ }
+ },
+ new Test("Min priority, high pri flag") {
+ public void run()
+ {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle("Min priority, high pri flag")
+ .setLights(0xff0000ff, 1, 0)
+ .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+ .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+ getPackageName() + "/raw/ringer"))
+ .setPriority(Notification.PRIORITY_MIN)
+ .setFullScreenIntent(makeIntent2(), true)
+ .build();
+ mNM.notify(7001, n);
+ }
+ },
+ new Test("Low priority") {
+ public void run()
+ {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle("Low priority")
+ .setLights(0xff0000ff, 1, 0)
+ .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+ .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+ getPackageName() + "/raw/ringer"))
+ .setPriority(Notification.PRIORITY_LOW)
+ .setFullScreenIntent(makeIntent2(), false)
+ .build();
+ mNM.notify(7002, n);
+ }
+ },
+ new Test("Default priority") {
+ public void run()
+ {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle("Default priority")
+ .setLights(0xff0000ff, 1, 0)
+ .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+ .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+ getPackageName() + "/raw/ringer"))
+ .setPriority(Notification.PRIORITY_DEFAULT)
+ .setFullScreenIntent(makeIntent2(), false)
+ .build();
+ mNM.notify(7004, n);
+ }
+ },
+ new Test("High priority") {
+ public void run()
+ {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle("High priority")
+ .setLights(0xff0000ff, 1, 0)
+ .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+ .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+ getPackageName() + "/raw/ringer"))
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setFullScreenIntent(makeIntent2(), false)
+ .build();
+ mNM.notify(7006, n);
+ }
+ },
+ new Test("Max priority") {
+ public void run()
+ {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle("Max priority")
+ .setLights(0xff0000ff, 1, 0)
+ .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+ .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+ getPackageName() + "/raw/ringer"))
+ .setPriority(Notification.PRIORITY_MAX)
+ .setFullScreenIntent(makeIntent2(), false)
+ .build();
+ mNM.notify(7008, n);
+ }
+ },
+ new Test("Max priority with delay") {
+ public void run()
+ {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle("Max priority")
+ .setLights(0xff0000ff, 1, 0)
+ .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+ .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+ getPackageName() + "/raw/ringer"))
+ .setPriority(Notification.PRIORITY_MAX)
+ .setFullScreenIntent(makeIntent2(), false)
+ .build();
+ mNM.notify(7008, n);
+ }
+ },
new Test("Off") {
public void run() {
PowerManager pm = (PowerManager)NotificationTestList.this.getSystemService(Context.POWER_SERVICE);
diff --git a/tests/UiBench/res/layout/activity_transition.xml b/tests/UiBench/res/layout/activity_transition.xml
index d4c6610..4556b02 100644
--- a/tests/UiBench/res/layout/activity_transition.xml
+++ b/tests/UiBench/res/layout/activity_transition.xml
@@ -15,6 +15,7 @@
~ limitations under the License
-->
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/transition_grid_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="true"
@@ -25,8 +26,6 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="centerCrop"
- android:layout_column="0"
- android:layout_row="0"
android:src="@drawable/ducky"
android:onClick="clicked"
android:transitionName="ducky"/>
@@ -36,8 +35,6 @@
android:layout_width="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/woot"
- android:layout_column="1"
- android:layout_row="0"
android:onClick="clicked"
android:transitionName="woot"/>
<ImageView
@@ -46,8 +43,6 @@
android:layout_width="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/ball"
- android:layout_column="0"
- android:layout_row="1"
android:onClick="clicked"
android:transitionName="ball"/>
<ImageView
@@ -56,8 +51,6 @@
android:layout_width="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/block"
- android:layout_column="1"
- android:layout_row="1"
android:onClick="clicked"
android:transitionName="block"/>
<ImageView
@@ -66,8 +59,6 @@
android:layout_width="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/jellies"
- android:layout_column="0"
- android:layout_row="2"
android:onClick="clicked"
android:transitionName="jellies"/>
<ImageView
@@ -76,8 +67,6 @@
android:layout_width="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/mug"
- android:layout_column="1"
- android:layout_row="2"
android:onClick="clicked"
android:transitionName="mug"/>
<ImageView
@@ -86,8 +75,6 @@
android:layout_width="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/pencil"
- android:layout_column="0"
- android:layout_row="3"
android:onClick="clicked"
android:transitionName="pencil"/>
<ImageView
@@ -96,8 +83,6 @@
android:layout_width="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/scissors"
- android:layout_column="1"
- android:layout_row="3"
android:onClick="clicked"
android:transitionName="scissors"/>
</GridLayout>
\ No newline at end of file
diff --git a/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java b/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java
index 1106a13..0a069c2 100644
--- a/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java
+++ b/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java
@@ -18,11 +18,13 @@
import android.app.ActivityOptions;
import android.app.SharedElementCallback;
import android.content.Intent;
+import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
+import android.widget.GridLayout;
import android.widget.ImageView;
import java.util.List;
@@ -90,6 +92,13 @@
getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
setContentView(R.layout.activity_transition);
setupHero();
+
+ // Ensure that all images are visible regardless of orientation.
+ GridLayout gridLayout = (GridLayout) findViewById(R.id.transition_grid_layout);
+ boolean isPortrait =
+ getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+ gridLayout.setRowCount(isPortrait ? 4 : 2);
+ gridLayout.setColumnCount(isPortrait ? 2 : 4);
}
private void setupHero() {
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index ed12bdf..a46aaec 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -160,21 +160,6 @@
}
}
- /** @hide */
- public static final int ENABLED = 0;
- /** @hide */
- public static final int AUTO_ROAM_DISABLED = 16;
- /** @hide */
- public static final int AUTO_JOIN_DISABLED = 32;
- /** @hide */
- public static final int AUTHENTICATION_ERROR = 128;
-
- /**
- * Status: indicating join status
- * @hide
- */
- public int autoJoinStatus;
-
/**
* num IP configuration failures
* @hide
@@ -187,17 +172,6 @@
*/
public long blackListTimestamp;
- /** @hide **/
- public void setAutoJoinStatus(int status) {
- if (status < 0) status = 0;
- if (status == 0) {
- blackListTimestamp = 0;
- } else if (status > autoJoinStatus) {
- blackListTimestamp = System.currentTimeMillis();
- }
- autoJoinStatus = status;
- }
-
/**
* Status: indicating the scan result is not a result
* that is part of user's saved configurations
@@ -462,7 +436,6 @@
distanceCm = source.distanceCm;
distanceSdCm = source.distanceSdCm;
seen = source.seen;
- autoJoinStatus = source.autoJoinStatus;
untrusted = source.untrusted;
numConnection = source.numConnection;
numUsage = source.numUsage;
@@ -506,9 +479,6 @@
sb.append(", passpoint: ");
sb.append(((flags & FLAG_PASSPOINT_NETWORK) != 0) ? "yes" : "no");
- if (autoJoinStatus != 0) {
- sb.append(", status: ").append(autoJoinStatus);
- }
sb.append(", ChannelBandwidth: ").append(channelWidth);
sb.append(", centerFreq0: ").append(centerFreq0);
sb.append(", centerFreq1: ").append(centerFreq1);
@@ -544,7 +514,6 @@
dest.writeInt(centerFreq0);
dest.writeInt(centerFreq1);
dest.writeLong(seen);
- dest.writeInt(autoJoinStatus);
dest.writeInt(untrusted ? 1 : 0);
dest.writeInt(numConnection);
dest.writeInt(numUsage);
@@ -615,7 +584,6 @@
);
sr.seen = in.readLong();
- sr.autoJoinStatus = in.readInt();
sr.untrusted = in.readInt() != 0;
sr.numConnection = in.readInt();
sr.numUsage = in.readInt();