Merge "Fix a bug of not deleting previously added config files on disk"
diff --git a/Android.bp b/Android.bp
index 05fb3c0..03abf75 100644
--- a/Android.bp
+++ b/Android.bp
@@ -425,6 +425,7 @@
"media/java/android/media/IAudioFocusDispatcher.aidl",
"media/java/android/media/IAudioRoutesObserver.aidl",
"media/java/android/media/IAudioService.aidl",
+ "media/java/android/media/IAudioServerStateDispatcher.aidl",
"media/java/android/media/IMediaHTTPConnection.aidl",
"media/java/android/media/IMediaHTTPService.aidl",
"media/java/android/media/IMediaResourceMonitor.aidl",
diff --git a/Android.mk b/Android.mk
index f420820..3b8d6a8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -804,7 +804,7 @@
-Iexternal/protobuf/src
LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \
store_unknown_fields = true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart
+LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto/android/os)
diff --git a/api/current.txt b/api/current.txt
index 3edd2c0..63a40e5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -40731,8 +40731,8 @@
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConnectionCapabilities(int);
- method public final void setConnectionElapsedTime(long);
method public final void setConnectionProperties(int);
+ method public final void setConnectionStartElapsedRealTime(long);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
diff --git a/api/system-current.txt b/api/system-current.txt
index 953941f..39cbe90 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -437,6 +437,19 @@
field public static final int STATE_USER_UNMANAGED = 0; // 0x0
}
+ public class SystemUpdatePolicy implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR;
+ field public static final int TYPE_PAUSE = 4; // 0x4
+ }
+
+ public static class SystemUpdatePolicy.InstallationOption {
+ method public long getEffectiveTime();
+ method public int getType();
+ }
+
}
package android.app.backup {
@@ -2524,12 +2537,15 @@
public class AudioManager {
method public deprecated int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
+ method public void clearAudioServerStateCallback();
method public int dispatchAudioFocusChange(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy);
+ method public boolean isAudioServerRunning();
method public boolean isHdmiSystemAudioSupported();
method public int registerAudioPolicy(android.media.audiopolicy.AudioPolicy);
method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
method public deprecated int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
method public int requestAudioFocus(android.media.AudioFocusRequest, android.media.audiopolicy.AudioPolicy);
+ method public void setAudioServerStateCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioServerStateCallback);
method public void setFocusRequestResult(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy);
method public void unregisterAudioPolicyAsync(android.media.audiopolicy.AudioPolicy);
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
@@ -2537,6 +2553,12 @@
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
}
+ public static abstract class AudioManager.AudioServerStateCallback {
+ ctor public AudioManager.AudioServerStateCallback();
+ method public void onAudioServerDown();
+ method public void onAudioServerUp();
+ }
+
public final class AudioPlaybackConfiguration implements android.os.Parcelable {
method public int getClientPid();
method public int getClientUid();
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index ce3a4b9..45fb5da 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -268,7 +268,9 @@
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
- if (value.mValue.getType() == INT) {
+ if (value.mValue.getType() == LONG) {
+ return value.mValue.long_value;
+ } else if (value.mValue.getType() == INT) {
return value.mValue.int_value;
} else {
*err = BAD_TYPE;
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 293b1a8..cb731c5 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -59,7 +59,6 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t durationTimeNs = 2 * 1000;
@@ -95,7 +94,6 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -129,7 +127,6 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -162,7 +159,6 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t durationTimeNs = 2 * 1000;
@@ -210,7 +206,6 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t durationTimeNs = 2 * 1000;
@@ -253,7 +248,6 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t durationTimeNs = 2 * 1000;
@@ -296,7 +290,6 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -338,7 +331,6 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
- uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
@@ -408,7 +400,6 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
- uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
@@ -421,15 +412,15 @@
tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
EXPECT_TRUE(tracker.mStarted.empty());
- EXPECT_EQ(10LL, tracker.mDuration);
+ EXPECT_EQ(10LL, tracker.mDuration); // 10ns
EXPECT_EQ(0u, tracker.mStarted.size());
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey());
EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
- EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
+ EXPECT_EQ((long long)(52ULL * NS_PER_SEC), // (10s + 1s + 1ns + 20ns) - 10ns + 40s, rounded up
(long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
- // The alarm is set to fire at 51s, and when it does, an anomaly would be declared. However,
+ // The alarm is set to fire at 52s, and when it does, an anomaly would be declared. However,
// because this is a unit test, the alarm won't actually fire at all. Since the alarm fails
// to fire in time, the anomaly is instead caught when noteStop is called, at around 71s.
tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets);
@@ -460,7 +451,6 @@
ConditionKey conkey;
conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
- uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
diff --git a/core/java/android/app/admin/FreezeInterval.java b/core/java/android/app/admin/FreezeInterval.java
index 7acdfc8..de5e21a 100644
--- a/core/java/android/app/admin/FreezeInterval.java
+++ b/core/java/android/app/admin/FreezeInterval.java
@@ -84,6 +84,10 @@
}
}
+ boolean after(LocalDate localDate) {
+ return mStartDay > dayOfYearDisregardLeapYear(localDate);
+ }
+
/**
* Instantiate the current interval to real calendar dates, given a calendar date
* {@code now}. If the interval contains now, the returned calendar dates should be the
@@ -161,7 +165,7 @@
* 3. At most one wrapped Interval remains, and it will be at the end of the list
* @hide
*/
- private static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) {
+ protected static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) {
boolean[] taken = new boolean[DAYS_IN_YEAR];
// First convert the intervals into flat array
for (FreezeInterval interval : intervals) {
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index 05d3fd9..47b3a81 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -21,6 +21,7 @@
import static org.xmlpull.v1.XmlPullParser.TEXT;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -33,9 +34,15 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -103,6 +110,19 @@
*/
public static final int TYPE_POSTPONE = 3;
+ /**
+ * Incoming system updates (including security updates) should be blocked. This flag is not
+ * exposed to third-party apps (and any attempt to set it will raise exceptions). This is used
+ * to represent the current installation option type to the privileged system update clients,
+ * for example to indicate OTA freeze is currently in place or when system is outside a daily
+ * maintenance window.
+ *
+ * @see InstallationOption
+ * @hide
+ */
+ @SystemApi
+ public static final int TYPE_PAUSE = 4;
+
private static final String KEY_POLICY_TYPE = "policy_type";
private static final String KEY_INSTALL_WINDOW_START = "install_window_start";
private static final String KEY_INSTALL_WINDOW_END = "install_window_end";
@@ -460,6 +480,30 @@
return null;
}
+ /**
+ * Returns time (in milliseconds) until the start of the next freeze period, assuming now
+ * is not within a freeze period.
+ */
+ private long timeUntilNextFreezePeriod(long now) {
+ List<FreezeInterval> sortedPeriods = FreezeInterval.canonicalizeIntervals(mFreezePeriods);
+ LocalDate nowDate = millisToDate(now);
+ LocalDate nextFreezeStart = null;
+ for (FreezeInterval interval : sortedPeriods) {
+ if (interval.after(nowDate)) {
+ nextFreezeStart = interval.toCurrentOrFutureRealDates(nowDate).first;
+ break;
+ } else if (interval.contains(nowDate)) {
+ throw new IllegalArgumentException("Given date is inside a freeze period");
+ }
+ }
+ if (nextFreezeStart == null) {
+ // If no interval is after now, then it must be the one that starts at the beginning
+ // of next year
+ nextFreezeStart = sortedPeriods.get(0).toCurrentOrFutureRealDates(nowDate).first;
+ }
+ return dateToMillis(nextFreezeStart) - now;
+ }
+
/** @hide */
public void validateFreezePeriods() {
FreezeInterval.validatePeriods(mFreezePeriods);
@@ -472,6 +516,134 @@
prevPeriodEnd, now);
}
+ /**
+ * An installation option represents how system update clients should act on incoming system
+ * updates and how long this action is valid for, given the current system update policy. Its
+ * action could be one of the following
+ * <ul>
+ * <li> {@code TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and without
+ * user intervention as soon as they become available.
+ * <li> {@code TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
+ * <li> {@code TYPE_PAUSE} system updates should be postponed indefinitely until further notice
+ * </ul>
+ *
+ * The effective time measures how long this installation option is valid for from the queried
+ * time, in milliseconds.
+ *
+ * This is an internal API for system update clients.
+ * @hide
+ */
+ @SystemApi
+ public static class InstallationOption {
+ private final int mType;
+ private long mEffectiveTime;
+
+ InstallationOption(int type, long effectiveTime) {
+ this.mType = type;
+ this.mEffectiveTime = effectiveTime;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public long getEffectiveTime() {
+ return mEffectiveTime;
+ }
+
+ /** @hide */
+ protected void limitEffectiveTime(long otherTime) {
+ mEffectiveTime = Long.min(mEffectiveTime, otherTime);
+ }
+ }
+
+ /**
+ * Returns the installation option at the specified time, under the current
+ * {@code SystemUpdatePolicy} object. This is a convenience method for system update clients
+ * so they can instantiate this policy at any given time and find out what to do with incoming
+ * system updates, without the need of examining the overall policy structure.
+ *
+ * Normally the system update clients will query the current installation option by calling this
+ * method with the current timestamp, and act on the returned option until its effective time
+ * lapses. It can then query the latest option using a new timestamp. It should also listen
+ * for {@code DevicePolicyManager#ACTION_SYSTEM_UPDATE_POLICY_CHANGED} broadcast, in case the
+ * whole policy is updated.
+ *
+ * @param when At what time the intallation option is being queried, specified in number of
+ milliseonds since the epoch.
+ * @see InstallationOption
+ * @hide
+ */
+ @SystemApi
+ public InstallationOption getInstallationOptionAt(long when) {
+ LocalDate whenDate = millisToDate(when);
+ Pair<LocalDate, LocalDate> current = getCurrentFreezePeriod(whenDate);
+ if (current != null) {
+ return new InstallationOption(TYPE_PAUSE,
+ dateToMillis(roundUpLeapDay(current.second).plusDays(1)) - when);
+ }
+ // We are not within a freeze period, query the underlying policy.
+ // But also consider the start of the next freeze period, which might
+ // reduce the effective time of the current installation option
+ InstallationOption option = getInstallationOptionRegardlessFreezeAt(when);
+ if (mFreezePeriods.size() > 0) {
+ option.limitEffectiveTime(timeUntilNextFreezePeriod(when));
+ }
+ return option;
+ }
+
+ private InstallationOption getInstallationOptionRegardlessFreezeAt(long when) {
+ if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
+ return new InstallationOption(mPolicyType, Long.MAX_VALUE);
+ } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+ Calendar query = Calendar.getInstance();
+ query.setTimeInMillis(when);
+ // Calculate the number of milliseconds since midnight of the time specified by when
+ long whenMillis = TimeUnit.HOURS.toMillis(query.get(Calendar.HOUR_OF_DAY))
+ + TimeUnit.MINUTES.toMillis(query.get(Calendar.MINUTE))
+ + TimeUnit.SECONDS.toMillis(query.get(Calendar.SECOND))
+ + query.get(Calendar.MILLISECOND);
+ long windowStartMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowStart);
+ long windowEndMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowEnd);
+ final long dayInMillis = TimeUnit.DAYS.toMillis(1);
+
+ if ((windowStartMillis <= whenMillis && whenMillis <= windowEndMillis)
+ || ((windowStartMillis > windowEndMillis)
+ && (windowStartMillis <= whenMillis || whenMillis <= windowEndMillis))) {
+ return new InstallationOption(TYPE_INSTALL_AUTOMATIC,
+ (windowEndMillis - whenMillis + dayInMillis) % dayInMillis);
+ } else {
+ return new InstallationOption(TYPE_PAUSE,
+ (windowStartMillis - whenMillis + dayInMillis) % dayInMillis);
+ }
+ } else {
+ throw new RuntimeException("Unknown policy type");
+ }
+ }
+
+ private static LocalDate roundUpLeapDay(LocalDate date) {
+ if (date.isLeapYear() && date.getMonthValue() == 2 && date.getDayOfMonth() == 28) {
+ return date.plusDays(1);
+ } else {
+ return date;
+ }
+ }
+
+ /** Convert a timestamp since epoch to a LocalDate using default timezone, truncating
+ * the hour/min/seconds part.
+ */
+ private static LocalDate millisToDate(long when) {
+ return Instant.ofEpochMilli(when).atZone(ZoneId.systemDefault()).toLocalDate();
+ }
+
+ /**
+ * Returns the timestamp since epoch of a LocalDate, assuming the time is 00:00:00.
+ */
+ private static long dateToMillis(LocalDate when) {
+ return LocalDateTime.of(when, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant()
+ .toEpochMilli();
+ }
+
@Override
public String toString() {
return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d, "
@@ -480,11 +652,13 @@
mFreezePeriods.stream().map(n -> n.toString()).collect(Collectors.joining(",")));
}
+ @SystemApi
@Override
public int describeContents() {
return 0;
}
+ @SystemApi
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mPolicyType);
@@ -499,6 +673,7 @@
}
}
+ @SystemApi
public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
new Parcelable.Creator<SystemUpdatePolicy>() {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index aa8faf8..07a9911 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2070,6 +2070,15 @@
"android.hardware.sensor.hifi_sensors";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports a hardware mechanism for invoking an assist gesture.
+ * @see android.provider.Settings.Secure#ASSIST_GESTURE_ENABLED
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_ASSIST_GESTURE = "android.hardware.sensor.assist";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a telephony radio with data
* communication support.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fef6495..8ab8361 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12134,6 +12134,12 @@
* @hide
*/
public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
+
+ /**
+ * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
+ * @hide
+ */
+ public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
}
/**
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index d66322c..171d4d9 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -94,7 +94,7 @@
private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true;
private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
- private static final int XML_VERSION = 2;
+ public static final int XML_VERSION = 3;
public static final String ZEN_TAG = "zen";
private static final String ZEN_ATT_VERSION = "version";
private static final String ZEN_ATT_USER = "user";
@@ -145,6 +145,7 @@
public int user = UserHandle.USER_SYSTEM;
public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
+ public int version;
public ZenRule manualRule;
public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
@@ -431,6 +432,7 @@
String tag = parser.getName();
if (!ZEN_TAG.equals(tag)) return null;
final ZenModeConfig rt = new ZenModeConfig();
+ rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index bb9e391..7bae28a 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -21,6 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pools;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.util.ArrayList;
import java.util.List;
@@ -46,7 +47,7 @@
public final Rect boundsInScreen = new Rect();
public List<IBinder> childTokens;
public CharSequence title;
- public int accessibilityIdOfAnchor = View.NO_ID;
+ public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
public boolean inPictureInPicture;
private WindowInfo() {
@@ -105,7 +106,7 @@
parcel.writeInt(focused ? 1 : 0);
boundsInScreen.writeToParcel(parcel, flags);
parcel.writeCharSequence(title);
- parcel.writeInt(accessibilityIdOfAnchor);
+ parcel.writeLong(accessibilityIdOfAnchor);
parcel.writeInt(inPictureInPicture ? 1 : 0);
if (childTokens != null && !childTokens.isEmpty()) {
@@ -142,7 +143,7 @@
focused = (parcel.readInt() == 1);
boundsInScreen.readFromParcel(parcel);
title = parcel.readCharSequence();
- accessibilityIdOfAnchor = parcel.readInt();
+ accessibilityIdOfAnchor = parcel.readLong();
inPictureInPicture = (parcel.readInt() == 1);
final boolean hasChildren = (parcel.readInt() == 1);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1c5e871..c0a9666 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -63,6 +63,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2344,7 +2345,7 @@
*
* @hide
*/
- public int accessibilityIdOfAnchor = -1;
+ public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
/**
* The window title isn't kept in sync with what is displayed in the title bar, so we
@@ -2538,7 +2539,7 @@
out.writeInt(hasManualSurfaceInsets ? 1 : 0);
out.writeInt(preservePreviousSurfaceInsets ? 1 : 0);
out.writeInt(needsMenuKey);
- out.writeInt(accessibilityIdOfAnchor);
+ out.writeLong(accessibilityIdOfAnchor);
TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
out.writeInt(mColorMode);
out.writeLong(hideTimeoutMilliseconds);
@@ -2594,7 +2595,7 @@
hasManualSurfaceInsets = in.readInt() != 0;
preservePreviousSurfaceInsets = in.readInt() != 0;
needsMenuKey = in.readInt();
- accessibilityIdOfAnchor = in.readInt();
+ accessibilityIdOfAnchor = in.readLong();
accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mColorMode = in.readInt();
hideTimeoutMilliseconds = in.readLong();
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 4a181b2..44adbb2 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -49,6 +49,7 @@
public static String USB = "USB";
public static String FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
+ public static String SYSTEM_CHANGES = "SYSTEM_CHANGES";
public static void createAll(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -152,6 +153,11 @@
.build());
channelsList.add(heavyWeightChannel);
+ NotificationChannel systemChanges = new NotificationChannel(SYSTEM_CHANGES,
+ context.getString(R.string.notification_channel_system_changes),
+ NotificationManager.IMPORTANCE_LOW);
+ channelsList.add(systemChanges);
+
nm.createNotificationChannels(channelsList);
}
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
index 65615c0..444049e 100644
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -78,10 +78,11 @@
final long userTimeUs = Long.parseLong(splitter.next(), 10);
final long systemTimeUs = Long.parseLong(splitter.next(), 10);
+ boolean notifyCallback = false;
+ long userTimeDeltaUs = userTimeUs;
+ long systemTimeDeltaUs = systemTimeUs;
// Only report if there is a callback and if this is not the first read.
if (callback != null && mLastTimeReadUs != 0) {
- long userTimeDeltaUs = userTimeUs;
- long systemTimeDeltaUs = systemTimeUs;
int index = mLastUserTimeUs.indexOfKey(uid);
if (index >= 0) {
userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
@@ -114,12 +115,13 @@
}
}
- if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) {
- callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
- }
+ notifyCallback = (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0);
}
mLastUserTimeUs.put(uid, userTimeUs);
mLastSystemTimeUs.put(uid, systemTimeUs);
+ if (notifyCallback) {
+ callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
+ }
}
} catch (IOException e) {
Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e69a360..fac6b23 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -53,8 +53,8 @@
public static final int DISABLE_VERIFIER = 1 << 9;
/** Only use oat files located in /system. Otherwise use dex/jar/apk . */
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
- /** Do not enfore hidden API access restrictions. */
- public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+ /** Do enfore hidden API access restrictions. */
+ public static final int ENABLE_HIDDEN_API_CHECKS = 1 << 11;
/** Force generation of native debugging information for backtraces. */
public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
@@ -160,9 +160,6 @@
*/
public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
- // SystemServer is always allowed to use hidden APIs.
- runtimeFlags |= DISABLE_HIDDEN_API_CHECKS;
-
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 74802c8..9c89976 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -98,10 +98,6 @@
private static final String SOCKET_NAME_ARG = "--socket-name=";
- /* Dexopt flag to disable hidden API access checks when dexopting SystemServer.
- * Must be kept in sync with com.android.server.pm.Installer. */
- private static final int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10;
-
/**
* Used to pre-load resources.
*/
@@ -569,10 +565,7 @@
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
final String packageName = "*";
final String outputPath = null;
- // Dexopt with a flag which lifts restrictions on hidden API usage.
- // Offending methods would otherwise be re-verified at runtime and
- // we want to avoid the performance overhead of that.
- final int dexFlags = DEXOPT_DISABLE_HIDDEN_API_CHECKS;
+ final int dexFlags = 0;
final String compilerFilter = systemServerFilter;
final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
final String seInfo = null;
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index f2b7ab2..52ee9e8 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -426,10 +426,11 @@
optional SettingProto show_first_crash_dialog = 349 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto show_restart_in_crash_dialog = 351 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto show_mute_in_crash_dialog = 352 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingsProto show_zen_upgrade_notification = 354 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Please insert fields in the same order as in
// frameworks/base/core/java/android/provider/Settings.java.
- // Next tag = 354;
+ // Next tag = 355;
}
message SecureSettingsProto {
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ec81df7..0efb6f9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4854,4 +4854,11 @@
<!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) -->
<string name="screenshot_edit">Edit</string>
+
+ <!-- Title for the notification channel notifying user of settings system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_system_changes">System changes</string>
+ <!-- Title of notification indicating do not disturb settings have changed when upgrading to P -->
+ <string name="zen_upgrade_notification_title">Do Not Disturb has changed</string>
+ <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
+ <string name="zen_upgrade_notification_content">Tap to check your behavior settings for interruptions</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index af25398..5a9dc7f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1716,6 +1716,7 @@
<java-symbol type="string" name="bugreport_status" />
<java-symbol type="string" name="bugreport_title" />
<java-symbol type="string" name="faceunlock_multiple_failures" />
+ <java-symbol type="string" name="global_actions" />
<java-symbol type="string" name="global_action_power_off" />
<java-symbol type="string" name="global_action_restart" />
<java-symbol type="string" name="global_actions_airplane_mode_off_status" />
@@ -3102,6 +3103,7 @@
<java-symbol type="string" name="notification_channel_retail_mode" />
<java-symbol type="string" name="notification_channel_usb" />
<java-symbol type="string" name="notification_channel_heavy_weight_app" />
+ <java-symbol type="string" name="notification_channel_system_changes" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierService" />
@@ -3257,4 +3259,7 @@
<!-- For Wear devices -->
<java-symbol type="array" name="config_wearActivityModeRadios" />
+
+ <java-symbol type="string" name="zen_upgrade_notification_title" />
+ <java-symbol type="string" name="zen_upgrade_notification_content" />
</resources>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index a446088..7849a2a 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,7 +18,7 @@
-->
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
-<zen version="2">
+<zen version="3">
<allow alarms="true" media_system_other="true" calls="false" messages="false" reminders="false"
events="false" />
</zen>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a0b6297..c36026c 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -354,6 +354,7 @@
Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
Settings.Global.SHOW_TEMPERATURE_WARNING,
+ Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION,
Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 0be54ec..9ff964b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -65,6 +66,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
/**
* AudioManager provides access to volume and ringer mode control.
@@ -4864,6 +4867,114 @@
}
}
+
+ /**
+ * @hide
+ * Abstract class to receive event notification about audioserver process state.
+ */
+ @SystemApi
+ public abstract static class AudioServerStateCallback {
+ public void onAudioServerDown() { }
+ public void onAudioServerUp() { }
+ }
+
+ private Executor mAudioServerStateExec;
+ private AudioServerStateCallback mAudioServerStateCb;
+ private final Object mAudioServerStateCbLock = new Object();
+
+ private final IAudioServerStateDispatcher mAudioServerStateDispatcher =
+ new IAudioServerStateDispatcher.Stub() {
+ @Override
+ public void dispatchAudioServerStateChange(boolean state) {
+ Executor exec;
+ AudioServerStateCallback cb;
+
+ synchronized (mAudioServerStateCbLock) {
+ exec = mAudioServerStateExec;
+ cb = mAudioServerStateCb;
+ }
+
+ if ((exec == null) || (cb == null)) {
+ return;
+ }
+ if (state) {
+ exec.execute(() -> cb.onAudioServerUp());
+ } else {
+ exec.execute(() -> cb.onAudioServerDown());
+ }
+ }
+ };
+
+ /**
+ * @hide
+ * Registers a callback for notification of audio server state changes.
+ * @param executor {@link Executor} to handle the callbacks
+ * @param stateCallback the callback to receive the audio server state changes
+ * To remove the callabck, pass a null reference for both executor and stateCallback.
+ */
+ @SystemApi
+ public void setAudioServerStateCallback(@NonNull Executor executor,
+ @NonNull AudioServerStateCallback stateCallback) {
+ if (stateCallback == null) {
+ throw new IllegalArgumentException("Illegal null AudioServerStateCallback");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException(
+ "Illegal null Executor for the AudioServerStateCallback");
+ }
+
+ synchronized (mAudioServerStateCbLock) {
+ if (mAudioServerStateCb != null) {
+ throw new IllegalStateException(
+ "setAudioServerStateCallback called with already registered callabck");
+ }
+ final IAudioService service = getService();
+ try {
+ service.registerAudioServerStateDispatcher(mAudioServerStateDispatcher);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mAudioServerStateExec = executor;
+ mAudioServerStateCb = stateCallback;
+ }
+ }
+
+ /**
+ * @hide
+ * Unregisters the callback for notification of audio server state changes.
+ */
+ @SystemApi
+ public void clearAudioServerStateCallback() {
+ synchronized (mAudioServerStateCbLock) {
+ if (mAudioServerStateCb != null) {
+ final IAudioService service = getService();
+ try {
+ service.unregisterAudioServerStateDispatcher(
+ mAudioServerStateDispatcher);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ mAudioServerStateExec = null;
+ mAudioServerStateCb = null;
+ }
+ }
+
+ /**
+ * @hide
+ * Checks if native audioservice is running or not.
+ * @return true if native audioservice runs, false otherwise.
+ */
+ @SystemApi
+ public boolean isAudioServerRunning() {
+ final IAudioService service = getService();
+ try {
+ return service.isAudioServerRunning();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
//---------------------------------------------------------
// Inner classes
//--------------------
diff --git a/media/java/android/media/IAudioServerStateDispatcher.aidl b/media/java/android/media/IAudioServerStateDispatcher.aidl
new file mode 100644
index 0000000..2bc90ea
--- /dev/null
+++ b/media/java/android/media/IAudioServerStateDispatcher.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * AIDL for the AudioService to signal audio server state changes
+ *
+ * {@hide}
+ */
+oneway interface IAudioServerStateDispatcher {
+
+ void dispatchAudioServerStateChange(boolean state);
+
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 4c37014..05ba4c3 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -24,10 +24,12 @@
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
+import android.media.IAudioServerStateDispatcher;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
import android.media.IVolumeController;
+import android.media.IVolumeController;
import android.media.PlayerBase;
import android.media.VolumePolicy;
import android.media.audiopolicy.AudioPolicyConfig;
@@ -208,6 +210,12 @@
oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
in IAudioPolicyCallback pcb);
+ void registerAudioServerStateDispatcher(IAudioServerStateDispatcher asd);
+
+ oneway void unregisterAudioServerStateDispatcher(IAudioServerStateDispatcher asd);
+
+ boolean isAudioServerRunning();
+
// WARNING: read warning at top of file, new methods that need to be used by native
// code via IAudioManager.h need to be added to the top section.
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index b8184a0..b8d01c4 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -119,7 +119,8 @@
private final ISession mBinder;
private final CallbackStub mCbStub;
- private CallbackMessageHandler mCallbackHandler;
+ // Do not change the name of mCallback. Support lib accesses this by using reflection.
+ private CallbackMessageHandler mCallback;
private VolumeProvider mVolumeProvider;
private PlaybackState mPlaybackState;
@@ -194,13 +195,13 @@
*/
public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
synchronized (mLock) {
- if (mCallbackHandler != null) {
+ if (mCallback != null) {
// We're updating the callback, clear the session from the old one.
- mCallbackHandler.mCallback.mSession = null;
- mCallbackHandler.removeCallbacksAndMessages(null);
+ mCallback.mCallback.mSession = null;
+ mCallback.removeCallbacksAndMessages(null);
}
if (callback == null) {
- mCallbackHandler = null;
+ mCallback = null;
return;
}
if (handler == null) {
@@ -209,7 +210,7 @@
callback.mSession = this;
CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
callback);
- mCallbackHandler = msgHandler;
+ mCallback = msgHandler;
}
}
@@ -634,8 +635,8 @@
private void postToCallback(int what, Object obj, Bundle extras) {
synchronized (mLock) {
- if (mCallbackHandler != null) {
- mCallbackHandler.post(what, obj, extras);
+ if (mCallback != null) {
+ mCallback.post(what, obj, extras);
}
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1551b8e..6cf5eef 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1123,6 +1123,9 @@
dumpSetting(s, p,
Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
GlobalSettingsProto.SHOW_MUTE_IN_CRASH_DIALOG);
+ dumpSetting(s, p,
+ Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION,
+ GlobalSettingsProto.SHOW_ZEN_UPGRADE_NOTIFICATION);
// Please insert new settings using the same order as in Settings.Global.
}
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..2d62a80
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..a52e5b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step.png
new file mode 100644
index 0000000..d13c730
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step_dark.png
new file mode 100644
index 0000000..31c602e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..ddbcb07
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step_dark.png
new file mode 100644
index 0000000..ce91fcbb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..c606a58
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step_dark.png
new file mode 100644
index 0000000..c2a5fef
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_quick_step_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index 8078c41..2470947 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -41,6 +41,15 @@
android:scaleType="centerInside"
/>
<com.android.systemui.statusbar.policy.KeyButtonView
+ android:id="@+id/rotate_suggestion"
+ android:layout_width="@dimen/navigation_extra_key_width"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="2dp"
+ android:visibility="invisible"
+ android:scaleType="centerInside"
+ android:contentDescription="@string/accessibility_rotate_button"
+ />
+ <com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/accessibility_button"
android:layout_width="@dimen/navigation_extra_key_width"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3f6c85f..d4eba45 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -884,7 +884,7 @@
<dimen name="nav_quick_scrub_track_thickness">2dp</dimen>
<!-- Home button padding for sizing -->
- <dimen name="home_padding">15dp</dimen>
+ <dimen name="home_padding">16dp</dimen>
<!-- Smart reply button -->
<dimen name="smart_reply_button_corner_radius">24dip</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index dc082a9..8c59e75 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1751,15 +1751,13 @@
<string-array name="nav_bar_buttons">
<item>Clipboard</item>
<item>Keycode</item>
- <item>Keyboard switcher</item>
- <item>Rotation suggestion</item>
+ <item>Rotate confirm, keyboard switcher</item>
<item>None</item>
</string-array>
<string-array name="nav_bar_button_values" translatable="false">
<item>clipboard</item>
<item>key</item>
<item>menu_ime</item>
- <item>rotate</item>
<item>space</item>
</string-array>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 7f382ac..e200a7f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -23,6 +23,14 @@
void onBind(in ISystemUiProxy sysUiProxy);
/**
+ * Called once immediately prior to the first onMotionEvent() call, providing a hint to the
+ * target the initial source of the subsequent motion events.
+ *
+ * @param downHitTarget is one of the {@link NavigationBarCompat.HitTarget}s
+ */
+ void onPreMotionEvent(int downHitTarget);
+
+ /**
* Proxies motion events from the nav bar in SystemUI to the OverviewProxyService. The sender
* guarantees the following order of events:
*
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 62bd72f..138910c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -316,6 +316,17 @@
}
/**
+ * Cancels the remote recents animation started from {@link #startRecentsActivity}.
+ */
+ public void cancelRecentsAnimation() {
+ try {
+ ActivityManager.getService().cancelRecentsAnimation();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to cancel recents animation", e);
+ }
+ }
+
+ /**
* Starts a task from Recents.
*
* @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
new file mode 100644
index 0000000..f622d4a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.systemui.shared.system;
+
+import android.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class NavigationBarCompat {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME})
+ public @interface HitTarget{}
+
+ public static final int HIT_TARGET_NONE = 0;
+ public static final int HIT_TARGET_BACK = 1;
+ public static final int HIT_TARGET_HOME = 2;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index c28b7ee..1aea5e7 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1369,6 +1369,7 @@
mListView = findViewById(android.R.id.list);
mHardwareLayout = HardwareUiLayout.get(mListView);
mHardwareLayout.setOutsideTouchListener(view -> dismiss());
+ setTitle(R.string.global_actions);
}
private void updateList() {
@@ -1464,20 +1465,6 @@
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- for (int i = 0; i < mAdapter.getCount(); ++i) {
- CharSequence label =
- mAdapter.getItem(i).getLabelForAccessibility(getContext());
- if (label != null) {
- event.getText().add(label);
- }
- }
- }
- return super.dispatchPopulateAccessibilityEvent(event);
- }
-
- @Override
public void onColorsChanged(ColorExtractor extractor, int which) {
if (mKeyguardShowing) {
if ((WallpaperManager.FLAG_LOCK & which) != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index eb5619b..0876507 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -308,6 +308,10 @@
}
}
+ public void setRippleAllowed(boolean allowed) {
+ mBackgroundNormal.setPressedAllowed(allowed);
+ }
+
private boolean handleTouchEventDimmed(MotionEvent event) {
if (mNeedsDimming && !mDimmed) {
// We're actually dimmed, but our content isn't dimmable, let's ensure we have a ripple
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index b3f68d3..2723df7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -370,14 +370,6 @@
mNotificationInflater.inflateNotificationViews();
}
- @Override
- public void setPressed(boolean pressed) {
- if (isOnKeyguard() || mEntry.notification.getNotification().contentIntent == null) {
- // We're dropping the ripple if we have a collapse / launch animation
- super.setPressed(pressed);
- }
- }
-
public void onNotificationUpdated() {
for (NotificationContentView l : mLayouts) {
l.onNotificationUpdated(mEntry);
@@ -407,6 +399,7 @@
showBlockingHelper(mEntry.userSentiment ==
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+ updateRippleAllowed();
}
@VisibleForTesting
@@ -1805,6 +1798,13 @@
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
}
+ updateRippleAllowed();
+ }
+
+ private void updateRippleAllowed() {
+ boolean allowed = isOnKeyguard()
+ || mEntry.notification.getNotification().contentIntent == null;
+ setRippleAllowed(allowed);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index ab89a52..0ff4dde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -28,6 +28,7 @@
import android.util.AttributeSet;
import android.view.View;
+import com.android.internal.util.ArrayUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -50,6 +51,7 @@
private boolean mExpandAnimationRunning;
private float mActualWidth;
private int mDrawableAlpha = 255;
+ private boolean mIsPressedAllowed;
public NotificationBackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -94,13 +96,7 @@
@Override
protected void drawableStateChanged() {
- drawableStateChanged(mBackground);
- }
-
- private void drawableStateChanged(Drawable d) {
- if (d != null && d.isStateful()) {
- d.setState(getDrawableState());
- }
+ setState(getDrawableState());
}
@Override
@@ -177,7 +173,13 @@
}
public void setState(int[] drawableState) {
- mBackground.setState(drawableState);
+ if (mBackground != null && mBackground.isStateful()) {
+ if (!mIsPressedAllowed) {
+ drawableState = ArrayUtils.removeInt(drawableState,
+ com.android.internal.R.attr.state_pressed);
+ }
+ mBackground.setState(drawableState);
+ }
}
public void setRippleColor(int color) {
@@ -258,4 +260,8 @@
}
invalidate();
}
+
+ public void setPressedAllowed(boolean allowed) {
+ mIsPressedAllowed = allowed;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 1239a9e..72938c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -114,8 +114,6 @@
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
- private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
-
protected NavigationBarView mNavigationBarView = null;
protected AssistManager mAssistManager;
@@ -152,7 +150,7 @@
private RotationLockController mRotationLockController;
private TaskStackListenerImpl mTaskStackListener;
- private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false);
+ private final Runnable mRemoveRotationProposal = () -> safeSetRotationButtonState(false);
private Animator mRotateShowAnimator;
private Animator mRotateHideAnimator;
@@ -361,22 +359,32 @@
// rotate button if shown.
if (!isValid) {
- setRotateSuggestionButtonState(false);
+ safeSetRotationButtonState(false);
return;
}
if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
// Use this as a signal to remove any current suggestions
getView().getHandler().removeCallbacks(mRemoveRotationProposal);
- setRotateSuggestionButtonState(false);
+ safeSetRotationButtonState(false);
} else {
mLastRotationSuggestion = rotation; // Remember rotation for click
- setRotateSuggestionButtonState(true);
+ safeSetRotationButtonState(true);
rescheduleRotationTimeout(false);
mMetricsLogger.visible(MetricsEvent.ROTATION_SUGGESTION_SHOWN);
}
}
+ private void safeSetRotationButtonState(boolean vis) {
+ if (mNavigationBarView != null) mNavigationBarView.setRotateSuggestionButtonState(vis);
+ }
+
+ private void safeSetRotationButtonState(boolean vis, boolean force) {
+ if (mNavigationBarView != null) {
+ mNavigationBarView.setRotateSuggestionButtonState(vis, force);
+ }
+ }
+
private void rescheduleRotationTimeout(final boolean reasonHover) {
// May be called due to a new rotation proposal or a change in hover state
if (reasonHover) {
@@ -402,84 +410,6 @@
return 6000;
}
- public void setRotateSuggestionButtonState(final boolean visible) {
- setRotateSuggestionButtonState(visible, false);
- }
-
- public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) {
- ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
- final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
-
- // Rerun a show animation to indicate change but don't rerun a hide animation
- if (!visible && !currentlyVisible) return;
-
- View currentView = rotBtn.getCurrentView();
- if (currentView == null) return;
-
- KeyButtonDrawable kbd = rotBtn.getImageDrawable();
- if (kbd == null) return;
-
- AnimatedVectorDrawable animIcon = null;
- if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
- animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
- }
-
- if (visible) { // Appear and change
- rotBtn.setVisibility(View.VISIBLE);
- mNavigationBarView.notifySubtreeAccessibilityStateChangedIfNeeded();
-
- if (skipAnim) {
- currentView.setAlpha(1f);
- return;
- }
-
- // Start a new animation if running
- if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
- if (mRotateHideAnimator != null) mRotateHideAnimator.pause();
-
- ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha",
- 0f, 1f);
- appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
- appearFade.setInterpolator(Interpolators.LINEAR);
- mRotateShowAnimator = appearFade;
- appearFade.start();
-
- // Run the rotate icon's animation if it has one
- if (animIcon != null) {
- animIcon.reset();
- animIcon.start();
- }
-
- } else { // Hide
-
- if (skipAnim) {
- rotBtn.setVisibility(View.INVISIBLE);
- mNavigationBarView.notifySubtreeAccessibilityStateChangedIfNeeded();
- return;
- }
-
- // Don't start any new hide animations if one is running
- if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
- // Pause any active show animations but don't reset the AVD to avoid jumps
- if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
-
- ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
- 0f);
- fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
- fadeOut.setInterpolator(Interpolators.LINEAR);
- fadeOut.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- rotBtn.setVisibility(View.INVISIBLE);
- mNavigationBarView.notifySubtreeAccessibilityStateChangedIfNeeded();
- }
- });
-
- mRotateHideAnimator = fadeOut;
- fadeOut.start();
- }
- }
-
// Injected from StatusBar at creation.
public void setCurrentSysuiVisibility(int systemUiVisibility) {
mSystemUiVisibility = systemUiVisibility;
@@ -892,7 +822,7 @@
if (shouldOverrideUserLockPrefs(rotation)) {
mRotationLockController.setRotationLockedAtAngle(true, rotation);
}
- setRotateSuggestionButtonState(false, true);
+ safeSetRotationButtonState(false, true);
}
if (mNavigationBarView != null
@@ -928,22 +858,22 @@
@Override
public void onTaskStackChanged() {
- setRotateSuggestionButtonState(false);
+ safeSetRotationButtonState(false);
}
@Override
public void onTaskRemoved(int taskId) {
- setRotateSuggestionButtonState(false);
+ safeSetRotationButtonState(false);
}
@Override
public void onTaskMovedToFront(int taskId) {
- setRotateSuggestionButtonState(false);
+ safeSetRotationButtonState(false);
}
@Override
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
- setRotateSuggestionButtonState(false);
+ safeSetRotationButtonState(false);
}
}
@@ -960,6 +890,7 @@
PixelFormat.TRANSLUCENT);
lp.token = new Binder();
lp.setTitle("NavigationBar");
+ lp.accessibilityTitle = context.getString(R.string.nav_bar);
lp.windowAnimations = 0;
View navigationBarView = LayoutInflater.from(context).inflate(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index d15c771..63f2ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -131,6 +131,9 @@
mNavigationBarView.requestUnbufferedDispatch(event);
event.transform(mTransformGlobalMatrix);
try {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ overviewProxy.onPreMotionEvent(mNavigationBarView.getDownHitTarget());
+ }
overviewProxy.onMotionEvent(event);
if (DEBUG_OVERVIEW_PROXY) {
Log.d(TAG_OPS, "Send MotionEvent: " + event.toString());
@@ -146,8 +149,8 @@
}
public boolean onInterceptTouchEvent(MotionEvent event) {
- int action = event.getAction();
- switch (action & MotionEvent.ACTION_MASK) {
+ int action = event.getActionMasked();
+ switch (action) {
case MotionEvent.ACTION_DOWN: {
mTouchDownX = (int) event.getX();
mTouchDownY = (int) event.getY();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 9d20e4e..9894235 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -57,13 +57,12 @@
public static final String NAV_BAR_LEFT = "sysui_nav_bar_left";
public static final String NAV_BAR_RIGHT = "sysui_nav_bar_right";
- public static final String MENU_IME = "menu_ime";
+ public static final String MENU_IME_ROTATE = "menu_ime";
public static final String BACK = "back";
public static final String HOME = "home";
public static final String RECENT = "recent";
public static final String NAVSPACE = "space";
public static final String CLIPBOARD = "clipboard";
- public static final String ROTATE = "rotate";
public static final String KEY = "key";
public static final String LEFT = "left";
public static final String RIGHT = "right";
@@ -317,10 +316,10 @@
View v = null;
String button = extractButton(buttonSpec);
if (LEFT.equals(button)) {
- String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, ROTATE);
+ String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, NAVSPACE);
button = extractButton(s);
} else if (RIGHT.equals(button)) {
- String s = Dependency.get(TunerService.class).getValue(NAV_BAR_RIGHT, MENU_IME);
+ String s = Dependency.get(TunerService.class).getValue(NAV_BAR_RIGHT, MENU_IME_ROTATE);
button = extractButton(s);
}
// Let plugins go first so they can override a standard view if they want.
@@ -334,14 +333,12 @@
v = inflater.inflate(R.layout.back, parent, false);
} else if (RECENT.equals(button)) {
v = inflater.inflate(R.layout.recent_apps, parent, false);
- } else if (MENU_IME.equals(button)) {
+ } else if (MENU_IME_ROTATE.equals(button)) {
v = inflater.inflate(R.layout.menu_ime, parent, false);
} else if (NAVSPACE.equals(button)) {
v = inflater.inflate(R.layout.nav_key_space, parent, false);
} else if (CLIPBOARD.equals(button)) {
v = inflater.inflate(R.layout.clipboard, parent, false);
- } else if (ROTATE.equals(button)) {
- v = inflater.inflate(R.layout.rotate_suggestion, parent, false);
} else if (button.startsWith(KEY)) {
String uri = extractImage(button);
int code = extractKeycode(button);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index af0afbd..53dc814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -16,6 +16,13 @@
package com.android.systemui.statusbar.phone;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
import android.animation.ObjectAnimator;
@@ -29,6 +36,7 @@
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
@@ -49,6 +57,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.DockedStackExistsListener;
+import com.android.systemui.Interpolators;
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
@@ -57,6 +66,7 @@
import com.android.systemui.plugins.statusbar.phone.NavGesture;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.recents.RecentsOnboarding;
+import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -70,6 +80,8 @@
final static boolean DEBUG = false;
final static String TAG = "StatusBar/NavBarView";
+ final static int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
+
// slippery nav bar when everything is disabled, e.g. during setup
final static boolean SLIPPERY_WHEN_DISABLED = true;
@@ -85,9 +97,15 @@
boolean mShowMenu;
boolean mShowAccessibilityButton;
boolean mLongClickableAccessibilityButton;
+ boolean mShowRotateButton;
int mDisabledFlags = 0;
int mNavigationIconHints = 0;
+ private @NavigationBarCompat.HitTarget int mDownHitTarget = HIT_TARGET_NONE;
+ private Rect mHomeButtonBounds = new Rect();
+ private Rect mBackButtonBounds = new Rect();
+ private int[] mTmpPosition = new int[2];
+
private KeyButtonDrawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
private KeyButtonDrawable mBackCarModeIcon, mBackLandCarModeIcon;
private KeyButtonDrawable mBackAltCarModeIcon, mBackAltLandCarModeIcon;
@@ -127,6 +145,8 @@
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
+ private Animator mRotateHideAnimator;
+
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
private boolean mHomeAppearing;
@@ -217,6 +237,9 @@
mShowAccessibilityButton = false;
mLongClickableAccessibilityButton = false;
+ mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
+
mConfiguration = new Configuration();
mConfiguration.updateFrom(context.getResources().getConfiguration());
updateIcons(context, Configuration.EMPTY, mConfiguration);
@@ -232,9 +255,6 @@
new ButtonDispatcher(R.id.accessibility_button));
mButtonDispatchers.put(R.id.rotate_suggestion,
new ButtonDispatcher(R.id.rotate_suggestion));
-
- mOverviewProxyService = Dependency.get(OverviewProxyService.class);
- mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
}
public BarTransitions getBarTransitions() {
@@ -275,6 +295,18 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case ACTION_DOWN:
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ mDownHitTarget = HIT_TARGET_NONE;
+ if (mBackButtonBounds.contains(x, y)) {
+ mDownHitTarget = HIT_TARGET_BACK;
+ } else if (mHomeButtonBounds.contains(x, y)) {
+ mDownHitTarget = HIT_TARGET_HOME;
+ }
+ break;
+ }
return mGestureHelper.onInterceptTouchEvent(event);
}
@@ -286,6 +318,10 @@
return super.onTouchEvent(event);
}
+ public @NavigationBarCompat.HitTarget int getDownHitTarget() {
+ return mDownHitTarget;
+ }
+
public void abortCurrentGesture() {
getHomeButton().abortCurrentGesture();
}
@@ -355,14 +391,23 @@
}
if (oldConfig.densityDpi != newConfig.densityDpi
|| oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {
- mBackIcon = getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
+ final boolean proxyAvailable = mOverviewProxyService.getProxy() != null;
+ mBackIcon = proxyAvailable
+ ? getDrawable(ctx, R.drawable.ic_sysbar_back_quick_step,
+ R.drawable.ic_sysbar_back_quick_step_dark)
+ : getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
mBackLandIcon = mBackIcon;
- mBackAltIcon = getDrawable(ctx,
- R.drawable.ic_sysbar_back_ime, R.drawable.ic_sysbar_back_ime_dark);
+ mBackAltIcon = proxyAvailable
+ ? getDrawable(ctx, R.drawable.ic_sysbar_back_ime_quick_step,
+ R.drawable.ic_sysbar_back_ime_quick_step_dark)
+ : getDrawable(ctx, R.drawable.ic_sysbar_back_ime,
+ R.drawable.ic_sysbar_back_ime_dark);
mBackAltLandIcon = mBackAltIcon;
- mHomeDefaultIcon = getDrawable(ctx,
- R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
+ mHomeDefaultIcon = proxyAvailable
+ ? getDrawable(ctx, R.drawable.ic_sysbar_home_quick_step,
+ R.drawable.ic_sysbar_home_quick_step_dark)
+ : getDrawable(ctx, R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
mRecentIcon = getDrawable(ctx,
R.drawable.ic_sysbar_recent, R.drawable.ic_sysbar_recent_dark);
mMenuIcon = getDrawable(ctx, R.drawable.ic_sysbar_menu, R.drawable.ic_sysbar_menu_dark);
@@ -462,22 +507,25 @@
getHomeButton().setImageDrawable(mHomeDefaultIcon);
}
- // The Accessibility button always overrides the appearance of the IME switcher
+ // Update IME button visibility, a11y and rotate button always overrides the appearance
final boolean showImeButton =
- !mShowAccessibilityButton && ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN)
- != 0);
+ !mShowAccessibilityButton &&
+ !mShowRotateButton &&
+ ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
getImeSwitchButton().setImageDrawable(mImeIcon);
- // Update menu button in case the IME state has changed.
+ // Update menu button, visibility logic in method
setMenuVisibility(mShowMenu, true);
getMenuButton().setImageDrawable(mMenuIcon);
+ // Update rotate button, visibility altered by a11y button logic
+ getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
+
+ // Update a11y button, visibility logic in state method
setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton);
getAccessibilityButton().setImageDrawable(mAccessibilityIcon);
- getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
-
setDisabledFlags(mDisabledFlags, true);
mBarTransitions.reapplyDarkIntensity();
@@ -621,8 +669,10 @@
mShowMenu = show;
- // Only show Menu if IME switcher and Accessibility button not shown.
- final boolean shouldShow = mShowMenu && !mShowAccessibilityButton &&
+ // Only show Menu if IME switcher, rotate and Accessibility buttons are not shown.
+ final boolean shouldShow = mShowMenu &&
+ !mShowAccessibilityButton &&
+ !mShowRotateButton &&
((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0);
getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
@@ -632,15 +682,96 @@
mShowAccessibilityButton = visible;
mLongClickableAccessibilityButton = longClickable;
if (visible) {
- // Accessibility button overrides Menu and IME switcher buttons.
+ // Accessibility button overrides Menu, IME switcher and rotate buttons.
setMenuVisibility(false, true);
getImeSwitchButton().setVisibility(View.INVISIBLE);
+ setRotateSuggestionButtonState(false, true);
}
getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
getAccessibilityButton().setLongClickable(longClickable);
}
+ public void setRotateSuggestionButtonState(final boolean visible) {
+ setRotateSuggestionButtonState(visible, false);
+ }
+
+ public void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
+ ButtonDispatcher rotBtn = getRotateSuggestionButton();
+ final boolean currentlyVisible = mShowRotateButton;
+
+ // Rerun a show animation to indicate change but don't rerun a hide animation
+ if (!visible && !currentlyVisible) return;
+
+ View currentView = rotBtn.getCurrentView();
+ if (currentView == null) return;
+
+ KeyButtonDrawable kbd = rotBtn.getImageDrawable();
+ if (kbd == null) return;
+
+ AnimatedVectorDrawable animIcon = null;
+ if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
+ animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+ }
+
+ if (visible) { // Appear and change, cannot force
+ setRotateButtonVisibility(true);
+
+ // Stop any currently running hide animations
+ if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+ mRotateHideAnimator.pause();
+ }
+
+ // Reset the alpha if any has changed due to hide animation
+ currentView.setAlpha(1f);
+
+ // Run the rotate icon's animation if it has one
+ if (animIcon != null) {
+ animIcon.reset();
+ animIcon.start();
+ }
+
+ } else { // Hide
+ if (force) {
+ // If a hide animator is running stop it and instantly make invisible
+ if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+ mRotateHideAnimator.pause();
+ }
+ setRotateButtonVisibility(false);
+ return;
+ }
+
+ // Don't start any new hide animations if one is running
+ if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
+
+ ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
+ 0f);
+ fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
+ fadeOut.setInterpolator(Interpolators.LINEAR);
+ fadeOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setRotateButtonVisibility(false);
+ }
+ });
+
+ mRotateHideAnimator = fadeOut;
+ fadeOut.start();
+ }
+ }
+
+ private void setRotateButtonVisibility(final boolean visible) {
+ // Never show if a11y is visible
+ final boolean adjVisible = visible && !mShowAccessibilityButton;
+ final int vis = adjVisible ? View.VISIBLE : View.INVISIBLE;
+
+ getRotateSuggestionButton().setVisibility(vis);
+ mShowRotateButton = visible;
+
+ // Hide/restore other button visibility, if necessary
+ setNavigationIconHints(mNavigationIconHints, true);
+ }
+
@Override
public void onFinishInflate() {
mNavigationInflaterView = (NavigationBarInflaterView) findViewById(
@@ -666,6 +797,8 @@
setSlippery(!isConnected);
setDisabledFlags(mDisabledFlags, true);
setUpSwipeUpOnboarding(isConnected);
+ updateIcons(getContext(), Configuration.EMPTY, mConfiguration);
+ setNavigationIconHints(mNavigationIconHints, true);
}
@Override
@@ -677,9 +810,23 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
+ updateButtonLocationOnScreen(getBackButton(), mBackButtonBounds);
+ updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds);
mGestureHelper.onLayout(changed, left, top, right, bottom);
}
+ private void updateButtonLocationOnScreen(ButtonDispatcher button, Rect buttonBounds) {
+ View view = button.getCurrentView();
+ if (view == null) {
+ buttonBounds.setEmpty();
+ return;
+ }
+ view.getLocationInWindow(mTmpPosition);
+ buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
+ mTmpPosition[0] + view.getMeasuredWidth(),
+ mTmpPosition[1] + view.getMeasuredHeight());
+ }
+
private void updateRotatedViews() {
mRotatedViews[Surface.ROTATION_0] =
mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index dc0835e..bb2f597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -53,6 +53,7 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
import static com.android.systemui.OverviewProxyService.TAG_OPS;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
/**
* Class to detect gestures on the navigation bar and implement quick scrub and switch.
@@ -92,7 +93,6 @@
private final Handler mHandler = new Handler();
private final Interpolator mQuickScrubEndInterpolator = new DecelerateInterpolator();
private final Rect mTrackRect = new Rect();
- private final Rect mHomeButtonRect = new Rect();
private final Paint mTrackPaint = new Paint();
private final int mScrollTouchSlop;
private final OverviewProxyService mOverviewEventSender;
@@ -138,7 +138,7 @@
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
if (!isQuickScrubEnabled() || mQuickScrubActive || !mAllowQuickSwitch ||
- !mHomeButtonRect.contains(mTouchDownX, mTouchDownY)) {
+ mNavigationBarView.getDownHitTarget() != HIT_TARGET_HOME) {
return false;
}
float velocityX = mIsRTL ? -velX : velX;
@@ -226,7 +226,8 @@
case MotionEvent.ACTION_DOWN: {
int x = (int) event.getX();
int y = (int) event.getY();
- if (isQuickScrubEnabled() && mHomeButtonRect.contains(x, y)) {
+ if (isQuickScrubEnabled()
+ && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME) {
mTouchDownX = x;
mTouchDownY = y;
homeButton.setDelayTouchFeedback(true);
@@ -346,17 +347,6 @@
x2 = x1 + width / 2 - mTrackPadding;
}
mTrackRect.set(x1, y1, x2, y2);
-
- // Get the touch rect of the home button location
- View homeView = mNavigationBarView.getHomeButton().getCurrentView();
- if (homeView != null) {
- int[] globalHomePos = homeView.getLocationOnScreen();
- int[] globalNavBarPos = mNavigationBarView.getLocationOnScreen();
- int homeX = globalHomePos[0] - globalNavBarPos[0];
- int homeY = globalHomePos[1] - globalNavBarPos[1];
- mHomeButtonRect.set(homeX, homeY, homeX + homeView.getMeasuredWidth(),
- homeY + homeView.getMeasuredHeight());
- }
}
@Override
@@ -381,7 +371,7 @@
}
boolean isQuickScrubEnabled() {
- return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", false);
+ return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true);
}
private void startQuickScrub() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1438763..94ebc1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -138,7 +138,8 @@
protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
- private float mFraction;
+ // Assuming the shade is expanded during initialization
+ private float mExpansionFraction = 1f;
private boolean mDarkenWhileDragging;
protected boolean mAnimateChange;
@@ -166,6 +167,7 @@
private boolean mScreenBlankingCallbackCalled;
private Callback mCallback;
private boolean mWallpaperSupportsAmbientMode;
+ private boolean mScreenOn;
// Scrim blanking callbacks
private Choreographer.FrameCallback mPendingFrameCallback;
@@ -251,6 +253,7 @@
mCurrentBehindTint = state.getBehindTint();
mCurrentInFrontAlpha = state.getFrontAlpha();
mCurrentBehindAlpha = state.getBehindAlpha();
+ applyExpansionToAlpha();
// Cancel blanking transitions that were pending before we requested a new state
if (mPendingFrameCallback != null) {
@@ -362,45 +365,50 @@
* @param fraction From 0 to 1 where 0 means collapse and 1 expanded.
*/
public void setPanelExpansion(float fraction) {
- if (mFraction != fraction) {
- mFraction = fraction;
+ if (mExpansionFraction != fraction) {
+ mExpansionFraction = fraction;
- if (mState == ScrimState.UNLOCKED) {
- // Darken scrim as you pull down the shade when unlocked
- float behindFraction = getInterpolatedFraction();
- behindFraction = (float) Math.pow(behindFraction, 0.8f);
- mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
- mCurrentInFrontAlpha = 0;
- } else if (mState == ScrimState.KEYGUARD) {
- if (mUpdatePending) {
- return;
- }
+ if (!(mState == ScrimState.UNLOCKED || mState == ScrimState.KEYGUARD)) {
+ return;
+ }
- // Either darken of make the scrim transparent when you
- // pull down the shade
- float interpolatedFract = getInterpolatedFraction();
- if (mDarkenWhileDragging) {
- mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
- mScrimBehindAlphaKeyguard, interpolatedFract);
- mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
- } else {
- mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
- interpolatedFract);
- mCurrentInFrontAlpha = 0;
- }
- } else {
+ applyExpansionToAlpha();
+
+ if (mUpdatePending) {
return;
}
if (mPinnedHeadsUpCount != 0) {
updateHeadsUpScrim(false);
}
-
updateScrim(false /* animate */, mScrimInFront, mCurrentInFrontAlpha);
updateScrim(false /* animate */, mScrimBehind, mCurrentBehindAlpha);
}
}
+ private void applyExpansionToAlpha() {
+ if (mState == ScrimState.UNLOCKED) {
+ // Darken scrim as you pull down the shade when unlocked
+ float behindFraction = getInterpolatedFraction();
+ behindFraction = (float) Math.pow(behindFraction, 0.8f);
+ mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
+ mCurrentInFrontAlpha = 0;
+ } else if (mState == ScrimState.KEYGUARD) {
+ // Either darken of make the scrim transparent when you
+ // pull down the shade
+ float interpolatedFract = getInterpolatedFraction();
+ if (mDarkenWhileDragging) {
+ mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
+ mScrimBehindAlphaKeyguard, interpolatedFract);
+ mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
+ } else {
+ mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
+ interpolatedFract);
+ mCurrentInFrontAlpha = 0;
+ }
+ }
+ }
+
/**
* Keyguard and shade scrim opacity varies according to how many notifications are visible.
* @param notificationCount Number of visible notifications.
@@ -496,7 +504,7 @@
}
private float getInterpolatedFraction() {
- float frac = mFraction;
+ float frac = mExpansionFraction;
// let's start this 20% of the way down the screen
frac = frac * 1.2f - 0.2f;
if (frac <= 0) {
@@ -785,10 +793,11 @@
// Setting power states can happen after we push out the frame. Make sure we
// stay fully opaque until the power state request reaches the lower levels.
+ final int delay = mScreenOn ? 16 : 500;
if (DEBUG) {
- Log.d(TAG, "Waiting for the screen to turn on...");
+ Log.d(TAG, "Fading out scrims with delay: " + delay);
}
- getHandler().postDelayed(mBlankingTransitionRunnable, 500);
+ getHandler().postDelayed(mBlankingTransitionRunnable, delay);
};
doOnTheNextFrame(mPendingFrameCallback);
}
@@ -825,7 +834,7 @@
} else {
alpha = 1.0f - mTopHeadsUpDragAmount;
}
- float expandFactor = (1.0f - mFraction);
+ float expandFactor = (1.0f - mExpansionFraction);
expandFactor = Math.max(expandFactor, 0.0f);
return alpha * expandFactor;
}
@@ -904,6 +913,7 @@
* Interrupts blanking transitions once the display notifies that it's already on.
*/
public void onScreenTurnedOn() {
+ mScreenOn = true;
final Handler handler = getHandler();
if (handler.hasCallbacks(mBlankingTransitionRunnable)) {
if (DEBUG) {
@@ -914,6 +924,10 @@
}
}
+ public void onScreenTurnedOff() {
+ mScreenOn = false;
+ }
+
public interface Callback {
default void onStart() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 458518c..3b63d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4413,6 +4413,7 @@
@Override
public void onScreenTurnedOff() {
mFalsingManager.onScreenOff();
+ mScrimController.onScreenTurnedOff();
// If we pulse in from AOD, we turn the screen off first. However, updatingIsKeyguard
// in that case destroys the HeadsUpManager state, so don't do it in that case.
if (!isPulsing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index c30f633..948f524 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -106,6 +106,7 @@
mLp.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");
+ mLp.accessibilityTitle = mContext.getString(R.string.status_bar);
mLp.packageName = mContext.getPackageName();
mStatusBarView = statusBarView;
mBarHeight = barHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 1da50ad..503a1b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -36,6 +36,7 @@
public class StatusIconContainer extends AlphaOptimizedLinearLayout {
private static final String TAG = "StatusIconContainer";
+ private static final boolean DEBUG = false;
private static final int MAX_ICONS = 5;
private static final int MAX_DOTS = 3;
@@ -94,7 +95,7 @@
int childCount = getChildCount();
// Underflow === don't show content until that index
int firstUnderflowIndex = -1;
- android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
+ if (DEBUG) android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
//TODO: Dots
for (int i = childCount - 1; i >= 0; i--) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 9d1c1e8..98bebec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -52,6 +52,7 @@
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
@@ -269,11 +270,7 @@
if (doIt) {
// If there was a pending remote recents animation, then we need to
// cancel the animation now before we handle the button itself
- try {
- ActivityManager.getService().cancelRecentsAnimation();
- } catch (RemoteException e) {
- Log.e(TAG, "Could not cancel recents animation", e);
- }
+ ActivityManagerWrapper.getInstance().cancelRecentsAnimation();
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
index 45abd45..eb0c89b 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
@@ -18,7 +18,7 @@
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_END;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_START;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_IMAGE_DELIM;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.MENU_IME;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.MENU_IME_ROTATE;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAVSPACE;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_LEFT;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_RIGHT;
@@ -29,24 +29,14 @@
import android.annotation.Nullable;
import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.FontMetricsInt;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Handler;
-import android.support.v14.preference.PreferenceFragment;
-import android.support.v7.preference.DropDownPreference;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
-import android.support.v7.preference.Preference.OnPreferenceClickListener;
-import android.support.v7.preference.PreferenceCategory;
import android.text.SpannableStringBuilder;
import android.text.style.ImageSpan;
import android.util.Log;
@@ -56,7 +46,6 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.NavigationBarInflaterView;
import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
@@ -100,7 +89,7 @@
addPreferencesFromResource(R.xml.nav_bar_tuner);
bindLayout((ListPreference) findPreference(LAYOUT));
bindButton(NAV_BAR_LEFT, NAVSPACE, LEFT);
- bindButton(NAV_BAR_RIGHT, MENU_IME, RIGHT);
+ bindButton(NAV_BAR_RIGHT, MENU_IME_ROTATE, RIGHT);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 43e16db..4702793 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -180,6 +180,7 @@
@Test
public void transitionToUnlocked() {
+ mScrimController.setPanelExpansion(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
@@ -197,6 +198,7 @@
public void transitionToUnlockedFromAod() {
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.setPanelExpansion(0f);
mScrimController.finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED);
// Immediately tinted after the transition starts
@@ -324,6 +326,23 @@
verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
}
+ @Test
+ public void testConservesExpansionOpacityAfterTransition() {
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.setPanelExpansion(0.5f);
+ mScrimController.finishAnimationsImmediately();
+
+ final float expandedAlpha = mScrimBehind.getViewAlpha();
+
+ mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
+ mScrimController.finishAnimationsImmediately();
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.finishAnimationsImmediately();
+
+ Assert.assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
+ expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
+ }
+
private void assertScrimTint(ScrimView scrimView, boolean tinted) {
final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
final String name = scrimView == mScrimInFront ? "front" : "back";
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 08fdb97..608970f 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -204,6 +204,10 @@
// Package: android
NOTE_USB_TETHER = 47;
+ // Inform that DND settings have changed on OS upgrade
+ // Package: android
+ NOTE_ZEN_UPGRADE = 48;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
@@ -253,6 +257,7 @@
// Notify the user about public volume state changes..
// Package: com.android.systemui
+
NOTE_STORAGE_PUBLIC = 0x53505542; // 1397773634
// Notify the user about private volume state changes.
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 6747be3..5e5eacb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -64,6 +64,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.NoSuchElementException;
/**
* Since phone process can be restarted, this class provides a centralized place
@@ -90,6 +91,8 @@
IBinder binder;
+ TelephonyRegistryDeathRecipient deathRecipient;
+
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
@@ -251,6 +254,21 @@
}
};
+ private class TelephonyRegistryDeathRecipient implements IBinder.DeathRecipient {
+
+ private final IBinder binder;
+
+ TelephonyRegistryDeathRecipient(IBinder binder) {
+ this.binder = binder;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DBG) log("binderDied " + binder);
+ remove(binder);
+ }
+ }
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -378,23 +396,14 @@
}
}
- Record r;
synchronized (mRecords) {
// register
- find_and_add: {
- IBinder b = callback.asBinder();
- final int N = mRecords.size();
- for (int i = 0; i < N; i++) {
- r = mRecords.get(i);
- if (b == r.binder) {
- break find_and_add;
- }
- }
- r = new Record();
- r.binder = b;
- mRecords.add(r);
- if (DBG) log("listen oscl: add new record");
+ IBinder b = callback.asBinder();
+ Record r = add(b);
+
+ if (r == null) {
+ return;
}
r.onSubscriptionsChangedListenerCallback = callback;
@@ -496,20 +505,11 @@
synchronized (mRecords) {
// register
- Record r;
- find_and_add: {
- IBinder b = callback.asBinder();
- final int N = mRecords.size();
- for (int i = 0; i < N; i++) {
- r = mRecords.get(i);
- if (b == r.binder) {
- break find_and_add;
- }
- }
- r = new Record();
- r.binder = b;
- mRecords.add(r);
- if (DBG) log("listen: add new record");
+ IBinder b = callback.asBinder();
+ Record r = add(b);
+
+ if (r == null) {
+ return;
}
r.callback = callback;
@@ -697,16 +697,57 @@
return record.canReadPhoneState ? mCallIncomingNumber[phoneId] : "";
}
+ private Record add(IBinder binder) {
+ Record r;
+
+ synchronized (mRecords) {
+ final int N = mRecords.size();
+ for (int i = 0; i < N; i++) {
+ r = mRecords.get(i);
+ if (binder == r.binder) {
+ // Already existed.
+ return r;
+ }
+ }
+ r = new Record();
+ r.binder = binder;
+ r.deathRecipient = new TelephonyRegistryDeathRecipient(binder);
+
+ try {
+ binder.linkToDeath(r.deathRecipient, 0);
+ } catch (RemoteException e) {
+ if (VDBG) log("LinkToDeath remote exception sending to r=" + r + " e=" + e);
+ // Binder already died. Return null.
+ return null;
+ }
+
+ mRecords.add(r);
+ if (DBG) log("add new record");
+ }
+
+ return r;
+ }
+
private void remove(IBinder binder) {
synchronized (mRecords) {
final int recordCount = mRecords.size();
for (int i = 0; i < recordCount; i++) {
- if (mRecords.get(i).binder == binder) {
+ Record r = mRecords.get(i);
+ if (r.binder == binder) {
if (DBG) {
- Record r = mRecords.get(i);
- log("remove: binder=" + binder + "r.callingPackage" + r.callingPackage
- + "r.callback" + r.callback);
+ log("remove: binder=" + binder + " r.callingPackage " + r.callingPackage
+ + " r.callback " + r.callback);
}
+
+ if (r.deathRecipient != null) {
+ try {
+ binder.unlinkToDeath(r.deathRecipient, 0);
+ } catch (NoSuchElementException e) {
+ if (VDBG) log("UnlinkToDeath NoSuchElementException sending to r="
+ + r + " e=" + e);
+ }
+ }
+
mRecords.remove(i);
return;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 14404f5..5fc4373 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1095,7 +1095,7 @@
active.mNumActive++;
}
r.isForeground = true;
- StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.userId, r.shortName,
+ StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
}
r.postNotification();
@@ -1112,7 +1112,7 @@
decActiveForegroundAppLocked(smap, r);
}
r.isForeground = false;
- StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.userId, r.shortName,
+ StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
if (r.app != null) {
mAm.updateLruProcessLocked(r.app, false, null);
@@ -2538,7 +2538,7 @@
cancelForegroundNotificationLocked(r);
if (r.isForeground) {
decActiveForegroundAppLocked(smap, r);
- StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.userId, r.shortName,
+ StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c958b67..f230b12 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4090,10 +4090,10 @@
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
- if (app.info.isAllowedToUseHiddenApi()) {
- // This app is allowed to use undocumented and private APIs. Set
- // up its runtime with the appropriate flag.
- runtimeFlags |= Zygote.DISABLE_HIDDEN_API_CHECKS;
+ if (!app.info.isAllowedToUseHiddenApi()) {
+ // This app is not allowed to use undocumented and private APIs.
+ // Set up its runtime with the appropriate flag.
+ runtimeFlags |= Zygote.ENABLE_HIDDEN_API_CHECKS;
}
String invokeWith = null;
@@ -4371,7 +4371,7 @@
"updateUsageStats: comp=" + component + "res=" + resumed);
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
- component.userId, component.realActivity.getPackageName(),
+ component.app.uid, component.realActivity.getPackageName(),
component.realActivity.getShortClassName(), resumed ?
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND :
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND);
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index db4e09f..6dcf041 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -28,7 +28,9 @@
import android.content.ComponentName;
import android.content.Intent;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.Trace;
+import android.util.Slog;
import android.view.IRecentsAnimationRunner;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
import com.android.server.wm.WindowManagerService;
@@ -63,6 +65,7 @@
mHandler = new Handler(mStackSupervisor.mLooper);
mWindowManager = wm;
mUserController = userController;
+
mCancelAnimationRunnable = () -> {
// The caller has not finished the animation in a predefined amount of time, so
// force-cancel the animation
@@ -73,13 +76,33 @@
void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
ComponentName recentsComponent, int recentsUid) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
+
+ if (!mWindowManager.canStartRecentsAnimation()) {
+ notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+ return;
+ }
+
+ // If the existing home activity is already on top, then cancel
+ ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
+ final boolean hasExistingHomeActivity = homeActivity != null;
+ if (hasExistingHomeActivity) {
+ final ActivityDisplay display = homeActivity.getDisplay();
+ mRestoreHomeBehindStack = display.getStackAboveHome();
+ if (mRestoreHomeBehindStack == null) {
+ notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+ return;
+ }
+ }
+
mWindowManager.deferSurfaceLayout();
try {
- // Cancel the previous recents animation if necessary
- mWindowManager.cancelRecentsAnimation();
- final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null;
- if (!hasExistingHomeActivity) {
+ final ActivityDisplay display;
+ if (hasExistingHomeActivity) {
+ // Move the home activity into place for the animation if it is not already top most
+ display = homeActivity.getDisplay();
+ display.moveHomeStackBehindBottomMostVisibleStack();
+ } else {
// No home activity
final ActivityOptions opts = ActivityOptions.makeBasic();
opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
@@ -95,25 +118,20 @@
.execute();
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+ homeActivity = mStackSupervisor.getHomeActivity();
+ display = homeActivity.getDisplay();
+
// TODO: Maybe wait for app to draw in this particular case?
}
- final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
- final ActivityDisplay display = homeActivity.getDisplay();
-
- // Save the initial position of the home activity stack to be restored to after the
- // animation completes
- mRestoreHomeBehindStack = hasExistingHomeActivity
- ? display.getStackAboveHome()
- : null;
-
- // Move the home activity into place for the animation
- display.moveHomeStackBehindBottomMostVisibleStack();
-
// Mark the home activity as launch-behind to bump its visibility for the
// duration of the gesture that is driven by the recents component
homeActivity.mLaunchTaskBehind = true;
+ // Post a timeout for the animation. This needs to happen before initializing the
+ // recents animation on the WM side since we may decide to cancel the animation there
+ mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
+
// Fetch all the surface controls and pass them to the client to get the animation
// started
mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this,
@@ -122,9 +140,6 @@
// If we updated the launch-behind state, update the visibility of the activities after
// we fetch the visible tasks to be controlled by the animation
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-
- // Post a timeout for the animation
- mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
} finally {
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -178,4 +193,15 @@
});
}
}
+
+ /**
+ * Called only when the animation should be canceled prior to starting.
+ */
+ private void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
+ try {
+ recentsAnimationRunner.onAnimationCanceled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel recents animation before start", e);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e95608f..fffe7dc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -70,6 +70,7 @@
import android.media.AudioSystem;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
+import android.media.IAudioServerStateDispatcher;
import android.media.IAudioService;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
@@ -242,6 +243,7 @@
private static final int MSG_INDICATE_SYSTEM_READY = 26;
private static final int MSG_ACCESSORY_PLUG_MEDIA_UNMUTE = 27;
private static final int MSG_NOTIFY_VOL_EVENT = 28;
+ private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 29;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -390,6 +392,8 @@
case AudioSystem.AUDIO_STATUS_SERVER_DIED:
sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED,
SENDMSG_NOOP, 0, 0, null, 0);
+ sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
+ SENDMSG_QUEUE, 0, 0, null, 0);
break;
default:
break;
@@ -1000,6 +1004,21 @@
onIndicateSystemReady();
// indicate the end of reconfiguration phase to audio HAL
AudioSystem.setParameters("restarting=false");
+
+ sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
+ SENDMSG_QUEUE, 1, 0, null, 0);
+ }
+
+ private void onDispatchAudioServerStateChange(boolean state) {
+ synchronized (mAudioServerStateListeners) {
+ for (AsdProxy asdp : mAudioServerStateListeners.values()) {
+ try {
+ asdp.callback().dispatchAudioServerStateChange(state);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not call dispatchAudioServerStateChange()", e);
+ }
+ }
+ }
}
private void createAudioSystemThread() {
@@ -5089,6 +5108,10 @@
onAudioServerDied();
break;
+ case MSG_DISPATCH_AUDIO_SERVER_STATE:
+ onDispatchAudioServerStateChange(msg.arg1 == 1);
+ break;
+
case MSG_UNLOAD_SOUND_EFFECTS:
onUnloadSoundEffects();
break;
@@ -7341,6 +7364,77 @@
//======================
+ // Audioserver state displatch
+ //======================
+ private class AsdProxy implements IBinder.DeathRecipient {
+ private final IAudioServerStateDispatcher mAsd;
+
+ AsdProxy(IAudioServerStateDispatcher asd) {
+ mAsd = asd;
+ }
+
+ public void binderDied() {
+ synchronized (mAudioServerStateListeners) {
+ mAudioServerStateListeners.remove(mAsd.asBinder());
+ }
+ }
+
+ IAudioServerStateDispatcher callback() {
+ return mAsd;
+ }
+ }
+
+ private HashMap<IBinder, AsdProxy> mAudioServerStateListeners =
+ new HashMap<IBinder, AsdProxy>();
+
+ private void checkMonitorAudioServerStatePermission() {
+ if (!(mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE) ==
+ PackageManager.PERMISSION_GRANTED ||
+ mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING) ==
+ PackageManager.PERMISSION_GRANTED)) {
+ throw new SecurityException("Not allowed to monitor audioserver state");
+ }
+ }
+
+ public void registerAudioServerStateDispatcher(IAudioServerStateDispatcher asd) {
+ checkMonitorAudioServerStatePermission();
+ synchronized (mAudioServerStateListeners) {
+ if (mAudioServerStateListeners.containsKey(asd.asBinder())) {
+ Slog.w(TAG, "Cannot re-register audio server state dispatcher");
+ return;
+ }
+ AsdProxy asdp = new AsdProxy(asd);
+ try {
+ asd.asBinder().linkToDeath(asdp, 0/*flags*/);
+ } catch (RemoteException e) {
+
+ }
+ mAudioServerStateListeners.put(asd.asBinder(), asdp);
+ }
+ }
+
+ public void unregisterAudioServerStateDispatcher(IAudioServerStateDispatcher asd) {
+ checkMonitorAudioServerStatePermission();
+ synchronized (mAudioServerStateListeners) {
+ AsdProxy asdp = mAudioServerStateListeners.remove(asd.asBinder());
+ if (asdp == null) {
+ Slog.w(TAG, "Trying to unregister unknown audioserver state dispatcher for pid "
+ + Binder.getCallingPid() + " / uid " + Binder.getCallingUid());
+ return;
+ } else {
+ asd.asBinder().unlinkToDeath(asdp, 0/*flags*/);
+ }
+ }
+ }
+
+ public boolean isAudioServerRunning() {
+ checkMonitorAudioServerStatePermission();
+ return (AudioSystem.checkAudioFlinger() == AudioSystem.AUDIO_STATUS_OK);
+ }
+
+ //======================
// misc
//======================
private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies =
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b877184..21d86c4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -29,6 +29,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -76,6 +77,7 @@
import android.view.KeyEvent;
import android.view.ViewConfiguration;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -180,9 +182,8 @@
updateUser();
+ registerPackageBroadcastReceivers();
// TODO(jaewan): Query per users
- // TODO(jaewan): Add listener to know changes in list of services.
- // Refer TvInputManagerService.registerBroadcastReceivers()
buildMediaSessionService2List();
}
@@ -437,12 +438,74 @@
mHandler.postSessionsChanged(session.getUserId());
}
+ private void registerPackageBroadcastReceivers() {
+ // TODO(jaewan): Only consider changed packages when building session service list
+ // when we make this multi-user aware. At that time,
+ // use PackageMonitor.getChangingUserId() to know which user has changed.
+ IntentFilter filter = new IntentFilter();
+ filter.addDataScheme("package");
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+ filter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
+ filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+
+ getContext().registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int changeUserId = intent.getIntExtra(
+ Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (changeUserId == UserHandle.USER_NULL) {
+ Log.w(TAG, "Intent broadcast does not contain user handle: "+ intent);
+ return;
+ }
+ // Check if the package is replacing (i.e. reinstalling)
+ final boolean isReplacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ // TODO(jaewan): Add multi-user support with this.
+ // final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+
+ if (DEBUG) {
+ Log.d(TAG, "Received change in packages, intent=" + intent);
+ }
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_ADDED:
+ case Intent.ACTION_PACKAGE_REMOVED:
+ case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+ case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+ if (isReplacing) {
+ // Ignore if the package(s) are replacing. In that case, followings will
+ // happen in order.
+ // 1. ACTION_PACKAGE_REMOVED with isReplacing=true
+ // 2. ACTION_PACKAGE_ADDED with isReplacing=true
+ // 3. ACTION_PACKAGE_REPLACED
+ // (Note that ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE and
+ // ACTION_EXTERNAL_APPLICATIONS_AVAILABLE will be also called with
+ // isReplacing=true for both ASEC hosted packages and packages in
+ // external storage)
+ // Since we only want to update session service list once, ignore
+ // actions above when replacing.
+ // Replacing will be handled only once with the ACTION_PACKAGE_REPLACED.
+ break;
+ }
+ // pass-through
+ case Intent.ACTION_PACKAGE_CHANGED:
+ case Intent.ACTION_PACKAGES_SUSPENDED:
+ case Intent.ACTION_PACKAGES_UNSUSPENDED:
+ case Intent.ACTION_PACKAGE_REPLACED:
+ buildMediaSessionService2List();
+ }
+ }
+ }, UserHandle.ALL, filter, null, BackgroundThread.getHandler());
+ }
+
private void buildMediaSessionService2List() {
if (DEBUG) {
Log.d(TAG, "buildMediaSessionService2List");
}
-
- // TODO(jaewan): Query per users.
+ // TODO(jaewan): Also query for managed profile users.
// TODO(jaewan): Similar codes are also at the updatable. Can't we share codes?
PackageManager manager = getContext().getPackageManager();
List<ResolveInfo> services = new ArrayList<>();
@@ -458,9 +521,13 @@
services.addAll(sessionServices);
}
synchronized (mLock) {
- mSessions.clear();
- if (services == null) {
- return;
+ // List to keep the session services that need be removed because they don't exist
+ // in the 'services' above.
+ List<MediaSession2Record> removeCandidates = new ArrayList<>();
+ for (int i = 0; i < mSessions.size(); i++) {
+ if (mSessions.get(i).getToken().getType() != TYPE_SESSION) {
+ removeCandidates.add(mSessions.get(i));
+ }
}
for (int i = 0; i < services.size(); i++) {
if (services.get(i) == null || services.get(i).serviceInfo == null) {
@@ -475,17 +542,35 @@
} catch (NameNotFoundException e) {
continue;
}
-
+ SessionToken2 token;
try {
- SessionToken2 token = new SessionToken2(getContext(),
+ token = new SessionToken2(getContext(),
serviceInfo.packageName, serviceInfo.name, uid);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Invalid session service", e);
+ continue;
+ }
+ boolean found = false;
+ for (int j = 0; j < mSessions.size(); j++) {
+ if (token.equals(mSessions.get(j).getToken())) {
+ // If the token already exists, keep it in the mSessions.
+ removeCandidates.remove(mSessions.get(j));
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // New session service is found.
MediaSession2Record record = new MediaSession2Record(getContext(),
token, mSessionDestroyedListener);
mSessions.add(record);
- } catch (IllegalArgumentException e) {
- Log.d(TAG, "Invalid session service", e);
}
}
+ for (int i = 0; i < removeCandidates.size(); i++) {
+ removeCandidates.get(i).onSessionDestroyed();
+ mSessions.remove(removeCandidates.get(i));
+ }
+ removeCandidates.clear();
}
if (DEBUG) {
Log.d(TAG, "Found " + mSessions.size() + " session services");
@@ -1503,12 +1588,14 @@
return tokens;
}
+ // TODO(jaewan): Protect this API with permission
@Override
public void addSessionTokensListener(ISessionTokensListener listener, int userId,
String packageName) {
// TODO(jaewan): Implement.
}
+ // TODO(jaewan): Protect this API with permission
@Override
public void removeSessionTokensListener(ISessionTokensListener listener) {
// TODO(jaewan): Implement
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 7e3b551..44b83d8 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -18,8 +18,10 @@
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
+import android.app.Notification;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -44,6 +46,7 @@
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
@@ -61,6 +64,8 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
import com.android.server.LocalServices;
import libcore.io.IoUtils;
@@ -89,6 +94,7 @@
private final H mHandler;
private final SettingsObserver mSettingsObserver;
@VisibleForTesting protected final AppOpsManager mAppOps;
+ @VisibleForTesting protected final NotificationManager mNotificationManager;
protected ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
@@ -112,12 +118,14 @@
protected String mDefaultRuleEveryNightName;
protected String mDefaultRuleEventsName;
+ @VisibleForTesting protected boolean mIsBootComplete;
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
mContext = context;
mHandler = new H(looper);
addCallback(mMetrics);
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mNotificationManager = context.getSystemService(NotificationManager.class);
mDefaultConfig = new ZenModeConfig();
setDefaultZenRules(mContext);
@@ -197,6 +205,8 @@
mHandler.postMetricsTimer();
cleanUpZenRules();
evaluateZenMode("onSystemReady", true);
+ mIsBootComplete = true;
+ showZenUpgradeNotification(mZenMode);
}
public void onUserSwitched(int user) {
@@ -612,6 +622,10 @@
throws XmlPullParserException, IOException {
final ZenModeConfig config = ZenModeConfig.readXml(parser);
if (config != null) {
+ if (config.version < ZenModeConfig.XML_VERSION) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
+ }
if (forRestore) {
//TODO: http://b/22388012
if (config.user != UserHandle.USER_SYSTEM) {
@@ -755,8 +769,10 @@
return Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_OFF);
}
- private void setZenModeSetting(int zen) {
+ @VisibleForTesting
+ protected void setZenModeSetting(int zen) {
Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zen);
+ showZenUpgradeNotification(zen);
}
private int getPreviousRingerModeSetting() {
@@ -1139,6 +1155,41 @@
}
}
+ private void showZenUpgradeNotification(int zen) {
+ final boolean showNotification = mIsBootComplete
+ && zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ && Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
+
+ if (showNotification) {
+ mNotificationManager.notify(TAG, SystemMessage.NOTE_ZEN_UPGRADE,
+ createZenUpgradeNotification());
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+ }
+ }
+
+ @VisibleForTesting
+ protected Notification createZenUpgradeNotification() {
+ Intent intent = new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS)
+ .setPackage("com.android.settings")
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ mContext.getResources().getString(R.string.global_action_settings));
+ return new Notification.Builder(mContext, SystemNotificationChannels.SYSTEM_CHANGES)
+ .setSmallIcon(R.drawable.ic_settings)
+ .setContentTitle(mContext.getResources().getString(
+ R.string.zen_upgrade_notification_title))
+ .setContentText(mContext.getResources().getString(
+ R.string.zen_upgrade_notification_content))
+ .setAutoCancel(true)
+ .setLocalOnly(true)
+ .addExtras(extras)
+ .setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0, null))
+ .build();
+ }
+
private final class Metrics extends Callback {
private static final String COUNTER_PREFIX = "dnd_mode_";
private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b79caca..1746dd1 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -63,9 +63,8 @@
public static final int DEXOPT_STORAGE_DE = 1 << 8;
/** Indicates that dexopt is invoked from the background service. */
public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
- /* Indicates that dexopt should not restrict access to private APIs.
- * Must be kept in sync with com.android.internal.os.ZygoteInit. */
- public static final int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10;
+ /** Indicates that dexopt should restrict access to private APIs. */
+ public static final int DEXOPT_ENABLE_HIDDEN_API_CHECKS = 1 << 10;
// NOTE: keep in sync with installd
public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 2c68e67..458d725 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -57,7 +57,7 @@
import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
-import static com.android.server.pm.Installer.DEXOPT_DISABLE_HIDDEN_API_CHECKS;
+import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -528,11 +528,9 @@
boolean isPublic = !info.isForwardLocked() &&
(!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
- // System apps are invoked with a runtime flag which exempts them from
- // restrictions on hidden API usage. We dexopt with the same runtime flag
- // otherwise offending methods would have to be re-verified at runtime
- // and we want to avoid the performance overhead of that.
- int hiddenApiFlag = info.isAllowedToUseHiddenApi() ? DEXOPT_DISABLE_HIDDEN_API_CHECKS : 0;
+ // Some apps are executed with restrictions on hidden API usage. If this app is one
+ // of them, pass a flag to dexopt to enable the same restrictions during compilation.
+ int hiddenApiFlag = info.isAllowedToUseHiddenApi() ? 0 : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
int dexFlags =
(isPublic ? DEXOPT_PUBLIC : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
@@ -655,8 +653,8 @@
if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
flagsList.add("idle_background_job");
}
- if ((flags & DEXOPT_DISABLE_HIDDEN_API_CHECKS) == DEXOPT_DISABLE_HIDDEN_API_CHECKS) {
- flagsList.add("disable_hidden_api_checks");
+ if ((flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) == DEXOPT_ENABLE_HIDDEN_API_CHECKS) {
+ flagsList.add("enable_hidden_api_checks");
}
return String.join(",", flagsList);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e869f58..6c3beaf 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -59,6 +59,7 @@
private final IRecentsAnimationRunner mRunner;
private final RecentsAnimationCallbacks mCallbacks;
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
+ private final int mDisplayId;
// The recents component app token that is shown behind the visibile tasks
private AppWindowToken mHomeAppToken;
@@ -159,8 +160,6 @@
};
/**
- * Initializes a new RecentsAnimationController.
- *
* @param remoteAnimationRunner The remote runner which should be notified when the animation is
* ready to start or has been canceled
* @param callbacks Callbacks to be made when the animation finishes
@@ -171,16 +170,19 @@
mService = service;
mRunner = remoteAnimationRunner;
mCallbacks = callbacks;
+ mDisplayId = displayId;
+ }
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
- final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
- if (visibleTasks.isEmpty()) {
- cancelAnimation();
- return;
- }
-
+ /**
+ * Initializes the recents animation controller. This is a separate call from the constructor
+ * because it may call cancelAnimation() which needs to properly clean up the controller
+ * in the window manager.
+ */
+ public void initialize() {
// Make leashes for each of the visible tasks and add it to the recents animation to be
// started
+ final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+ final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
final int taskCount = visibleTasks.size();
for (int i = 0; i < taskCount; i++) {
final Task task = visibleTasks.get(i);
@@ -193,6 +195,12 @@
addAnimation(task);
}
+ // Skip the animation if there is nothing to animate
+ if (mPendingAnimations.isEmpty()) {
+ cancelAnimation();
+ return;
+ }
+
// Adjust the wallpaper visibility for the showing home activity
final AppWindowToken recentsComponentAppToken =
dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
@@ -222,8 +230,10 @@
}
void startAnimation() {
- if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart);
- if (!mPendingStart) {
+ if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
+ + " mCanceled=" + mCanceled);
+ if (!mPendingStart || mCanceled) {
+ // Skip starting if we've already started or canceled the animation
return;
}
try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c2ed2ae..e68fbdb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2680,6 +2680,7 @@
cancelRecentsAnimation();
mRecentsAnimationController = new RecentsAnimationController(this,
recentsAnimationRunner, callbacks, displayId);
+ mRecentsAnimationController.initialize();
}
}
@@ -2687,6 +2688,19 @@
return mRecentsAnimationController;
}
+ /**
+ * @return Whether the next recents animation can continue to start. Called from
+ * {@link RecentsAnimation#startRecentsActivity}.
+ */
+ public boolean canStartRecentsAnimation() {
+ synchronized (mWindowMap) {
+ if (mAppTransition.isTransitionSet()) {
+ return false;
+ }
+ return true;
+ }
+ }
+
public void cancelRecentsAnimation() {
synchronized (mWindowMap) {
if (mRecentsAnimationController != null) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
index 98c428d..091d9bd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
@@ -45,6 +45,7 @@
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* Unit tests for {@link android.app.admin.SystemUpdatePolicy}.
@@ -253,6 +254,147 @@
}
+ @Test
+ public void testInstallationOptionWithoutFreeze() {
+ // Also duplicated at com.google.android.gts.deviceowner.SystemUpdatePolicyTest
+ final long millis_2018_01_01 = TimeUnit.SECONDS.toMillis(1514764800);
+
+ SystemUpdatePolicy p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+ assertInstallationOption(SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, Long.MAX_VALUE,
+ millis_2018_01_01, p);
+
+ p = SystemUpdatePolicy.createPostponeInstallPolicy();
+ assertInstallationOption(SystemUpdatePolicy.TYPE_POSTPONE, Long.MAX_VALUE,
+ millis_2018_01_01, p);
+
+ p = SystemUpdatePolicy.createWindowedInstallPolicy(120, 180); // 2:00 - 3:00
+ // 00:00 is two hours before the next window
+ assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(2),
+ millis_2018_01_01, p);
+ // 02:00 is within the current maintenance window, and one hour until the window ends
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(1),
+ millis_2018_01_01 + TimeUnit.HOURS.toMillis(2), p);
+ // 04:00 is 22 hours from the window next day
+ assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(22),
+ millis_2018_01_01 + TimeUnit.HOURS.toMillis(4), p);
+
+ p = SystemUpdatePolicy.createWindowedInstallPolicy(22 * 60, 2 * 60); // 22:00 - 2:00
+ // 21:00 is one hour from the next window
+ assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(1),
+ millis_2018_01_01 + TimeUnit.HOURS.toMillis(21), p);
+ // 00:00 is two hours from the end of current window
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(2),
+ millis_2018_01_01, p);
+ // 03:00 is 22 hours from the window today
+ assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(19),
+ millis_2018_01_01 + TimeUnit.HOURS.toMillis(3), p);
+ }
+
+ @Test
+ public void testInstallationOptionWithFreeze() throws Exception {
+ final long millis_2016_02_29 = TimeUnit.SECONDS.toMillis(1456704000);
+ final long millis_2017_01_31 = TimeUnit.SECONDS.toMillis(1485820800);
+ final long millis_2017_02_28 = TimeUnit.SECONDS.toMillis(1488240000);
+ final long millis_2018_01_01 = TimeUnit.SECONDS.toMillis(1514764800);
+ final long millis_2018_08_01 = TimeUnit.SECONDS.toMillis(1533081600);
+
+ SystemUpdatePolicy p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+ setFreezePeriods(p, "01-01", "01-31");
+ // Inside a freeze period
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(31),
+ millis_2018_01_01, p);
+ // Device is outside freeze between 2/28 to 12/31 inclusive
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(307),
+ millis_2017_02_28, p);
+
+ // Freeze period contains leap day Feb 29
+ p = SystemUpdatePolicy.createPostponeInstallPolicy();
+ setFreezePeriods(p, "02-01", "03-15");
+ // Freezed until 3/31, note 2016 is a leap year
+ assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(16),
+ millis_2016_02_29, p);
+ // Freezed until 3/31, note 2017 is not a leap year
+ assertInstallationOption(SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(16),
+ millis_2017_02_28, p);
+ // Next freeze is 2018/2/1
+ assertInstallationOption(SystemUpdatePolicy.TYPE_POSTPONE, TimeUnit.DAYS.toMillis(31),
+ millis_2018_01_01, p);
+
+ // Freeze period start on or right after leap day
+ p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+ setFreezePeriods(p, "03-01", "03-31");
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(1),
+ millis_2016_02_29, p);
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(1),
+ millis_2017_02_28, p);
+ setFreezePeriods(p, "02-28", "03-15");
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(16),
+ millis_2016_02_29, p);
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(16),
+ millis_2017_02_28, p);
+
+ // Freeze period end on or right after leap day
+ p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+ setFreezePeriods(p, "02-01", "02-28");
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(1),
+ millis_2016_02_29, p);
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(1),
+ millis_2017_02_28, p);
+ p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+ setFreezePeriods(p, "02-01", "03-01");
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(2),
+ millis_2016_02_29, p);
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.DAYS.toMillis(2),
+ millis_2017_02_28, p);
+
+ // Freeze period with maintenance window
+ p = SystemUpdatePolicy.createWindowedInstallPolicy(23 * 60, 1 * 60); // 23:00 - 1:00
+ setFreezePeriods(p, "02-01", "02-28");
+ // 00:00 is within the current window, outside freeze period
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(1),
+ millis_2018_01_01, p);
+ // Last day of feeze period, which ends in 22 hours
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(22),
+ millis_2017_02_28 + TimeUnit.HOURS.toMillis(2), p);
+ // Last day before the next freeze, and within window
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.HOURS.toMillis(1),
+ millis_2017_01_31, p);
+ // Last day before the next freeze, and there is still a partial maintenance window before
+ // the freeze.
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_PAUSE, TimeUnit.HOURS.toMillis(19),
+ millis_2017_01_31 + TimeUnit.HOURS.toMillis(4), p);
+
+ // Two freeze periods
+ p = SystemUpdatePolicy.createAutomaticInstallPolicy();
+ setFreezePeriods(p, "05-01", "06-01", "12-01", "01-31");
+ // automatic policy for August, September, November and December
+ assertInstallationOption(
+ SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(122),
+ millis_2018_08_01, p);
+ }
+
+ private void assertInstallationOption(int expectedType, long expectedTime, long now,
+ SystemUpdatePolicy p) {
+ assertEquals(expectedType, p.getInstallationOptionAt(now).getType());
+ assertEquals(expectedTime, p.getInstallationOptionAt(now).getEffectiveTime());
+ }
+
private void testFreezePeriodsSucceeds(String...dates) throws Exception {
SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
setFreezePeriods(p, dates);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index c532a8a..6144c51 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -17,21 +17,32 @@
package com.android.server.notification;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.TestCase.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
import android.media.AudioAttributes;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
@@ -46,15 +57,24 @@
public class ZenModeHelperTest extends UiServiceTestCase {
@Mock ConditionProviders mConditionProviders;
+ @Mock NotificationManager mNotificationManager;
+ @Mock private Resources mResources;
private TestableLooper mTestableLooper;
private ZenModeHelper mZenModeHelperSpy;
+ private Context mContext;
+ private ContentResolver mContentResolver;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- mZenModeHelperSpy = spy(new ZenModeHelper(getContext(), mTestableLooper.getLooper(),
+ mContext = spy(getContext());
+ mContentResolver = mContext.getContentResolver();
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
+
+ mZenModeHelperSpy = spy(new ZenModeHelper(mContext, mTestableLooper.getLooper(),
mConditionProviders));
}
@@ -194,4 +214,31 @@
verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
}
}
+
+ @Test
+ public void testZenUpgradeNotification() {
+ // shows zen upgrade notification if stored settings says to shows, boot is completed
+ // and we're setting zen mode on
+ Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
+ mZenModeHelperSpy.mIsBootComplete = true;
+ mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+ verify(mZenModeHelperSpy, times(1)).createZenUpgradeNotification();
+ verify(mNotificationManager, times(1)).notify(eq(ZenModeHelper.TAG),
+ eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
+ assertEquals(0, Settings.Global.getInt(mContentResolver,
+ Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, -1));
+ }
+
+ @Test
+ public void testNoZenUpgradeNotification() {
+ // doesn't show upgrade notification if stored settings says don't show
+ Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+ mZenModeHelperSpy.mIsBootComplete = true;
+ mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+ verify(mZenModeHelperSpy, never()).createZenUpgradeNotification();
+ verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
+ eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 6806947..a7fc470 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1786,7 +1786,7 @@
mGadgetProxy = IUsbGadget.getService();
mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
USB_GADGET_HAL_DEATH_COOKIE);
- if (!mCurrentFunctionsApplied) {
+ if (!mCurrentFunctionsApplied && !mCurrentUsbFunctionsRequested) {
setEnabledFunctions(mCurrentFunctions, false);
}
} catch (NoSuchElementException e) {
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 5fcff18..024bd30 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -29,7 +29,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
@@ -82,7 +81,7 @@
private int mConnectionProperties;
private String mDisconnectMessage;
private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
- private long mConnectElapsedTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
+ private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED;
private StatusHints mStatusHints;
private Bundle mExtras;
private Set<String> mPreviousExtraKeys;
@@ -584,30 +583,36 @@
}
/**
- * Sets the connection start time of the {@code Conference}. Should be specified in wall-clock
- * time returned by {@link System#currentTimeMillis()}.
+ * Sets the connection start time of the {@code Conference}. This is used in the call log to
+ * indicate the date and time when the conference took place.
+ * <p>
+ * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
* <p>
* When setting the connection time, you should always set the connection elapsed time via
- * {@link #setConnectionElapsedTime(long)}.
+ * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected.
*
- * @param connectionTimeMillis The connection time, in milliseconds.
+ * @param connectionTimeMillis The connection time, in milliseconds, as returned by
+ * {@link System#currentTimeMillis()}.
*/
public final void setConnectionTime(long connectionTimeMillis) {
mConnectTimeMillis = connectionTimeMillis;
}
/**
- * Sets the elapsed time since system boot when the {@link Conference} was connected.
- * This is used to determine the duration of the {@link Conference}.
+ * Sets the start time of the {@link Conference} which is the basis for the determining the
+ * duration of the {@link Conference}.
* <p>
- * When setting the connection elapsed time, you should always set the connection time via
+ * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
+ * zone changes do not impact the conference duration.
+ * <p>
+ * When setting this, you should also set the connection time via
* {@link #setConnectionTime(long)}.
*
- * @param connectionElapsedTime The connection time, as measured by
+ * @param connectionStartElapsedRealTime The connection time, as measured by
* {@link SystemClock#elapsedRealtime()}.
*/
- public final void setConnectionElapsedTime(long connectionElapsedTime) {
- mConnectElapsedTimeMillis = connectionElapsedTime;
+ public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
+ mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
}
/**
@@ -642,8 +647,8 @@
* @return The elapsed time at which the {@link Conference} was connected.
* @hide
*/
- public final long getConnectElapsedTime() {
- return mConnectElapsedTimeMillis;
+ public final long getConnectionStartElapsedRealTime() {
+ return mConnectionStartElapsedRealTime;
}
/**
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index d8599e8..3229705 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2299,7 +2299,7 @@
*
* @hide
*/
- public final void setConnectElapsedTimeMillis(long connectElapsedTimeMillis) {
+ public final void setConnectionStartElapsedRealTime(long connectElapsedTimeMillis) {
mConnectElapsedTimeMillis = connectElapsedTimeMillis;
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 211699e..1547857 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -21,7 +21,6 @@
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -2007,7 +2006,7 @@
null : conference.getVideoProvider().getInterface(),
conference.getVideoState(),
conference.getConnectTimeMillis(),
- conference.getConnectElapsedTime(),
+ conference.getConnectionStartElapsedRealTime(),
conference.getStatusHints(),
conference.getExtras());