Merge "Disable long press recents whenever overview service is bound" into pi-dev
diff --git a/api/current.txt b/api/current.txt
index d37d3d1..64b2d58 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7041,7 +7041,8 @@
method public int getBackoffPolicy();
method public android.content.ClipData getClipData();
method public int getClipGrantFlags();
- method public long getEstimatedNetworkBytes();
+ method public long getEstimatedNetworkDownloadBytes();
+ method public long getEstimatedNetworkUploadBytes();
method public android.os.PersistableBundle getExtras();
method public long getFlexMillis();
method public int getId();
@@ -7058,8 +7059,10 @@
method public long getTriggerContentMaxDelay();
method public long getTriggerContentUpdateDelay();
method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
+ method public boolean isImportantWhileForeground();
method public boolean isPeriodic();
method public boolean isPersisted();
+ method public boolean isPrefetch();
method public boolean isRequireBatteryNotLow();
method public boolean isRequireCharging();
method public boolean isRequireDeviceIdle();
@@ -7085,15 +7088,15 @@
method public android.app.job.JobInfo build();
method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
method public android.app.job.JobInfo.Builder setClipData(android.content.ClipData, int);
- method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
+ method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle);
method public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
- method public android.app.job.JobInfo.Builder setIsPrefetch(boolean);
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
method public android.app.job.JobInfo.Builder setPeriodic(long);
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
method public android.app.job.JobInfo.Builder setPersisted(boolean);
+ method public android.app.job.JobInfo.Builder setPrefetch(boolean);
method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest);
method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
@@ -7163,10 +7166,11 @@
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
- ctor public JobWorkItem(android.content.Intent, long);
+ ctor public JobWorkItem(android.content.Intent, long, long);
method public int describeContents();
method public int getDeliveryCount();
- method public long getEstimatedNetworkBytes();
+ method public long getEstimatedNetworkDownloadBytes();
+ method public long getEstimatedNetworkUploadBytes();
method public android.content.Intent getIntent();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;
@@ -24480,29 +24484,6 @@
field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
}
- public final class Rating2 {
- method public static android.media.Rating2 fromBundle(android.content.Context, android.os.Bundle);
- method public float getPercentRating();
- method public int getRatingStyle();
- method public float getStarRating();
- method public boolean hasHeart();
- method public boolean isRated();
- method public boolean isThumbUp();
- method public static android.media.Rating2 newHeartRating(android.content.Context, boolean);
- method public static android.media.Rating2 newPercentageRating(android.content.Context, float);
- method public static android.media.Rating2 newStarRating(android.content.Context, int, float);
- method public static android.media.Rating2 newThumbRating(android.content.Context, boolean);
- method public static android.media.Rating2 newUnratedRating(android.content.Context, int);
- method public android.os.Bundle toBundle();
- field public static final int RATING_3_STARS = 3; // 0x3
- field public static final int RATING_4_STARS = 4; // 0x4
- field public static final int RATING_5_STARS = 5; // 0x5
- field public static final int RATING_HEART = 1; // 0x1
- field public static final int RATING_NONE = 0; // 0x0
- field public static final int RATING_PERCENTAGE = 6; // 0x6
- field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
- }
-
public deprecated class RemoteControlClient {
ctor public RemoteControlClient(android.app.PendingIntent);
ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
@@ -27278,6 +27259,7 @@
method public static long getMobileTxBytes();
method public static long getMobileTxPackets();
method public static int getThreadStatsTag();
+ method public static int getThreadStatsUid();
method public static long getTotalRxBytes();
method public static long getTotalRxPackets();
method public static long getTotalTxBytes();
@@ -27297,7 +27279,7 @@
method public static void incrementOperationCount(int);
method public static void incrementOperationCount(int, int);
method public static void setThreadStatsTag(int);
- method public static void setThreadStatsUidSelf();
+ method public static void setThreadStatsUid(int);
method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
method public static void tagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException;
method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
@@ -32058,7 +32040,6 @@
ctor public Build.VERSION();
field public static final java.lang.String BASE_OS;
field public static final java.lang.String CODENAME;
- field public static final int FIRST_SDK_INT;
field public static final java.lang.String INCREMENTAL;
field public static final int MIN_SUPPORTED_TARGET_SDK_INT;
field public static final int PREVIEW_SDK_INT;
@@ -33094,8 +33075,6 @@
}
public final class SystemClock {
- method public static java.time.Clock currentNetworkTimeClock();
- method public static long currentNetworkTimeMillis();
method public static long currentThreadTimeMillis();
method public static long elapsedRealtime();
method public static long elapsedRealtimeNanos();
@@ -41676,6 +41655,8 @@
public abstract class CellIdentity implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.CharSequence getOperatorAlphaLong();
+ method public java.lang.CharSequence getOperatorAlphaShort();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentity> CREATOR;
}
@@ -41685,8 +41666,6 @@
method public int getLatitude();
method public int getLongitude();
method public int getNetworkId();
- method public java.lang.CharSequence getOperatorAlphaLong();
- method public java.lang.CharSequence getOperatorAlphaShort();
method public int getSystemId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
@@ -41702,8 +41681,6 @@
method public deprecated int getMnc();
method public java.lang.String getMncString();
method public java.lang.String getMobileNetworkOperator();
- method public java.lang.CharSequence getOperatorAlphaLong();
- method public java.lang.CharSequence getOperatorAlphaShort();
method public deprecated int getPsc();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityGsm> CREATOR;
@@ -41718,8 +41695,6 @@
method public deprecated int getMnc();
method public java.lang.String getMncString();
method public java.lang.String getMobileNetworkOperator();
- method public java.lang.CharSequence getOperatorAlphaLong();
- method public java.lang.CharSequence getOperatorAlphaShort();
method public int getPci();
method public int getTac();
method public void writeToParcel(android.os.Parcel, int);
@@ -41744,8 +41719,6 @@
method public deprecated int getMnc();
method public java.lang.String getMncString();
method public java.lang.String getMobileNetworkOperator();
- method public java.lang.CharSequence getOperatorAlphaLong();
- method public java.lang.CharSequence getOperatorAlphaShort();
method public int getPsc();
method public int getUarfcn();
method public void writeToParcel(android.os.Parcel, int);
@@ -45071,18 +45044,6 @@
field public static final deprecated boolean RELEASE = true;
}
- public class DataUnit extends java.lang.Enum {
- method public long toBytes(long);
- method public static android.util.DataUnit valueOf(java.lang.String);
- method public static final android.util.DataUnit[] values();
- enum_constant public static final android.util.DataUnit GIBIBYTES;
- enum_constant public static final android.util.DataUnit GIGABYTES;
- enum_constant public static final android.util.DataUnit KIBIBYTES;
- enum_constant public static final android.util.DataUnit KILOBYTES;
- enum_constant public static final android.util.DataUnit MEBIBYTES;
- enum_constant public static final android.util.DataUnit MEGABYTES;
- }
-
public class DebugUtils {
method public static boolean isObjectSelected(java.lang.Object);
}
diff --git a/api/removed.txt b/api/removed.txt
index 1228fd1..4279c31 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -54,6 +54,24 @@
}
+package android.app.job {
+
+ public class JobInfo implements android.os.Parcelable {
+ method public deprecated long getEstimatedNetworkBytes();
+ }
+
+ public static final class JobInfo.Builder {
+ method public deprecated android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
+ method public deprecated android.app.job.JobInfo.Builder setIsPrefetch(boolean);
+ }
+
+ public final class JobWorkItem implements android.os.Parcelable {
+ ctor public deprecated JobWorkItem(android.content.Intent, long);
+ method public deprecated long getEstimatedNetworkBytes();
+ }
+
+}
+
package android.app.usage {
public final class StorageStats implements android.os.Parcelable {
@@ -261,6 +279,10 @@
method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
}
+ public class TrafficStats {
+ method public static deprecated void setThreadStatsUidSelf();
+ }
+
}
package android.os {
diff --git a/api/test-current.txt b/api/test-current.txt
index 85ed4b9..d323725 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -482,6 +482,7 @@
package android.os {
public static class Build.VERSION {
+ field public static final int FIRST_SDK_INT;
field public static final int RESOURCES_SDK_INT;
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 556709b..1aef0c4 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -206,7 +206,9 @@
tests/e2e/GaugeMetric_e2e_push_test.cpp \
tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp \
tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp \
- tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+ tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp \
+ tests/e2e/Anomaly_count_e2e_test.cpp \
+ tests/e2e/Anomaly_duration_sum_e2e_test.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 1be4dc5..a07a355 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -69,6 +69,11 @@
void dumpStates(FILE* out, bool verbose);
private:
+ // For testing only.
+ inline sp<AlarmMonitor> getAnomalyAlarmMonitor() const {
+ return mAnomalyAlarmMonitor;
+ }
+
mutable mutex mMetricsMutex;
std::unordered_map<ConfigKey, sp<MetricsManager>> mMetricsManagers;
@@ -133,13 +138,15 @@
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
-
-
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
-
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
};
} // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 49de1ac..f0960e3 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -66,6 +66,9 @@
void AnomalyTracker::advanceMostRecentBucketTo(const int64_t& bucketNum) {
VLOG("advanceMostRecentBucketTo() called.");
+ if (mNumOfPastBuckets <= 0) {
+ return;
+ }
if (bucketNum <= mMostRecentBucketNum) {
ALOGW("Cannot advance buckets backwards (bucketNum=%lld but mMostRecentBucketNum=%lld)",
(long long)bucketNum, (long long)mMostRecentBucketNum);
@@ -170,7 +173,8 @@
int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
const int64_t& bucketNum) const {
- if (bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets
+ if (bucketNum < 0 || mMostRecentBucketNum < 0
+ || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets
|| bucketNum > mMostRecentBucketNum) {
return 0;
}
@@ -241,14 +245,10 @@
}
bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
- const MetricDimensionKey& key) {
+ const MetricDimensionKey& key) const {
const auto& it = mRefractoryPeriodEndsSec.find(key);
if (it != mRefractoryPeriodEndsSec.end()) {
- if (timestampNs < it->second * NS_PER_SEC) {
- return true;
- } else {
- mRefractoryPeriodEndsSec.erase(key);
- }
+ return timestampNs < it->second * NS_PER_SEC;
}
return false;
}
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index d3da7dc..ae0af64 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -113,6 +113,13 @@
}
protected:
+ // For testing only.
+ // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
+ // returns 0.
+ virtual uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const {
+ return 0; // The base AnomalyTracker class doesn't have alarms.
+ }
+
// statsd_config.proto Alert message that defines this tracker.
const Alert mAlert;
@@ -159,8 +166,7 @@
void subtractValueFromSum(const MetricDimensionKey& key, const int64_t& bucketValue);
// Returns true if in the refractory period, else false.
- // If there is a stored refractory period but it ended prior to timestampNs, it is removed.
- bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key);
+ bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key) const;
// Calculates the corresponding bucket index within the circular array.
// Requires bucketNum >= 0.
@@ -176,6 +182,9 @@
FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
};
} // namespace statsd
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 79067eb..cdc4251 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -38,11 +38,10 @@
void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
const uint64_t& timestampNs) {
// Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely.
- uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1)/ NS_PER_SEC) + 1; // round up
+ uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1) / NS_PER_SEC) + 1; // round up
if (isInRefractoryPeriod(timestampNs, dimensionKey)) {
- // TODO: Bug! By the refractory's end, the data might be erased and the alarm inapplicable.
- VLOG("Setting a delayed anomaly alarm lest it fall in the refractory period");
- timestampSec = getRefractoryPeriodEndsSec(dimensionKey) + 1;
+ VLOG("Not setting anomaly alarm since it would fall in the refractory period.");
+ return;
}
auto itr = mAlarms.find(dimensionKey);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
index 92bb2bc..53155d9 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -52,6 +52,13 @@
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) override;
protected:
+ // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
+ // returns 0.
+ uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const override {
+ auto it = mAlarms.find(dimensionKey);
+ return it == mAlarms.end() ? 0 : it->second->timestampSec;
+ }
+
// The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
// are still active.
std::unordered_map<MetricDimensionKey, sp<const InternalAlarm>> mAlarms;
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index ea45f43..4983f96 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -188,7 +188,7 @@
// Convenience to compute the current bucket's end time, which is always aligned with the
// start time of the metric.
- uint64_t getCurrentBucketEndTimeNs() {
+ uint64_t getCurrentBucketEndTimeNs() const {
return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 46a9b34..05ce84d 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -178,6 +178,12 @@
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
+
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 991a76a..ddfb8cc 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -109,7 +109,7 @@
// Predict the anomaly timestamp given the current status.
virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
- const uint64_t currentTimestamp) const = 0;
+ const int64_t currentTimestamp) const = 0;
// Dump internal states for debugging
virtual void dumpStates(FILE* out, bool verbose) const = 0;
@@ -118,12 +118,19 @@
}
protected:
+ uint64_t getCurrentBucketEndTimeNs() const {
+ return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
+ }
+
// Starts the anomaly alarm.
void startAnomalyAlarm(const uint64_t eventTime) {
for (auto& anomalyTracker : mAnomalyTrackers) {
if (anomalyTracker != nullptr) {
- anomalyTracker->startAlarm(mEventKey,
- predictAnomalyTimestampNs(*anomalyTracker, eventTime));
+ const uint64_t alarmTimestampNs =
+ predictAnomalyTimestampNs(*anomalyTracker, eventTime);
+ if (alarmTimestampNs > 0) {
+ anomalyTracker->startAlarm(mEventKey, alarmTimestampNs);
+ }
}
}
}
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index c9547cf..df9e6ae 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -313,7 +313,7 @@
}
int64_t MaxDurationTracker::predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
- const uint64_t currentTimestamp) const {
+ const int64_t currentTimestamp) const {
// The allowed time we can continue in the current state is the
// (anomaly threshold) - max(elapsed time of the started mInfos).
int64_t maxElapsed = 0;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 0452d37..32d42fa 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -57,7 +57,7 @@
void onConditionChanged(bool condition, const uint64_t timestamp) override;
int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
- const uint64_t currentTimestamp) const override;
+ const int64_t currentTimestamp) const override;
void dumpStates(FILE* out, bool verbose) const override;
private:
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index b418a85..da79217 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -166,13 +166,13 @@
current_info.mDuration = mDuration;
(*output)[mEventKey].push_back(current_info);
mDurationFullBucket += mDuration;
- if (eventTimeNs > fullBucketEnd) {
- // End of full bucket, can send to anomaly tracker now.
- addPastBucketToAnomalyTrackers(mDurationFullBucket, mCurrentBucketNum);
- mDurationFullBucket = 0;
- }
VLOG(" duration: %lld", (long long)current_info.mDuration);
}
+ if (eventTimeNs > fullBucketEnd) {
+ // End of full bucket, can send to anomaly tracker now.
+ addPastBucketToAnomalyTrackers(mDurationFullBucket, mCurrentBucketNum);
+ mDurationFullBucket = 0;
+ }
if (mStarted.size() > 0) {
for (int i = 1; i < numBucketsForward; i++) {
@@ -186,6 +186,10 @@
addPastBucketToAnomalyTrackers(info.mDuration, mCurrentBucketNum + i);
VLOG(" add filling bucket with duration %lld", (long long)info.mDuration);
}
+ } else {
+ if (numBucketsForward >= 2) {
+ addPastBucketToAnomalyTrackers(0, mCurrentBucketNum + numBucketsForward - 1);
+ }
}
mDuration = 0;
@@ -320,57 +324,84 @@
}
int64_t OringDurationTracker::predictAnomalyTimestampNs(
- const DurationAnomalyTracker& anomalyTracker, const uint64_t eventTimestampNs) const {
+ const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const {
// TODO: Unit-test this and see if it can be done more efficiently (e.g. use int32).
- // All variables below represent durations (not timestamps).
+ // The anomaly threshold.
const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
- // The time until the current bucket ends. This is how much more 'space' it can hold.
- const int64_t currRemainingBucketSizeNs =
- mBucketSizeNs - (eventTimestampNs - mCurrentBucketStartTimeNs);
- if (currRemainingBucketSizeNs < 0) {
- ALOGE("OringDurationTracker currRemainingBucketSizeNs < 0");
- // This should never happen. Return the safest thing possible given that data is corrupt.
- return eventTimestampNs + thresholdNs;
- }
+ // The timestamp of the current bucket end.
+ const int64_t currentBucketEndNs = getCurrentBucketEndTimeNs();
+
+ // The past duration ns for the current bucket.
+ int64_t currentBucketPastNs = mDuration + mDurationFullBucket;
// As we move into the future, old buckets get overwritten (so their old data is erased).
-
// Sum of past durations. Will change as we overwrite old buckets.
- int64_t pastNs = mDuration + mDurationFullBucket;
- pastNs += anomalyTracker.getSumOverPastBuckets(mEventKey);
+ int64_t pastNs = currentBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
- // How much of the threshold is still unaccounted after considering pastNs.
- int64_t leftNs = thresholdNs - pastNs;
+ // The refractory period end timestamp for dimension mEventKey.
+ const int64_t refractoryPeriodEndNs =
+ anomalyTracker.getRefractoryPeriodEndsSec(mEventKey) * NS_PER_SEC;
- // First deal with the remainder of the current bucket.
- if (leftNs <= currRemainingBucketSizeNs) { // Predict the anomaly will occur in this bucket.
- return eventTimestampNs + leftNs;
+ // The anomaly should happen when accumulated wakelock duration is above the threshold and
+ // not within the refractory period.
+ int64_t anomalyTimestampNs =
+ std::max(eventTimestampNs + thresholdNs - pastNs, refractoryPeriodEndNs);
+ // If the predicted the anomaly timestamp is within the current bucket, return it directly.
+ if (anomalyTimestampNs <= currentBucketEndNs) {
+ return std::max(eventTimestampNs, anomalyTimestampNs);
}
- // The remainder of this bucket contributes, but we must then move to the next bucket.
- pastNs += currRemainingBucketSizeNs;
- // Now deal with the past buckets, starting with the oldest.
- for (int futBucketIdx = 0; futBucketIdx < anomalyTracker.getNumOfPastBuckets();
- futBucketIdx++) {
- // We now overwrite the oldest bucket with the previous 'current', and start a new
- // 'current'.
+ // Remove the old bucket.
+ if (anomalyTracker.getNumOfPastBuckets() > 0) {
pastNs -= anomalyTracker.getPastBucketValue(
- mEventKey, mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futBucketIdx);
- leftNs = thresholdNs - pastNs;
- if (leftNs <= mBucketSizeNs) { // Predict anomaly will occur in this bucket.
- return eventTimestampNs + currRemainingBucketSizeNs + (futBucketIdx * mBucketSizeNs) +
- leftNs;
- } else { // This bucket would be entirely filled, and we'll need to move to the next
- // bucket.
- pastNs += mBucketSizeNs;
+ mEventKey,
+ mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets());
+ // Add the remaining of the current bucket to the accumulated wakelock duration.
+ pastNs += (currentBucketEndNs - eventTimestampNs);
+ } else {
+ // The anomaly depends on only one bucket.
+ pastNs = 0;
+ }
+
+ // The anomaly will not happen in the current bucket. We need to iterate over the future buckets
+ // to predict the accumulated wakelock duration and determine the anomaly timestamp accordingly.
+ for (int futureBucketIdx = 1; futureBucketIdx <= anomalyTracker.getNumOfPastBuckets() + 1;
+ futureBucketIdx++) {
+ // The alarm candidate timestamp should meet two requirements:
+ // 1. the accumulated wakelock duration is above the threshold.
+ // 2. it is not within the refractory period.
+ // 3. the alarm timestamp falls in this bucket. Otherwise we need to flush the past buckets,
+ // find the new alarm candidate timestamp and check these requirements again.
+ const int64_t bucketEndNs = currentBucketEndNs + futureBucketIdx * mBucketSizeNs;
+ int64_t anomalyTimestampNs =
+ std::max(bucketEndNs - mBucketSizeNs + thresholdNs - pastNs, refractoryPeriodEndNs);
+ if (anomalyTimestampNs <= bucketEndNs) {
+ return anomalyTimestampNs;
+ }
+ if (anomalyTracker.getNumOfPastBuckets() <= 0) {
+ continue;
+ }
+
+ // No valid alarm timestamp is found in this bucket. The clock moves to the end of the
+ // bucket. Update the pastNs.
+ pastNs += mBucketSizeNs;
+ // 1. If the oldest past bucket is still in the past bucket window, we could fetch the past
+ // bucket and erase it from pastNs.
+ // 2. If the oldest past bucket is the current bucket, we should compute the
+ // wakelock duration in the current bucket and erase it from pastNs.
+ // 3. Otherwise all othe past buckets are ancient.
+ if (futureBucketIdx < anomalyTracker.getNumOfPastBuckets()) {
+ pastNs -= anomalyTracker.getPastBucketValue(
+ mEventKey,
+ mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futureBucketIdx);
+ } else if (futureBucketIdx == anomalyTracker.getNumOfPastBuckets()) {
+ pastNs -= (currentBucketPastNs + (currentBucketEndNs - eventTimestampNs));
}
}
- // If we have reached this point, we even have to overwrite the the original current bucket.
- // Thus, none of the past data will still be extant - pastNs is now 0.
- return eventTimestampNs + thresholdNs;
+ return std::max(eventTimestampNs + thresholdNs, refractoryPeriodEndNs);
}
void OringDurationTracker::dumpStates(FILE* out, bool verbose) const {
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 610e3ea..ca8abfe 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -56,7 +56,7 @@
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
- const uint64_t currentTimestamp) const override;
+ const int64_t currentTimestamp) const override;
void dumpStates(FILE* out, bool verbose) const override;
private:
diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
new file mode 100644
index 0000000..93ecde5
--- /dev/null
+++ b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
@@ -0,0 +1,241 @@
+// 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.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+
+ *config.add_atom_matcher() = wakelockAcquireMatcher;
+
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(123456);
+ countMetric->set_what(wakelockAcquireMatcher.id());
+ *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ countMetric->set_bucket(FIVE_MINUTES);
+
+ auto alert = config.add_alert();
+ alert->set_id(StringToId("alert"));
+ alert->set_metric_id(123456);
+ alert->set_num_buckets(num_buckets);
+ alert->set_refractory_period_secs(10);
+ alert->set_trigger_if_sum_gt(threshold);
+ return config;
+}
+
+} // namespace
+
+TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) {
+ const int num_buckets = 1;
+ const int threshold = 3;
+ auto config = CreateStatsdConfig(num_buckets, threshold);
+ const uint64_t alert_id = config.alert(0).id();
+ const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+ sp<AnomalyTracker> anomalyTracker =
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+ std::vector<AttributionNodeInternal> attributions2 = {
+ CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
+ std::vector<AttributionNodeInternal> attributions3 = {
+ CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
+ std::vector<AttributionNodeInternal> attributions4 = {
+ CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
+ std::vector<AttributionNodeInternal> attributions5 = {
+ CreateAttribution(222, "GMSCoreModule1") };
+
+ FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ Value((int32_t)111));
+ HashableDimensionKey whatKey1({fieldValue1});
+ MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
+
+ FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ Value((int32_t)222));
+ HashableDimensionKey whatKey2({fieldValue2});
+ MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
+
+ auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + 2);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+ event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 3);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+ event = CreateAcquireWakelockEvent(attributions3, "wl1", bucketStartTimeNs + 4);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 4);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+ // Fired alarm and refractory period end timestamp updated.
+ event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 5);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 100);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + bucketSizeNs + 1);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+ event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 2);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+ event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 3);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+ event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 4);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+}
+
+TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) {
+ const int num_buckets = 3;
+ const int threshold = 3;
+ auto config = CreateStatsdConfig(num_buckets, threshold);
+ const uint64_t alert_id = config.alert(0).id();
+ const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+ sp<AnomalyTracker> anomalyTracker =
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+ std::vector<AttributionNodeInternal> attributions2 = {
+ CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
+ std::vector<AttributionNodeInternal> attributions3 = {
+ CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
+ std::vector<AttributionNodeInternal> attributions4 = {
+ CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
+ std::vector<AttributionNodeInternal> attributions5 = {
+ CreateAttribution(222, "GMSCoreModule1") };
+
+ FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ Value((int32_t)111));
+ HashableDimensionKey whatKey1({fieldValue1});
+ MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
+
+ FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ Value((int32_t)222));
+ HashableDimensionKey whatKey2({fieldValue2});
+ MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
+
+ auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ // Fired alarm and refractory period end timestamp updated.
+ event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 4);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 1);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ event = CreateAcquireWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 2);
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
new file mode 100644
index 0000000..e924b03
--- /dev/null
+++ b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
@@ -0,0 +1,486 @@
+// 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.
+
+#include <gtest/gtest.h>
+
+#include "src/anomaly/DurationAnomalyTracker.h"
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig(int num_buckets,
+ uint64_t threshold_ns,
+ DurationMetric::AggregationType aggregationType,
+ bool nesting) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = screenIsOffPredicate;
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ FieldMatcher dimensions = CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock.
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
+ holdingWakelockPredicate.mutable_simple_predicate()->set_count_nesting(nesting);
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("WakelockDuration"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->set_condition(screenIsOffPredicate.id());
+ durationMetric->set_aggregation_type(aggregationType);
+ *durationMetric->mutable_dimensions_in_what() =
+ CreateAttributionUidDimensions(android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ auto alert = config.add_alert();
+ alert->set_id(StringToId("alert"));
+ alert->set_metric_id(StringToId("WakelockDuration"));
+ alert->set_num_buckets(num_buckets);
+ alert->set_refractory_period_secs(2);
+ alert->set_trigger_if_sum_gt(threshold_ns);
+ return config;
+}
+
+} // namespace
+
+std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
+ CreateAttribution(222, "GMSCoreModule1")};
+
+std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"),
+ CreateAttribution(222, "GMSCoreModule1")};
+
+std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1")};
+
+MetricDimensionKey dimensionKey(
+ HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
+ (int32_t)0x02010101), Value((int32_t)111))}),
+ DEFAULT_DIMENSION_KEY);
+
+MetricDimensionKey dimensionKey2(
+ HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
+ (int32_t)0x02010101), Value((int32_t)222))}),
+ DEFAULT_DIMENSION_KEY);
+
+TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
+ const int num_buckets = 1;
+ const uint64_t threshold_ns = NS_PER_SEC;
+ auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
+ const uint64_t alert_id = config.alert(0).id();
+ const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+ int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+ sp<AnomalyTracker> anomalyTracker =
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+ auto screen_on_event = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1);
+ auto screen_off_event = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 10);
+ processor->OnLogEvent(screen_on_event.get());
+ processor->OnLogEvent(screen_off_event.get());
+
+ // Acquire wakelock wl1.
+ auto acquire_event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 11);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event.
+ auto release_event = CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 101);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Acquire wakelock wl1 within bucket #0.
+ acquire_event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 110);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Release wakelock wl1. One anomaly detected.
+ release_event = CreateReleaseWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + NS_PER_SEC + 109);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Acquire wakelock wl1.
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + NS_PER_SEC + 112);
+ processor->OnLogEvent(acquire_event.get());
+ // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the
+ // end of the refractory period.
+ const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey);
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
+ (uint32_t)alarmFiredTimestampSec0);
+
+ // Anomaly alarm fired.
+ auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
+ static_cast<uint32_t>(alarmFiredTimestampSec0));
+ EXPECT_EQ(1u, alarmSet.size());
+ processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet);
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Release wakelock wl1.
+ release_event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ // Within refractory period. No more anomaly detected.
+ EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Acquire wakelock wl1.
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11);
+ processor->OnLogEvent(acquire_event.get());
+ const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey);
+ EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC,
+ (uint64_t)alarmFiredTimestampSec1);
+
+ // Release wakelock wl1.
+ release_event = CreateReleaseWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec +
+ (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
+ static_cast<uint32_t>(alarmFiredTimestampSec1));
+ EXPECT_EQ(0u, alarmSet.size());
+
+ // Acquire wakelock wl1 near the end of bucket #0.
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 2);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+ // Release the event at early bucket #1.
+ release_event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ // Anomaly detected when stopping the alarm. The refractory period does not change.
+ EXPECT_EQ(refractory_period_sec +
+ (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Condition changes to false.
+ screen_on_event = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ bucketStartTimeNs + 2 * bucketSizeNs + 20);
+ processor->OnLogEvent(screen_on_event.get());
+ EXPECT_EQ(refractory_period_sec +
+ (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 30);
+ processor->OnLogEvent(acquire_event.get());
+ // The condition is false. Do not start the alarm.
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec +
+ (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Condition turns true.
+ screen_off_event = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC);
+ processor->OnLogEvent(screen_off_event.get());
+ EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+ // Condition turns to false.
+ screen_on_event = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1);
+ processor->OnLogEvent(screen_on_event.get());
+ // Condition turns to false. Cancelled the alarm.
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ // Detected one anomaly.
+ EXPECT_EQ(refractory_period_sec +
+ (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Condition turns to true again.
+ screen_off_event = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2);
+ processor->OnLogEvent(screen_off_event.get());
+ EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+ release_event = CreateReleaseWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(refractory_period_sec +
+ (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+}
+
+TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
+ const int num_buckets = 3;
+ const uint64_t threshold_ns = NS_PER_SEC;
+ auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
+ const uint64_t alert_id = config.alert(0).id();
+ const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+ int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+ sp<AnomalyTracker> anomalyTracker =
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+ auto screen_off_event = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1);
+ processor->OnLogEvent(screen_off_event.get());
+
+ // Acquire wakelock "wc1" in bucket #0.
+ auto acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Release wakelock "wc1" in bucket #0.
+ auto release_event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Acquire wakelock "wc1" in bucket #1.
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ release_event = CreateReleaseWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 100);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Acquire wakelock "wc2" in bucket #2.
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+ // Release wakelock "wc2" in bucket #2.
+ release_event = CreateReleaseWakelockEvent(
+ attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+ EXPECT_EQ(refractory_period_sec +
+ (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+ // Acquire wakelock "wc1" in bucket #2.
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Release wakelock "wc1" in bucket #2.
+ release_event = CreateReleaseWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec +
+ (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4);
+ processor->OnLogEvent(acquire_event.get());
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+
+ release_event = CreateReleaseWakelockEvent(
+ attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs + 2);
+ processor->OnLogEvent(release_event.get());
+ release_event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs + 6);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+ // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered.
+ EXPECT_EQ(refractory_period_sec +
+ (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+}
+
+TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
+ const int num_buckets = 2;
+ const uint64_t threshold_ns = 3 * NS_PER_SEC;
+ auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false);
+ int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+
+ const uint64_t alert_id = config.alert(0).id();
+ const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC;
+ config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+ sp<AnomalyTracker> anomalyTracker =
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+ auto screen_off_event = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1);
+ processor->OnLogEvent(screen_off_event.get());
+
+ // Acquire wakelock "wc1" in bucket #0.
+ auto acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 100);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Acquire the wakelock "wc1" again.
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1);
+ processor->OnLogEvent(acquire_event.get());
+ // The alarm does not change.
+ EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // Anomaly alarm fired late.
+ const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC;
+ auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
+ static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC));
+ EXPECT_EQ(1u, alarmSet.size());
+ processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet);
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs - 100);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ auto release_event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ // Within the refractory period. No anomaly.
+ EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ // A new wakelock, but still within refractory period.
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+ release_event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC);
+ // Still in the refractory period. No anomaly.
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+ release_event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4);
+ processor->OnLogEvent(release_event.get());
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+
+ acquire_event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3);
+ processor->OnLogEvent(acquire_event.get());
+ EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
+ anomalyTracker->getAlarmTimestampSec(dimensionKey));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
index 2287c2b..c2334d8 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
@@ -458,7 +458,7 @@
} // namespace
TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition) {
- for (auto aggregationType : { DurationMetric::MAX_SPARSE}) { // DurationMetric::SUM,
+ for (auto aggregationType : { DurationMetric::MAX_SPARSE, DurationMetric::SUM}) {
ConfigKey cfgKey;
auto config = CreateDurationMetricConfig_NoLink_CombinationCondition(aggregationType);
int64_t bucketStartTimeNs = 10000000000;
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 5ef84e6..a75d6c8 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -40,7 +40,7 @@
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
const int64_t metricId = 123;
-const int64_t bucketStartTimeNs = 10000000000;
+const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 9b27f3c..13cdb0b 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -44,7 +44,7 @@
const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(TagId, 1, "maps");
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-const uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+const uint64_t bucketSizeNs = 30 * NS_PER_SEC;
TEST(OringDurationTrackerTest, TestDurationOverlap) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
@@ -370,6 +370,103 @@
tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
}
+TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) {
+ vector<Matcher> dimensionInCondition;
+ Alert alert;
+ alert.set_id(101);
+ alert.set_metric_id(1);
+ alert.set_trigger_if_sum_gt(5 * NS_PER_SEC);
+ alert.set_num_buckets(1);
+ alert.set_refractory_period_secs(20);
+
+ uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t bucketNum = 0;
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<AlarmMonitor> alarmMonitor;
+ sp<DurationAnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
+ OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, 1,
+ dimensionInCondition,
+ true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
+ bucketSizeNs, true, false, {anomalyTracker});
+
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
+ // Anomaly happens in the bucket #1.
+ EXPECT_EQ((long long)(bucketStartTimeNs + 14 * NS_PER_SEC),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
+
+ tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 14 * NS_PER_SEC, false);
+
+ EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY));
+
+ uint64_t event2StartTimeNs = bucketStartTimeNs + 22 * NS_PER_SEC;
+ EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY));
+ EXPECT_EQ((long long)(bucketStartTimeNs + 35 * NS_PER_SEC),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
+}
+
+TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp3) {
+ // Test the cases where the refractory period is smaller than the bucket size, longer than
+ // the bucket size, and longer than 2x of the anomaly detection window.
+ for (int j = 0; j < 3; j++) {
+ uint64_t thresholdNs = j * bucketSizeNs + 5 * NS_PER_SEC;
+ for (int i = 0; i <= 7; ++i) {
+ vector<Matcher> dimensionInCondition;
+ Alert alert;
+ alert.set_id(101);
+ alert.set_metric_id(1);
+ alert.set_trigger_if_sum_gt(thresholdNs);
+ alert.set_num_buckets(3);
+ alert.set_refractory_period_secs(
+ bucketSizeNs / NS_PER_SEC / 2 + i * bucketSizeNs / NS_PER_SEC);
+
+ uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t bucketNum = 101;
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<AlarmMonitor> alarmMonitor;
+ sp<DurationAnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
+ OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY,
+ wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
+ bucketSizeNs, true, false, {anomalyTracker});
+
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
+ EXPECT_EQ((long long)(eventStartTimeNs + thresholdNs),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
+ uint64_t eventStopTimeNs = eventStartTimeNs + thresholdNs + NS_PER_SEC;
+ tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStopTimeNs, false);
+
+ uint64_t refractoryPeriodEndSec =
+ anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY);
+ EXPECT_EQ((long long)(eventStopTimeNs) / NS_PER_SEC + alert.refractory_period_secs(),
+ refractoryPeriodEndSec);
+
+ // Acquire and release a wakelock in the next bucket.
+ uint64_t event2StartTimeNs = eventStopTimeNs + bucketSizeNs;
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, event2StartTimeNs, ConditionKey());
+ uint64_t event2StopTimeNs = event2StartTimeNs + 4 * NS_PER_SEC;
+ tracker.noteStop(DEFAULT_DIMENSION_KEY, event2StopTimeNs, false);
+
+ // Test the alarm prediction works well when seeing another wakelock start event.
+ for (int k = 0; k <= 2; ++k) {
+ uint64_t event3StartTimeNs = event2StopTimeNs + NS_PER_SEC + k * bucketSizeNs;
+ uint64_t alarmTimestampNs =
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs);
+ EXPECT_GT(alarmTimestampNs, 0u);
+ EXPECT_GE(alarmTimestampNs, event3StartTimeNs);
+ EXPECT_GE(alarmTimestampNs, refractoryPeriodEndSec * NS_PER_SEC);
+ }
+ }
+ }
+}
+
TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 0f785df..ce44a35 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -447,7 +447,9 @@
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
const ConfigKey& key) {
sp<UidMap> uidMap = new UidMap();
- sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> anomalyAlarmMonitor =
+ new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
+ [](const sp<IStatsCompanionService>&){});
sp<AlarmMonitor> periodicAlarmMonitor;
sp<StatsLogProcessor> processor = new StatsLogProcessor(
uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8a9c738..0afc1ce 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -330,8 +330,6 @@
Landroid/app/TaskStackListener;-><init>()V
Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker;
Landroid/app/trust/ITrustManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/app/usage/StorageStatsManager;->getFreeBytes(Ljava/lang/String;)J
-Landroid/app/usage/StorageStatsManager;->getTotalBytes(Ljava/lang/String;)J
Landroid/app/usage/UsageStatsManager;->mService:Landroid/app/usage/IUsageStatsManager;
Landroid/app/usage/UsageStats;->mLastEvent:I
Landroid/app/usage/UsageStats;->mTotalTimeInForeground:J
@@ -1357,7 +1355,6 @@
Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
Landroid/os/storage/VolumeInfo;->getState()I
Landroid/os/storage/VolumeInfo;->getType()I
-Landroid/os/storage/VolumeInfo;->isMountedReadable()Z
Landroid/os/storage/VolumeInfo;->isPrimary()Z
Landroid/os/storage/VolumeInfo;->isVisible()Z
Landroid/os/StrictMode;->disableDeathOnFileUriExposure()V
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4cb7f89..3015398 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8610,6 +8610,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
@UserProvisioningState
public int getUserProvisioningState() {
throwIfParentInstance("getUserProvisioningState");
@@ -8754,6 +8755,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean isDeviceProvisioned() {
try {
return mService.isDeviceProvisioned();
@@ -9418,7 +9420,21 @@
* <p>This method may returns {@code -1} if {@code apnSetting} conflicts with an existing
* override APN. Update the existing conflicted APN with
* {@link #updateOverrideApn(ComponentName, int, ApnSetting)} instead of adding a new entry.
- * <p>See {@link ApnSetting} for the definition of conflict.
+ * <p>Two override APNs are considered to conflict when all the following APIs return
+ * the same values on both override APNs:
+ * <ul>
+ * <li>{@link ApnSetting#getOperatorNumeric()}</li>
+ * <li>{@link ApnSetting#getApnName()}</li>
+ * <li>{@link ApnSetting#getProxyAddress()}</li>
+ * <li>{@link ApnSetting#getProxyPort()}</li>
+ * <li>{@link ApnSetting#getMmsProxyAddress()}</li>
+ * <li>{@link ApnSetting#getMmsProxyPort()}</li>
+ * <li>{@link ApnSetting#getMmsc()}</li>
+ * <li>{@link ApnSetting#isEnabled()}</li>
+ * <li>{@link ApnSetting#getMvnoType()}</li>
+ * <li>{@link ApnSetting#getProtocol()}</li>
+ * <li>{@link ApnSetting#getRoamingProtocol()}</li>
+ * </ul>
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @param apnSetting the override APN to insert
@@ -9447,7 +9463,7 @@
* {@code apnId}.
* <p>This method may also returns {@code false} if {@code apnSetting} conflicts with an
* existing override APN. Update the existing conflicted APN instead.
- * <p>See {@link ApnSetting} for the definition of conflict.
+ * <p>See {@link #addOverrideApn} for the definition of conflict.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @param apnId the {@code id} of the override APN to update
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index ee13880..02afcc7 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -65,7 +65,6 @@
NETWORK_TYPE_UNMETERED,
NETWORK_TYPE_NOT_ROAMING,
NETWORK_TYPE_CELLULAR,
- NETWORK_TYPE_METERED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface NetworkType {}
@@ -253,7 +252,7 @@
/**
* @hide
*/
- public static final int FLAG_IS_PREFETCH = 1 << 2;
+ public static final int FLAG_PREFETCH = 1 << 2;
/**
* This job needs to be exempted from the app standby throttling. Only the system (UID 1000)
@@ -296,7 +295,8 @@
private final boolean hasEarlyConstraint;
private final boolean hasLateConstraint;
private final NetworkRequest networkRequest;
- private final long networkBytes;
+ private final long networkDownloadBytes;
+ private final long networkUploadBytes;
private final long minLatencyMillis;
private final long maxExecutionDelayMillis;
private final boolean isPeriodic;
@@ -317,30 +317,28 @@
}
/**
- * Bundle of extras which are returned to your application at execution time.
+ * @see JobInfo.Builder#setExtras(PersistableBundle)
*/
public @NonNull PersistableBundle getExtras() {
return extras;
}
/**
- * Bundle of transient extras which are returned to your application at execution time,
- * but not persisted by the system.
+ * @see JobInfo.Builder#setTransientExtras(Bundle)
*/
public @NonNull Bundle getTransientExtras() {
return transientExtras;
}
/**
- * ClipData of information that is returned to your application at execution time,
- * but not persisted by the system.
+ * @see JobInfo.Builder#setClipData(ClipData, int)
*/
public @Nullable ClipData getClipData() {
return clipData;
}
/**
- * Permission grants that go along with {@link #getClipData}.
+ * @see JobInfo.Builder#setClipData(ClipData, int)
*/
public int getClipGrantFlags() {
return clipGrantFlags;
@@ -369,32 +367,28 @@
}
/**
- * Whether this job requires that the device be charging (or be a non-battery-powered
- * device connected to permanent power, such as Android TV devices).
+ * @see JobInfo.Builder#setRequiresCharging(boolean)
*/
public boolean isRequireCharging() {
return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
}
/**
- * Whether this job needs the device's battery level to not be at below the critical threshold.
+ * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
*/
public boolean isRequireBatteryNotLow() {
return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0;
}
/**
- * Whether this job requires that the user <em>not</em> be interacting with the device.
- *
- * <p class="note">This is <em>not</em> the same as "doze" or "device idle";
- * it is purely about the user's direct interactions.</p>
+ * @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
*/
public boolean isRequireDeviceIdle() {
return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
}
/**
- * Whether this job needs the device's storage to not be low.
+ * @see JobInfo.Builder#setRequiresStorageNotLow(boolean)
*/
public boolean isRequireStorageNotLow() {
return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0;
@@ -410,6 +404,7 @@
/**
* Which content: URIs must change for the job to be scheduled. Returns null
* if there are none required.
+ * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri)
*/
public @Nullable TriggerContentUri[] getTriggerContentUris() {
return triggerContentUris;
@@ -418,6 +413,7 @@
/**
* When triggering on content URI changes, this is the delay from when a change
* is detected until the job is scheduled.
+ * @see JobInfo.Builder#setTriggerContentUpdateDelay(long)
*/
public long getTriggerContentUpdateDelay() {
return triggerContentUpdateDelay;
@@ -426,6 +422,7 @@
/**
* When triggering on content URI changes, this is the maximum delay we will
* use before scheduling the job.
+ * @see JobInfo.Builder#setTriggerContentMaxDelay(long)
*/
public long getTriggerContentMaxDelay() {
return triggerContentMaxDelay;
@@ -466,28 +463,59 @@
}
/**
- * Return the estimated size of network traffic that will be performed by
+ * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
+ * {@link #getEstimatedNetworkUploadBytes()}.
+ * @removed
+ */
+ @Deprecated
+ public @BytesLong long getEstimatedNetworkBytes() {
+ if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN
+ && networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+ return NETWORK_BYTES_UNKNOWN;
+ } else if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
+ return networkUploadBytes;
+ } else if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+ return networkDownloadBytes;
+ } else {
+ return networkDownloadBytes + networkUploadBytes;
+ }
+ }
+
+ /**
+ * Return the estimated size of download traffic that will be performed by
* this job, in bytes.
*
- * @return Estimated size of network traffic, or
+ * @return Estimated size of download traffic, or
* {@link #NETWORK_BYTES_UNKNOWN} when unknown.
- * @see Builder#setEstimatedNetworkBytes(long)
+ * @see Builder#setEstimatedNetworkBytes(long, long)
*/
- public @BytesLong long getEstimatedNetworkBytes() {
- return networkBytes;
+ public @BytesLong long getEstimatedNetworkDownloadBytes() {
+ return networkDownloadBytes;
+ }
+
+ /**
+ * Return the estimated size of upload traffic that will be performed by
+ * this job, in bytes.
+ *
+ * @return Estimated size of upload traffic, or
+ * {@link #NETWORK_BYTES_UNKNOWN} when unknown.
+ * @see Builder#setEstimatedNetworkBytes(long, long)
+ */
+ public @BytesLong long getEstimatedNetworkUploadBytes() {
+ return networkUploadBytes;
}
/**
* Set for a job that does not recur periodically, to specify a delay after which the job
* will be eligible for execution. This value is not set if the job recurs periodically.
+ * @see JobInfo.Builder#setMinimumLatency(long)
*/
public long getMinLatencyMillis() {
return minLatencyMillis;
}
/**
- * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs
- * periodically.
+ * @see JobInfo.Builder#setOverrideDeadline(long)
*/
public long getMaxExecutionDelayMillis() {
return maxExecutionDelayMillis;
@@ -495,13 +523,15 @@
/**
* Track whether this job will repeat with a given period.
+ * @see JobInfo.Builder#setPeriodic(long)
+ * @see JobInfo.Builder#setPeriodic(long, long)
*/
public boolean isPeriodic() {
return isPeriodic;
}
/**
- * @return Whether or not this job should be persisted across device reboots.
+ * @see JobInfo.Builder#setPersisted(boolean)
*/
public boolean isPersisted() {
return isPersisted;
@@ -510,6 +540,8 @@
/**
* Set to the interval between occurrences of this job. This value is <b>not</b> set if the
* job does not recur periodically.
+ * @see JobInfo.Builder#setPeriodic(long)
+ * @see JobInfo.Builder#setPeriodic(long, long)
*/
public long getIntervalMillis() {
return intervalMillis;
@@ -518,6 +550,8 @@
/**
* Flex time for this job. Only valid if this is a periodic job. The job can
* execute at any time in a window of flex length at the end of the period.
+ * @see JobInfo.Builder#setPeriodic(long)
+ * @see JobInfo.Builder#setPeriodic(long, long)
*/
public long getFlexMillis() {
return flexMillis;
@@ -527,6 +561,7 @@
* The amount of time the JobScheduler will wait before rescheduling a failed job. This value
* will be increased depending on the backoff policy specified at job creation time. Defaults
* to 30 seconds, minimum is currently 10 seconds.
+ * @see JobInfo.Builder#setBackoffCriteria(long, int)
*/
public long getInitialBackoffMillis() {
return initialBackoffMillis;
@@ -534,12 +569,27 @@
/**
* Return the backoff policy of this job.
+ * @see JobInfo.Builder#setBackoffCriteria(long, int)
*/
public @BackoffPolicy int getBackoffPolicy() {
return backoffPolicy;
}
/**
+ * @see JobInfo.Builder#setImportantWhileForeground(boolean)
+ */
+ public boolean isImportantWhileForeground() {
+ return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0;
+ }
+
+ /**
+ * @see JobInfo.Builder#setPrefetch(boolean)
+ */
+ public boolean isPrefetch() {
+ return (flags & FLAG_PREFETCH) != 0;
+ }
+
+ /**
* User can specify an early constraint of 0L, which is valid, so we keep track of whether the
* function was called at all.
* @hide
@@ -610,7 +660,10 @@
if (!Objects.equals(networkRequest, j.networkRequest)) {
return false;
}
- if (networkBytes != j.networkBytes) {
+ if (networkDownloadBytes != j.networkDownloadBytes) {
+ return false;
+ }
+ if (networkUploadBytes != j.networkUploadBytes) {
return false;
}
if (minLatencyMillis != j.minLatencyMillis) {
@@ -673,7 +726,8 @@
if (networkRequest != null) {
hashCode = 31 * hashCode + networkRequest.hashCode();
}
- hashCode = 31 * hashCode + Long.hashCode(networkBytes);
+ hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
+ hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
@@ -708,7 +762,8 @@
} else {
networkRequest = null;
}
- networkBytes = in.readLong();
+ networkDownloadBytes = in.readLong();
+ networkUploadBytes = in.readLong();
minLatencyMillis = in.readLong();
maxExecutionDelayMillis = in.readLong();
isPeriodic = in.readInt() == 1;
@@ -737,7 +792,8 @@
triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
triggerContentMaxDelay = b.mTriggerContentMaxDelay;
networkRequest = b.mNetworkRequest;
- networkBytes = b.mNetworkBytes;
+ networkDownloadBytes = b.mNetworkDownloadBytes;
+ networkUploadBytes = b.mNetworkUploadBytes;
minLatencyMillis = b.mMinLatencyMillis;
maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
isPeriodic = b.mIsPeriodic;
@@ -780,7 +836,8 @@
} else {
out.writeInt(0);
}
- out.writeLong(networkBytes);
+ out.writeLong(networkDownloadBytes);
+ out.writeLong(networkUploadBytes);
out.writeLong(minLatencyMillis);
out.writeLong(maxExecutionDelayMillis);
out.writeInt(isPeriodic ? 1 : 0);
@@ -914,7 +971,8 @@
// Requirements.
private int mConstraintFlags;
private NetworkRequest mNetworkRequest;
- private long mNetworkBytes = NETWORK_BYTES_UNKNOWN;
+ private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
+ private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
private ArrayList<TriggerContentUri> mTriggerContentUris;
private long mTriggerContentUpdateDelay = -1;
private long mTriggerContentMaxDelay = -1;
@@ -965,6 +1023,7 @@
/**
* Set optional extras. This is persisted, so we only allow primitive types.
* @param extras Bundle containing extras you want the scheduler to hold on to for you.
+ * @see JobInfo#getExtras()
*/
public Builder setExtras(@NonNull PersistableBundle extras) {
mExtras = extras;
@@ -979,6 +1038,7 @@
* {@link android.app.job.JobInfo.Builder#build()} is called.</p>
*
* @param extras Bundle containing extras you want the scheduler to hold on to for you.
+ * @see JobInfo#getTransientExtras()
*/
public Builder setTransientExtras(@NonNull Bundle extras) {
mTransientExtras = extras;
@@ -1006,6 +1066,8 @@
* a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION},
* {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and
* {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
+ * @see JobInfo#getClipData()
+ * @see JobInfo#getClipGrantFlags()
*/
public Builder setClipData(@Nullable ClipData clip, int grantFlags) {
mClipData = clip;
@@ -1096,6 +1158,16 @@
}
/**
+ * @deprecated replaced by
+ * {@link #setEstimatedNetworkBytes(long, long)}.
+ * @removed
+ */
+ @Deprecated
+ public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
+ return setEstimatedNetworkBytes(networkBytes, NETWORK_BYTES_UNKNOWN);
+ }
+
+ /**
* Set the estimated size of network traffic that will be performed by
* this job, in bytes.
* <p>
@@ -1112,23 +1184,30 @@
* <li>A job that synchronizes email could end up using an extreme range
* of data, from under 1KB when nothing has changed, to dozens of MB
* when there are new emails with attachments. Jobs that cannot provide
- * reasonable estimates should leave this estimated value undefined.
+ * reasonable estimates should use the sentinel value
+ * {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
* </ul>
* Note that the system may choose to delay jobs with large network
* usage estimates when the device has a poor network connection, in
* order to save battery.
+ * <p>
+ * The values provided here only reflect the traffic that will be
+ * performed by the base job; if you're using {@link JobWorkItem} then
+ * you also need to define the network traffic used by each work item
+ * when constructing them.
*
- * @param networkBytes The estimated size of network traffic that will
- * be performed by this job, in bytes. This value only
- * reflects the traffic that will be performed by the base
- * job; if you're using {@link JobWorkItem} then you also
- * need to define the network traffic used by each work item
- * when constructing them.
- * @see JobInfo#getEstimatedNetworkBytes()
- * @see JobWorkItem#JobWorkItem(android.content.Intent, long)
+ * @param downloadBytes The estimated size of network traffic that will
+ * be downloaded by this job, in bytes.
+ * @param uploadBytes The estimated size of network traffic that will be
+ * uploaded by this job, in bytes.
+ * @see JobInfo#getEstimatedNetworkDownloadBytes()
+ * @see JobInfo#getEstimatedNetworkUploadBytes()
+ * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long)
*/
- public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
- mNetworkBytes = networkBytes;
+ public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
+ @BytesLong long uploadBytes) {
+ mNetworkDownloadBytes = downloadBytes;
+ mNetworkUploadBytes = uploadBytes;
return this;
}
@@ -1146,6 +1225,7 @@
*
* @param requiresCharging Pass {@code true} to require that the device be
* charging in order to run the job.
+ * @see JobInfo#isRequireCharging()
*/
public Builder setRequiresCharging(boolean requiresCharging) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
@@ -1159,6 +1239,7 @@
* is not low, which is generally the point where the user is given a "low battery"
* warning.
* @param batteryNotLow Whether or not the device's battery level must not be low.
+ * @see JobInfo#isRequireBatteryNotLow()
*/
public Builder setRequiresBatteryNotLow(boolean batteryNotLow) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
@@ -1183,6 +1264,7 @@
*
* @param requiresDeviceIdle Pass {@code true} to prevent the job from running
* while the device is being used interactively.
+ * @see JobInfo#isRequireDeviceIdle()
*/
public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
@@ -1196,6 +1278,7 @@
* in a low storage state, which is generally the point where the user is given a
* "low storage" warning.
* @param storageNotLow Whether or not the device's available storage must not be low.
+ * @see JobInfo#isRequireStorageNotLow()
*/
public Builder setRequiresStorageNotLow(boolean storageNotLow) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW)
@@ -1228,6 +1311,7 @@
* job}
*
* @param uri The content: URI to monitor.
+ * @see JobInfo#getTriggerContentUris()
*/
public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
if (mTriggerContentUris == null) {
@@ -1242,6 +1326,7 @@
* the job is scheduled. If there are more changes during that time, the delay
* will be reset to start at the time of the most recent change.
* @param durationMs Delay after most recent content change, in milliseconds.
+ * @see JobInfo#getTriggerContentUpdateDelay()
*/
public Builder setTriggerContentUpdateDelay(long durationMs) {
mTriggerContentUpdateDelay = durationMs;
@@ -1252,6 +1337,7 @@
* Set the maximum total delay (in milliseconds) that is allowed from the first
* time a content change is detected until the job is scheduled.
* @param durationMs Delay after initial content change, in milliseconds.
+ * @see JobInfo#getTriggerContentMaxDelay()
*/
public Builder setTriggerContentMaxDelay(long durationMs) {
mTriggerContentMaxDelay = durationMs;
@@ -1265,6 +1351,8 @@
* Setting this function on the builder with {@link #setMinimumLatency(long)} or
* {@link #setOverrideDeadline(long)} will result in an error.
* @param intervalMillis Millisecond interval for which this job will repeat.
+ * @see JobInfo#getIntervalMillis()
+ * @see JobInfo#getFlexMillis()
*/
public Builder setPeriodic(long intervalMillis) {
return setPeriodic(intervalMillis, intervalMillis);
@@ -1278,6 +1366,8 @@
* @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
* {@link #getMinFlexMillis()} or 5 percent of the period, whichever is
* higher.
+ * @see JobInfo#getIntervalMillis()
+ * @see JobInfo#getFlexMillis()
*/
public Builder setPeriodic(long intervalMillis, long flexMillis) {
final long minPeriod = getMinPeriodMillis();
@@ -1309,6 +1399,7 @@
* {@link android.app.job.JobInfo.Builder#build()} is called.
* @param minLatencyMillis Milliseconds before which this job will not be considered for
* execution.
+ * @see JobInfo#getMinLatencyMillis()
*/
public Builder setMinimumLatency(long minLatencyMillis) {
mMinLatencyMillis = minLatencyMillis;
@@ -1322,6 +1413,7 @@
* this property on a periodic job, doing so will throw an
* {@link java.lang.IllegalArgumentException} when
* {@link android.app.job.JobInfo.Builder#build()} is called.
+ * @see JobInfo#getMaxExecutionDelayMillis()
*/
public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
mMaxExecutionDelayMillis = maxExecutionDelayMillis;
@@ -1341,6 +1433,8 @@
* mode.
* @param initialBackoffMillis Millisecond time interval to wait initially when job has
* failed.
+ * @see JobInfo#getInitialBackoffMillis()
+ * @see JobInfo#getBackoffPolicy()
*/
public Builder setBackoffCriteria(long initialBackoffMillis,
@BackoffPolicy int backoffPolicy) {
@@ -1371,6 +1465,7 @@
*
* @param importantWhileForeground whether to relax doze restrictions for this job when the
* app is in the foreground. False by default.
+ * @see JobInfo#isImportantWhileForeground()
*/
public Builder setImportantWhileForeground(boolean importantWhileForeground) {
if (importantWhileForeground) {
@@ -1382,6 +1477,15 @@
}
/**
+ * @removed
+ * @deprecated replaced with {@link #setPrefetch(boolean)}
+ */
+ @Deprecated
+ public Builder setIsPrefetch(boolean isPrefetch) {
+ return setPrefetch(isPrefetch);
+ }
+
+ /**
* Setting this to true indicates that this job is designed to prefetch
* content that will make a material improvement to the experience of
* the specific user of this device. For example, fetching top headlines
@@ -1393,12 +1497,13 @@
* network when there is a surplus of metered data available. The system
* may also use this signal in combination with end user usage patterns
* to ensure data is prefetched before the user launches your app.
+ * @see JobInfo#isPrefetch()
*/
- public Builder setIsPrefetch(boolean isPrefetch) {
- if (isPrefetch) {
- mFlags |= FLAG_IS_PREFETCH;
+ public Builder setPrefetch(boolean prefetch) {
+ if (prefetch) {
+ mFlags |= FLAG_PREFETCH;
} else {
- mFlags &= (~FLAG_IS_PREFETCH);
+ mFlags &= (~FLAG_PREFETCH);
}
return this;
}
@@ -1408,6 +1513,7 @@
*
* @param isPersisted True to indicate that the job will be written to
* disk and loaded at boot.
+ * @see JobInfo#isPersisted()
*/
@RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED)
public Builder setPersisted(boolean isPersisted) {
@@ -1427,7 +1533,7 @@
"constraints, this is not allowed.");
}
// Check that network estimates require network type
- if (mNetworkBytes > 0 && mNetworkRequest == null) {
+ if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
throw new IllegalArgumentException(
"Can't provide estimated network usage without requiring a network");
}
diff --git a/core/java/android/app/job/JobWorkItem.java b/core/java/android/app/job/JobWorkItem.java
index 1c46e8e..995f522 100644
--- a/core/java/android/app/job/JobWorkItem.java
+++ b/core/java/android/app/job/JobWorkItem.java
@@ -16,6 +16,8 @@
package android.app.job;
+import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN;
+
import android.annotation.BytesLong;
import android.content.Intent;
import android.os.Parcel;
@@ -28,7 +30,8 @@
*/
final public class JobWorkItem implements Parcelable {
final Intent mIntent;
- final long mNetworkBytes;
+ final long mNetworkDownloadBytes;
+ final long mNetworkUploadBytes;
int mDeliveryCount;
int mWorkId;
Object mGrants;
@@ -41,22 +44,36 @@
*/
public JobWorkItem(Intent intent) {
mIntent = intent;
- mNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
+ mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+ }
+
+ /**
+ * @deprecated replaced by {@link #JobWorkItem(Intent, long, long)}
+ * @removed
+ */
+ @Deprecated
+ public JobWorkItem(Intent intent, @BytesLong long networkBytes) {
+ this(intent, networkBytes, NETWORK_BYTES_UNKNOWN);
}
/**
* Create a new piece of work, which can be submitted to
* {@link JobScheduler#enqueue JobScheduler.enqueue}.
+ * <p>
+ * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
+ * details about how to estimate network traffic.
*
* @param intent The general Intent describing this work.
- * @param networkBytes The estimated size of network traffic that will be
- * performed by this job work item, in bytes. See
- * {@link JobInfo.Builder#setEstimatedNetworkBytes(long)} for
- * details about how to estimate.
+ * @param downloadBytes The estimated size of network traffic that will be
+ * downloaded by this job work item, in bytes.
+ * @param uploadBytes The estimated size of network traffic that will be
+ * uploaded by this job work item, in bytes.
*/
- public JobWorkItem(Intent intent, @BytesLong long networkBytes) {
+ public JobWorkItem(Intent intent, @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
mIntent = intent;
- mNetworkBytes = networkBytes;
+ mNetworkDownloadBytes = downloadBytes;
+ mNetworkUploadBytes = uploadBytes;
}
/**
@@ -67,14 +84,44 @@
}
/**
- * Return the estimated size of network traffic that will be performed by
+ * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
+ * {@link #getEstimatedNetworkUploadBytes()}.
+ * @removed
+ */
+ @Deprecated
+ public @BytesLong long getEstimatedNetworkBytes() {
+ if (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN
+ && mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+ return NETWORK_BYTES_UNKNOWN;
+ } else if (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
+ return mNetworkUploadBytes;
+ } else if (mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+ return mNetworkDownloadBytes;
+ } else {
+ return mNetworkDownloadBytes + mNetworkUploadBytes;
+ }
+ }
+
+ /**
+ * Return the estimated size of download traffic that will be performed by
+ * this job, in bytes.
+ *
+ * @return Estimated size of download traffic, or
+ * {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
+ */
+ public @BytesLong long getEstimatedNetworkDownloadBytes() {
+ return mNetworkDownloadBytes;
+ }
+
+ /**
+ * Return the estimated size of upload traffic that will be performed by
* this job work item, in bytes.
*
- * @return estimated size, or {@link JobInfo#NETWORK_BYTES_UNKNOWN} when
- * unknown.
+ * @return Estimated size of upload traffic, or
+ * {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
*/
- public @BytesLong long getEstimatedNetworkBytes() {
- return mNetworkBytes;
+ public @BytesLong long getEstimatedNetworkUploadBytes() {
+ return mNetworkUploadBytes;
}
/**
@@ -128,9 +175,13 @@
sb.append(mWorkId);
sb.append(" intent=");
sb.append(mIntent);
- if (mNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
- sb.append(" networkBytes=");
- sb.append(mNetworkBytes);
+ if (mNetworkDownloadBytes != NETWORK_BYTES_UNKNOWN) {
+ sb.append(" downloadBytes=");
+ sb.append(mNetworkDownloadBytes);
+ }
+ if (mNetworkUploadBytes != NETWORK_BYTES_UNKNOWN) {
+ sb.append(" uploadBytes=");
+ sb.append(mNetworkUploadBytes);
}
if (mDeliveryCount != 0) {
sb.append(" dcount=");
@@ -151,7 +202,8 @@
} else {
out.writeInt(0);
}
- out.writeLong(mNetworkBytes);
+ out.writeLong(mNetworkDownloadBytes);
+ out.writeLong(mNetworkUploadBytes);
out.writeInt(mDeliveryCount);
out.writeInt(mWorkId);
}
@@ -173,7 +225,8 @@
} else {
mIntent = null;
}
- mNetworkBytes = in.readLong();
+ mNetworkDownloadBytes = in.readLong();
+ mNetworkUploadBytes = in.readLong();
mDeliveryCount = in.readInt();
mWorkId = in.readInt();
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 387a836..e85058d 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1101,6 +1101,58 @@
/** @hide */
public String[] splitClassLoaderNames;
+ /**
+ * Represents the default policy. The actual policy used will depend on other properties of
+ * the application, e.g. the target SDK version.
+ * @hide
+ */
+ public static final int HIDDEN_API_ENFORCEMENT_DEFAULT = -1;
+ /**
+ * No API enforcement; the app can access the entire internal private API. Only for use by
+ * system apps.
+ * @hide
+ */
+ public static final int HIDDEN_API_ENFORCEMENT_NONE = 0;
+ /**
+ * Light grey list enforcement, the strictest option. Enforces the light grey, dark grey and
+ * black lists.
+ * @hide
+ * */
+ public static final int HIDDEN_API_ENFORCEMENT_ALL_LISTS = 1;
+ /**
+ * Dark grey list enforcement. Enforces the dark grey and black lists
+ * @hide
+ */
+ public static final int HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK = 2;
+ /**
+ * Blacklist enforcement only.
+ * @hide
+ */
+ public static final int HIDDEN_API_ENFORCEMENT_BLACK = 3;
+
+ private static final int HIDDEN_API_ENFORCEMENT_MAX = HIDDEN_API_ENFORCEMENT_BLACK;
+
+ /**
+ * Values in this IntDef MUST be kept in sync with enum hiddenapi::EnforcementPolicy in
+ * art/runtime/hidden_api.h
+ * @hide
+ */
+ @IntDef(prefix = { "HIDDEN_API_ENFORCEMENT_" }, value = {
+ HIDDEN_API_ENFORCEMENT_DEFAULT,
+ HIDDEN_API_ENFORCEMENT_NONE,
+ HIDDEN_API_ENFORCEMENT_ALL_LISTS,
+ HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK,
+ HIDDEN_API_ENFORCEMENT_BLACK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HiddenApiEnforcementPolicy {}
+
+ private boolean isValidHiddenApiEnforcementPolicy(int policy) {
+ return policy >= HIDDEN_API_ENFORCEMENT_DEFAULT && policy <= HIDDEN_API_ENFORCEMENT_MAX;
+ }
+
+ private int mHiddenApiPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
+
public void dump(Printer pw, String prefix) {
dump(pw, prefix, DUMP_FLAG_ALL);
}
@@ -1188,7 +1240,7 @@
if (category != CATEGORY_UNDEFINED) {
pw.println(prefix + "category=" + category);
}
- pw.println(prefix + "isAllowedToUseHiddenApi=" + isAllowedToUseHiddenApi());
+ pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy());
}
super.dumpBack(pw, prefix);
}
@@ -1386,6 +1438,7 @@
appComponentFactory = orig.appComponentFactory;
compileSdkVersion = orig.compileSdkVersion;
compileSdkVersionCodename = orig.compileSdkVersionCodename;
+ mHiddenApiPolicy = orig.mHiddenApiPolicy;
}
public String toString() {
@@ -1459,6 +1512,7 @@
dest.writeInt(compileSdkVersion);
dest.writeString(compileSdkVersionCodename);
dest.writeString(appComponentFactory);
+ dest.writeInt(mHiddenApiPolicy);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1529,6 +1583,7 @@
compileSdkVersion = source.readInt();
compileSdkVersionCodename = source.readString();
appComponentFactory = source.readString();
+ mHiddenApiPolicy = source.readInt();
}
/**
@@ -1599,13 +1654,31 @@
}
}
+ private boolean isPackageWhitelistedForHiddenApis() {
+ return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+ }
+
/**
* @hide
*/
- public boolean isAllowedToUseHiddenApi() {
- boolean whitelisted =
- SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
- return whitelisted && (isSystemApp() || isUpdatedSystemApp());
+ public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
+ if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
+ return mHiddenApiPolicy;
+ }
+ if (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp())) {
+ return HIDDEN_API_ENFORCEMENT_NONE;
+ }
+ return HIDDEN_API_ENFORCEMENT_BLACK;
+ }
+
+ /**
+ * @hide
+ */
+ public void setHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
+ if (!isValidHiddenApiEnforcementPolicy(policy)) {
+ throw new IllegalArgumentException("Invalid API enforcement policy: " + policy);
+ }
+ mHiddenApiPolicy = policy;
}
/**
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 91bbdc7..6d9c913 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -105,6 +105,12 @@
/* Gets the current screen unlocked functions. */
long getScreenUnlockedFunctions();
+ /* Get the functionfs control handle for the given function. Usb
+ * descriptors will already be written, and the handle will be
+ * ready to use.
+ */
+ ParcelFileDescriptor getControlFd(long function);
+
/* Allow USB debugging from the attached host. If alwaysAllow is true, add the
* the public key to list of host keys that the user has approved.
*/
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 572c585..46142e3 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -192,14 +192,6 @@
public static final String USB_DATA_UNLOCKED = "unlocked";
/**
- * Boolean extra indicating whether the intent represents a change in the usb
- * configuration (as opposed to a state update).
- *
- * {@hide}
- */
- public static final String USB_CONFIG_CHANGED = "config_changed";
-
- /**
* A placeholder indicating that no USB function is being specified.
* Used for compatibility with old init scripts to indicate no functions vs. charging function.
*
@@ -471,6 +463,25 @@
}
/**
+ * Gets the functionfs control file descriptor for the given function, with
+ * the usb descriptors and strings already written. The file descriptor is used
+ * by the function implementation to handle events and control requests.
+ *
+ * @param function to get control fd for. Currently {@link #FUNCTION_MTP} and
+ * {@link #FUNCTION_PTP} are supported.
+ * @return A ParcelFileDescriptor holding the valid fd, or null if the fd was not found.
+ *
+ * {@hide}
+ */
+ public ParcelFileDescriptor getControlFd(long function) {
+ try {
+ return mService.getControlFd(function);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns true if the caller has permission to access the device.
* Permission might have been granted temporarily via
* {@link #requestPermission(UsbDevice, PendingIntent)} or
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 7922276..40d53b7 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,7 +16,6 @@
package android.net;
-import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -259,30 +258,47 @@
/**
* Set specific UID to use when accounting {@link Socket} traffic
* originating from the current thread. Designed for use when performing an
- * operation on behalf of another application.
+ * operation on behalf of another application, or when another application
+ * is performing operations on your behalf.
+ * <p>
+ * Any app can <em>accept</em> blame for traffic performed on a socket
+ * originally created by another app by calling this method with the
+ * {@link android.system.Os#getuid()} value. However, only apps holding the
+ * {@code android.Manifest.permission#UPDATE_DEVICE_STATS} permission may
+ * <em>assign</em> blame to another UIDs.
* <p>
* Changes only take effect during subsequent calls to
* {@link #tagSocket(Socket)}.
- * <p>
- * To take effect, caller must hold
- * {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission.
- *
- * @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ @SuppressLint("Doclava125")
public static void setThreadStatsUid(int uid) {
NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
}
/**
+ * Get the active UID used when accounting {@link Socket} traffic originating
+ * from the current thread. Only one active tag per thread is supported.
+ * {@link #tagSocket(Socket)}.
+ *
+ * @see #setThreadStatsUid(int)
+ */
+ public static int getThreadStatsUid() {
+ return NetworkManagementSocketTagger.getThreadSocketStatsUid();
+ }
+
+ /**
* Set specific UID to use when accounting {@link Socket} traffic
* originating from the current thread as the calling UID. Designed for use
* when another application is performing operations on your behalf.
* <p>
* Changes only take effect during subsequent calls to
* {@link #tagSocket(Socket)}.
+ *
+ * @removed
+ * @deprecated use {@link #setThreadStatsUid(int)} instead.
*/
+ @Deprecated
public static void setThreadStatsUidSelf() {
setThreadStatsUid(android.os.Process.myUid());
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index df6ce8e..e22c65f 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -242,7 +242,9 @@
* Possible values are defined in {@link Build.VERSION_CODES}.
*
* @see #SDK_INT
+ * @hide
*/
+ @TestApi
public static final int FIRST_SDK_INT = SystemProperties
.getInt("ro.product.first_api_level", 0);
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 1160415..88d6e84 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -411,6 +411,9 @@
checkpoint = 0;
}
}
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
return progress;
}
@@ -440,6 +443,9 @@
checkpoint = 0;
}
}
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
return progress;
}
@@ -479,6 +485,9 @@
checkpoint = 0;
}
}
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
return progress;
}
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 0f70427..b254166 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -269,6 +269,7 @@
* time or throw.
*
* @throws DateTimeException when no accurate network time can be provided.
+ * @hide
*/
public static long currentNetworkTimeMillis() {
final IAlarmManager mgr = IAlarmManager.Stub
@@ -302,6 +303,7 @@
* time or throw.
*
* @throws DateTimeException when no accurate network time can be provided.
+ * @hide
*/
public static @NonNull Clock currentNetworkTimeClock() {
return new SimpleClock(ZoneOffset.UTC) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b58e5b3..9ecae8c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1348,10 +1348,18 @@
= "android.settings.NOTIFICATION_SETTINGS";
/**
+ * Activity Action: Show app listing settings, filtered by those that send notifications.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_ALL_APPS_NOTIFICATION_SETTINGS =
+ "android.settings.ALL_APPS_NOTIFICATION_SETTINGS";
+
+ /**
* Activity Action: Show notification settings for a single app.
* <p>
- * Input: {@link #EXTRA_APP_PACKAGE}, the package containing the channel to display.
- * Input: Optionally, {@link #EXTRA_CHANNEL_ID}, to highlight that channel.
+ * Input: {@link #EXTRA_APP_PACKAGE}, the package to display.
* <p>
* Output: Nothing.
*/
diff --git a/core/java/android/util/DataUnit.java b/core/java/android/util/DataUnit.java
index ea4266e..cf045b8 100644
--- a/core/java/android/util/DataUnit.java
+++ b/core/java/android/util/DataUnit.java
@@ -29,6 +29,8 @@
* "kibibyte" as an IEC unit of 1024 bytes.
* <p>
* This design is mirrored after {@link TimeUnit} and {@link ChronoUnit}.
+ *
+ * @hide
*/
public enum DataUnit {
KILOBYTES { @Override public long toBytes(long v) { return v * 1_000; } },
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 5bee87c..01fd090 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -274,6 +274,16 @@
public static final int STATE_DISABLED_BY_SERVICE = 4;
/**
+ * Same as {@link #STATE_UNKNOWN}, but used on
+ * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
+ * the URL bar changed on client mode
+ *
+ * @hide
+ */
+ public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
+
+
+ /**
* Timeout in ms for calls to the field classification service.
* @hide
*/
@@ -1947,15 +1957,24 @@
* Marks the state of the session as finished.
*
* @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
- * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
- * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
- * requests for the activity).
+ * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
+ * {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
+ * changed on compat mode), or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service
+ * disabled further autofill requests for the activity).
*/
private void setSessionFinished(int newState) {
synchronized (mLock) {
- if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
- resetSessionLocked(/* resetEnteredIds= */ false);
- mState = newState;
+ if (sVerbose) {
+ Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
+ + getStateAsString(newState));
+ }
+ if (newState == STATE_UNKNOWN_COMPAT_MODE) {
+ resetSessionLocked(/* resetEnteredIds= */ true);
+ mState = STATE_UNKNOWN;
+ } else {
+ resetSessionLocked(/* resetEnteredIds= */ false);
+ mState = newState;
+ }
}
}
@@ -2107,19 +2126,26 @@
@GuardedBy("mLock")
private String getStateAsStringLocked() {
- switch (mState) {
+ return getStateAsString(mState);
+ }
+
+ @NonNull
+ private static String getStateAsString(int state) {
+ switch (state) {
case STATE_UNKNOWN:
- return "STATE_UNKNOWN";
+ return "UNKNOWN";
case STATE_ACTIVE:
- return "STATE_ACTIVE";
+ return "ACTIVE";
case STATE_FINISHED:
- return "STATE_FINISHED";
+ return "FINISHED";
case STATE_SHOWING_SAVE_UI:
- return "STATE_SHOWING_SAVE_UI";
+ return "SHOWING_SAVE_UI";
case STATE_DISABLED_BY_SERVICE:
- return "STATE_DISABLED_BY_SERVICE";
+ return "DISABLED_BY_SERVICE";
+ case STATE_UNKNOWN_COMPAT_MODE:
+ return "UNKNOWN_COMPAT_MODE";
default:
- return "INVALID:" + mState;
+ return "INVALID:" + state;
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 28a7c12..cbd3ad5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -53,10 +53,21 @@
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 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;
+ public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11;
+ /**
+ * Hidden API access restrictions. This is a mask for bits representing the API enforcement
+ * policy, defined by {@code @ApplicationInfo.HiddenApiEnforcementPolicy}.
+ */
+ public static final int API_ENFORCEMENT_POLICY_MASK = (1 << 12) | (1 << 13);
+ /**
+ * Bit shift for use with {@link #API_ENFORCEMENT_POLICY_MASK}.
+ *
+ * (flags & API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT gives
+ * @ApplicationInfo.ApiEnforcementPolicy values.
+ */
+ public static final int API_ENFORCEMENT_POLICY_SHIFT =
+ Integer.numberOfTrailingZeros(API_ENFORCEMENT_POLICY_MASK);
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 66035f4..9467ecc 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -652,7 +652,7 @@
String args[] = {
"--setuid=1000",
"--setgid=1000",
- "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,1065,3001,3002,3003,3006,3007,3009,3010",
+ "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
index 03f2bc1..2959667 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -67,6 +67,10 @@
return old;
}
+ public static int getThreadSocketStatsUid() {
+ return threadSocketTags.get().statsUid;
+ }
+
@Override
public void tag(FileDescriptor fd) throws SocketException {
final SocketTags options = threadSocketTags.get();
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 122e5c4..8d4b56c 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -514,7 +514,7 @@
optional int32 uid = 3;
// Job IDs can technically be negative.
optional int32 job_id = 4;
- optional string tag = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
+ optional string tag = 5;
// Only valid for STOP_JOB or STOP_PERIODIC_JOB Events.
optional .android.app.job.StopReasonEnum stop_reason = 6;
}
@@ -538,7 +538,7 @@
// The UID that scheduled the job.
optional int32 calling_uid = 1;
- optional string tag = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
+ optional string tag = 2;
// The UID for which the job is being run.
optional int32 source_uid = 3;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 55c17b9..b5e223c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1340,6 +1340,30 @@
<integer-array name="config_autoBrightnessKeyboardBacklightValues">
</integer-array>
+ <!-- Array of light sensor lux values to define the minimum brightness curve, which guarantees
+ that any curve that dips below it is rejected by the system.
+ This prevents auto-brightness from setting the screen so dark as to prevent the user from
+ disabling auto-brightness or reseting the brightness curve via ADB.
+
+ The control points must be strictly increasing. Each control point corresponds to an entry
+ in the minimum brightness nits array. -->
+ <integer-array name="config_autoBrightnessMinimumBrightnessCurveLux">
+ <item>2000</item>
+ <item>4000</item>
+ </integer-array>
+
+ <!-- Array of desired screen brightness in nits corresponding to the lux values
+ in the config_autoBrightnessMinimumBrightnessCurveLux array.
+
+ This array should have size one greater than the size of the
+ config_autoBrightnessMinimumBrightnessCurveLux array. The values must be non-negative and
+ non-decreasing. -->
+ <array name="config_autoBrightnessMinimumBrightnessCurveNits">
+ <item>1.0</item>
+ <item>50.0</item>
+ <item>90.0</item>
+ </array>
+
<!-- Array of hysteresis constraint values for brightening, represented as tenths of a
percent. The length of this array is assumed to be one greater than
config_dynamicHysteresisLuxLevels. The brightening threshold is calculated as
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 75f8013..5eeb418 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1794,6 +1794,8 @@
<java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" />
<java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
<java-symbol type="array" name="config_autoBrightnessLevels" />
+ <java-symbol type="array" name="config_autoBrightnessMinimumBrightnessCurveLux" />
+ <java-symbol type="array" name="config_autoBrightnessMinimumBrightnessCurveNits" />
<java-symbol type="array" name="config_dynamicHysteresisBrightLevels" />
<java-symbol type="array" name="config_dynamicHysteresisDarkLevels" />
<java-symbol type="array" name="config_dynamicHysteresisLuxLevels" />
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index bacddf14..a4a1b94 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -64,6 +64,7 @@
<hidden-api-whitelisted-app package="com.android.gallery" />
<hidden-api-whitelisted-app package="com.android.hotspot2" />
<hidden-api-whitelisted-app package="com.android.keychain" />
+ <hidden-api-whitelisted-app package="com.android.launcher3" />
<hidden-api-whitelisted-app package="com.android.location.fused" />
<hidden-api-whitelisted-app package="com.android.managedprovisioning" />
<hidden-api-whitelisted-app package="com.android.mms.service" />
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
index 2daef0c..7949c77 100644
--- a/data/keyboards/Android.mk
+++ b/data/keyboards/Android.mk
@@ -28,7 +28,7 @@
validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
$(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps)
$(LOCAL_BUILT_MODULE) : $(framework_keylayouts) $(framework_keycharmaps) $(framework_keyconfigs) | $(validatekeymaps)
- $(hide) -q $(PRIVATE_VALIDATEKEYMAPS) $^
+ $(hide) $(PRIVATE_VALIDATEKEYMAPS) -q $^
$(hide) mkdir -p $(dir $@) && touch $@
# Run validatekeymaps uncondionally for platform build.
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 457e4aa..898939e 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -52,8 +52,20 @@
/**
* {@link Drawable} for drawing animated images (like GIF).
*
+ * <p>The framework handles decoding subsequent frames in another thread and
+ * updating when necessary. The drawable will only animate while it is being
+ * displayed.</p>
+ *
* <p>Created by {@link ImageDecoder#decodeDrawable}. A user needs to call
* {@link #start} to start the animation.</p>
+ *
+ * <p>It can also be defined in XML using the <code><animated-image></code>
+ * element.</p>
+ *
+ * @attr ref android.R.styleable#AnimatedImageDrawable_src
+ * @attr ref android.R.styleable#AnimatedImageDrawable_autoStart
+ * @attr ref android.R.styleable#AnimatedImageDrawable_repeatCount
+ * @attr ref android.R.styleable#AnimatedImageDrawable_autoMirrored
*/
public class AnimatedImageDrawable extends Drawable implements Animatable2 {
private int mIntrinsicWidth;
@@ -456,8 +468,8 @@
* <p>Does nothing if the animation is already running. If the animation is stopped,
* this will reset it.</p>
*
- * <p>If the animation starts, this will call
- * {@link Animatable2.AnimationCallback#onAnimationStart}.</p>
+ * <p>When the drawable is drawn, starting the animation,
+ * {@link Animatable2.AnimationCallback#onAnimationStart} will be called.</p>
*/
@Override
public void start() {
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 9175416..bc0e43b 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -3226,9 +3226,18 @@
if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) {
long[] stripOffsets =
- (long[]) stripOffsetsAttribute.getValue(mExifByteOrder);
+ convertToLongArray(stripOffsetsAttribute.getValue(mExifByteOrder));
long[] stripByteCounts =
- (long[]) stripByteCountsAttribute.getValue(mExifByteOrder);
+ convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
+
+ if (stripOffsets == null) {
+ Log.w(TAG, "stripOffsets should not be null.");
+ return;
+ }
+ if (stripByteCounts == null) {
+ Log.w(TAG, "stripByteCounts should not be null.");
+ return;
+ }
// Set thumbnail byte array data for non-consecutive strip bytes
byte[] totalStripBytes =
@@ -4025,4 +4034,22 @@
}
return false;
}
+
+ /**
+ * Convert given int[] to long[]. If long[] is given, just return it.
+ * Return null for other types of input.
+ */
+ private static long[] convertToLongArray(Object inputObj) {
+ if (inputObj instanceof int[]) {
+ int[] input = (int[]) inputObj;
+ long[] result = new long[input.length];
+ for (int i = 0; i < input.length; i++) {
+ result[i] = input[i];
+ }
+ return result;
+ } else if (inputObj instanceof long[]) {
+ return (long[]) inputObj;
+ }
+ return null;
+ }
}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 17002aa..591f33f 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -25,9 +25,7 @@
import android.content.Context;
import android.media.MediaPlaylistAgent.RepeatMode;
import android.media.MediaPlaylistAgent.ShuffleMode;
-import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
-import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.ErrorCode;
import android.media.session.MediaSessionManager;
@@ -56,7 +54,7 @@
* When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
* available only if the session service allows this controller by
* {@link MediaSession2.SessionCallback#onConnect(MediaSession2, ControllerInfo)} for the service.
- * Wait {@link ControllerCallback#onConnected(MediaController2, CommandGroup)} or
+ * Wait {@link ControllerCallback#onConnected(MediaController2, SessionCommandGroup2)} or
* {@link ControllerCallback#onDisconnected(MediaController2)} for the result.
* <p>
* A controller can be created through token from {@link MediaSessionManager} if you hold the
@@ -83,7 +81,7 @@
* @param allowedCommands commands that's allowed by the session.
*/
public void onConnected(@NonNull MediaController2 controller,
- @NonNull CommandGroup allowedCommands) { }
+ @NonNull SessionCommandGroup2 allowedCommands) { }
/**
* Called when the session refuses the controller or the controller is disconnected from
@@ -102,7 +100,8 @@
* Called when the session set the custom layout through the
* {@link MediaSession2#setCustomLayout(ControllerInfo, List)}.
* <p>
- * Can be called before {@link #onConnected(MediaController2, CommandGroup)} is called.
+ * Can be called before {@link #onConnected(MediaController2, SessionCommandGroup2)} is
+ * called.
*
* @param controller the controller for this event
* @param layout
@@ -126,7 +125,7 @@
* @param commands newly allowed commands
*/
public void onAllowedCommandsChanged(@NonNull MediaController2 controller,
- @NonNull CommandGroup commands) { }
+ @NonNull SessionCommandGroup2 commands) { }
/**
* Called when the session sent a custom command.
@@ -137,7 +136,7 @@
* @param receiver
*/
public void onCustomCommand(@NonNull MediaController2 controller,
- @NonNull Command command, @Nullable Bundle args,
+ @NonNull SessionCommand2 command, @Nullable Bundle args,
@Nullable ResultReceiver receiver) { }
/**
@@ -149,16 +148,6 @@
public void onPlayerStateChanged(@NonNull MediaController2 controller, int state) { }
/**
- * Called when the player's position is changed
- *
- * @param controller the controller for this event
- * @param eventTimeMs timestamp when the position information is sent from the session
- * @param positionMs position in millis
- */
- public void onPositionChanged(@NonNull MediaController2 controller,
- long eventTimeMs, long positionMs) { }
-
- /**
* Called when playback speed is changed.
*
* @param controller the controller for this event
@@ -180,6 +169,14 @@
@NonNull MediaItem2 item, @MediaPlayerBase.BuffState int state) { }
/**
+ * Called to indicate that seeking is completed.
+ *
+ * @param controller the controller for this event.
+ * @param position the previous seeking request.
+ */
+ public void onSeekCompleted(@NonNull MediaController2 controller, long position) { }
+
+ /**
* Called when a error from
*
* @param controller the controller for this event
@@ -197,7 +194,6 @@
*
* @param controller the controller for this event
* @param item new item
- * @see #onPositionChanged(MediaController2, long, long)
* @see #onBufferingStateChanged(MediaController2, MediaItem2, int)
*/
// TODO(jaewan): Use this (b/74316764)
@@ -423,16 +419,14 @@
}
/**
- * Start fast forwarding. If playback is already fast forwarding this
- * may increase the rate.
+ * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
*/
public void fastForward() {
mProvider.fastForward_impl();
}
/**
- * Start rewinding. If playback is already rewinding this may increase
- * the rate.
+ * Rewinds playback. If playback is already rewinding this may increase the rate.
*/
public void rewind() {
mProvider.rewind_impl();
@@ -689,7 +683,7 @@
* @param args optional argument
* @param cb optional result receiver
*/
- public void sendCustomCommand(@NonNull Command command, @Nullable Bundle args,
+ public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
@Nullable ResultReceiver cb) {
mProvider.sendCustomCommand_impl(command, args, cb);
}
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 1967a1c..423a1fd 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.MediaItem2Provider;
import android.os.Bundle;
@@ -81,8 +80,8 @@
return mProvider.toBundle_impl();
}
- public static MediaItem2 fromBundle(Context context, Bundle bundle) {
- return ApiLoader.getProvider().fromBundle_MediaItem2(context, bundle);
+ public static MediaItem2 fromBundle(Bundle bundle) {
+ return ApiLoader.getProvider().fromBundle_MediaItem2(bundle);
}
public String toString() {
@@ -161,11 +160,10 @@
/**
* Constructor for {@link Builder}
*
- * @param context
* @param flags
*/
- public Builder(@NonNull Context context, @Flags int flags) {
- mProvider = ApiLoader.getProvider().createMediaItem2Builder(context, this, flags);
+ public Builder(@Flags int flags) {
+ mProvider = ApiLoader.getProvider().createMediaItem2Builder(this, flags);
}
/**
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index 034d17e..f29d386 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
-import android.content.Context;
import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
import android.media.MediaSession2.ControllerInfo;
@@ -74,8 +73,8 @@
* Callback for the {@link MediaLibrarySession}.
*/
public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
- public MediaLibrarySessionCallback(@NonNull Context context) {
- super(context);
+ public MediaLibrarySessionCallback() {
+ super();
}
/**
@@ -401,10 +400,9 @@
* @param rootId The root id for browsing.
* @param extras Any extras about the library service.
*/
- public LibraryRoot(@NonNull Context context,
- @NonNull String rootId, @Nullable Bundle extras) {
+ public LibraryRoot(@NonNull String rootId, @Nullable Bundle extras) {
mProvider = ApiLoader.getProvider().createMediaLibraryService2LibraryRoot(
- context, this, rootId, extras);
+ this, rootId, extras);
}
/**
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index 59dd8cb..7b03ae0 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
-import android.content.Context;
import android.graphics.Bitmap;
import android.media.update.ApiLoader;
import android.media.update.MediaMetadata2Provider;
@@ -657,13 +656,11 @@
* Creates the {@link MediaMetadata2} from the bundle that previously returned by
* {@link #toBundle()}.
*
- * @param context context
* @param bundle bundle for the metadata
* @return a new MediaMetadata2
*/
- public static @NonNull MediaMetadata2 fromBundle(@NonNull Context context,
- @Nullable Bundle bundle) {
- return ApiLoader.getProvider().fromBundle_MediaMetadata2(context, bundle);
+ public static @NonNull MediaMetadata2 fromBundle(@Nullable Bundle bundle) {
+ return ApiLoader.getProvider().fromBundle_MediaMetadata2(bundle);
}
/**
@@ -677,8 +674,8 @@
* Create an empty Builder. Any field that should be included in the
* {@link MediaMetadata2} must be added.
*/
- public Builder(@NonNull Context context) {
- mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(context, this);
+ public Builder() {
+ mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this);
}
/**
@@ -688,8 +685,8 @@
*
* @param source
*/
- public Builder(@NonNull Context context, @NonNull MediaMetadata2 source) {
- mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(context, this, source);
+ public Builder(@NonNull MediaMetadata2 source) {
+ mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this, source);
}
/**
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
index 5c08f19..a426552 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -130,33 +130,6 @@
*/
public abstract void seekTo(long pos);
- /**
- * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
- * <p>
- * Default implementation sets the playback speed to the 2.0f
- * @see #setPlaybackSpeed(float)
- * @hide
- */
- // TODO(jaewan): Unhide (b/74724709)
- public void fastForward() {
- setPlaybackSpeed(2.0f);
- }
-
- /**
- * Rewinds playback. If playback is already rewinding this may increase the rate.
- * <p>
- * Default implementation sets the playback speed to the -1.0f if
- * {@link #isReversePlaybackSupported()} returns {@code true}.
- * @see #setPlaybackSpeed(float)
- * @hide
- */
- // TODO(jaewan): Unhide (b/74724709)
- public void rewind() {
- if (isReversePlaybackSupported()) {
- setPlaybackSpeed(-1.0f);
- }
- }
-
public static final long UNKNOWN_TIME = -1;
/**
@@ -340,10 +313,19 @@
/**
* Called to indicate that the playback speed has changed.
- * @param mpb the player that is buffering
+ * @param mpb the player that has changed the playback speed.
* @param speed the new playback speed.
*/
public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { }
+
+ /**
+ * Called to indicate that {@link #seekTo(long)} is completed.
+ *
+ * @param mpb the player that has completed seeking.
+ * @param position the previous seeking request.
+ * @see #seekTo(long)
+ */
+ public void onSeekCompleted(@NonNull MediaPlayerBase mpb, long position) { }
}
}
diff --git a/media/java/android/media/MediaPlaylistAgent.java b/media/java/android/media/MediaPlaylistAgent.java
index f339229..88f37e7 100644
--- a/media/java/android/media/MediaPlaylistAgent.java
+++ b/media/java/android/media/MediaPlaylistAgent.java
@@ -20,7 +20,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.MediaPlaylistAgentProvider;
@@ -148,8 +147,8 @@
@RepeatMode int repeatMode) { }
}
- public MediaPlaylistAgent(@NonNull Context context) {
- mProvider = ApiLoader.getProvider().createMediaPlaylistAgent(context, this);
+ public MediaPlaylistAgent() {
+ mProvider = ApiLoader.getProvider().createMediaPlaylistAgent(this);
}
/**
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 2033c59..2b3c2b4 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -33,8 +33,6 @@
import android.media.update.MediaSession2Provider;
import android.media.update.MediaSession2Provider.BuilderBaseProvider;
import android.media.update.MediaSession2Provider.CommandButtonProvider;
-import android.media.update.MediaSession2Provider.CommandGroupProvider;
-import android.media.update.MediaSession2Provider.CommandProvider;
import android.media.update.MediaSession2Provider.ControllerInfoProvider;
import android.media.update.ProviderCreator;
import android.net.Uri;
@@ -45,7 +43,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -85,255 +82,6 @@
private final MediaSession2Provider mProvider;
/**
- * Command code for the custom command which can be defined by string action in the
- * {@link Command}.
- */
- public static final int COMMAND_CODE_CUSTOM = 0;
-
- /**
- * Command code for {@link MediaController2#play()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
-
- /**
- * Command code for {@link MediaController2#pause()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
-
- /**
- * Command code for {@link MediaController2#stop()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
-
- /**
- * Command code for {@link MediaController2#skipToNextItem()}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the {@link SessionCallback#onCommandRequest(
- * MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
-
- /**
- * Command code for {@link MediaController2#skipToPreviousItem()}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the {@link SessionCallback#onCommandRequest(
- * MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
-
- /**
- * Command code for {@link MediaController2#prepare()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
-
- /**
- * Command code for {@link MediaController2#fastForward()}.
- * <p>
- * This is transport control command. Command would be sent directly to the player if the
- * session doesn't reject the request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7;
-
- /**
- * Command code for {@link MediaController2#rewind()}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_REWIND = 8;
-
- /**
- * Command code for {@link MediaController2#seekTo(long)}.
- * <p>
- * Command would be sent directly to the player if the session doesn't reject the request
- * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
-
- /**
- * Command code for both {@link MediaController2#setVolumeTo(int, int)}.
- * <p>
- * Command would set the device volume or send to the volume provider directly if the session
- * doesn't reject the request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_SET_VOLUME = 10;
-
- /**
- * Command code for both {@link MediaController2#adjustVolume(int, int)}.
- * <p>
- * Command would adjust the device volume or send to the volume provider directly if the session
- * doesn't reject the request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYBACK_ADJUST_VOLUME = 11;
-
- /**
- * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12;
-
- /**
- * Command code for {@link MediaController2#setShuffleMode(int)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13;
-
- /**
- * Command code for {@link MediaController2#setRepeatMode(int)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14;
-
- /**
- * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15;
-
- /**
- * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16;
-
- /**
- * Command code for {@link MediaController2#replacePlaylistItem(int, MediaItem2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17;
-
- /**
- * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
- * information to the controller.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
-
- /**
- * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
-
- /**
- * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
- * metadata information to the controller.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20;
-
- /**
- * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
- * <p>
- * Command would be sent directly to the playlist agent if the session doesn't reject the
- * request through the
- * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
- */
- public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21;
-
- /**
- * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
-
- /**
- * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
-
- /**
- * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
-
- /**
- * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
-
- /**
- * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
-
- /**
- * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
- */
- public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
-
- /**
- * Command code for {@link MediaController2#setRating(String, Rating2)}.
- * @hide
- */
- public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
-
- /**
- * Command code for {@link android.media.MediaLibraryService2.MediaLibrarySession} specific
- * functions. With or without this, a {@link MediaSession2} that isn't
- * {@link android.media.MediaLibraryService2.MediaLibrarySession} would automatically reject
- * the calls.
- *
- * @see android.media.MediaLibraryService2.MediaLibrarySession
- * @see MediaBrowser2
- * @hide
- */
- // TODO(jaewan): Remove
- public static final int COMMAND_CODE_BROWSER = 29;
-
- // TODO(jaewan): Add javadoc
- public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
- public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
- public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
- public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
- public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
- public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
- public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
-
- /**
* @hide
*/
@IntDef({ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED,
@@ -436,157 +184,6 @@
}
/**
- * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
- * <p>
- * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
- * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
- * {@link #getCustomCommand()} shouldn't be {@code null}.
- */
- public static final class Command {
- private final CommandProvider mProvider;
-
- public Command(@NonNull Context context, int commandCode) {
- mProvider = ApiLoader.getProvider().createMediaSession2Command(
- this, commandCode, null, null);
- }
-
- public Command(@NonNull Context context, @NonNull String action, @Nullable Bundle extras) {
- if (action == null) {
- throw new IllegalArgumentException("action shouldn't be null");
- }
- mProvider = ApiLoader.getProvider().createMediaSession2Command(
- this, COMMAND_CODE_CUSTOM, action, extras);
- }
-
- /**
- * @hide
- */
- public CommandProvider getProvider() {
- return mProvider;
- }
-
- public int getCommandCode() {
- return mProvider.getCommandCode_impl();
- }
-
- public @Nullable String getCustomCommand() {
- return mProvider.getCustomCommand_impl();
- }
-
- public @Nullable Bundle getExtras() {
- return mProvider.getExtras_impl();
- }
-
- /**
- * @return a new Bundle instance from the Command
- * @hide
- */
- public Bundle toBundle() {
- return mProvider.toBundle_impl();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof Command)) {
- return false;
- }
- return mProvider.equals_impl(((Command) obj).mProvider);
- }
-
- @Override
- public int hashCode() {
- return mProvider.hashCode_impl();
- }
-
- /**
- * @return a new Command instance from the Bundle
- * @hide
- */
- public static Command fromBundle(@NonNull Context context, @NonNull Bundle command) {
- return ApiLoader.getProvider().fromBundle_MediaSession2Command(context, command);
- }
- }
-
- /**
- * Represent set of {@link Command}.
- */
- public static final class CommandGroup {
- private final CommandGroupProvider mProvider;
-
- public CommandGroup(@NonNull Context context) {
- mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(
- context, this, null);
- }
-
- public CommandGroup(@NonNull Context context, @Nullable CommandGroup others) {
- mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(
- context, this, others);
- }
-
- /**
- * @hide
- */
- public CommandGroup(@NonNull CommandGroupProvider provider) {
- mProvider = provider;
- }
-
- public void addCommand(@NonNull Command command) {
- mProvider.addCommand_impl(command);
- }
-
- public void addCommand(int commandCode) {
- // TODO(jaewna): Implement
- }
-
- public void addAllPredefinedCommands() {
- mProvider.addAllPredefinedCommands_impl();
- }
-
- public void removeCommand(@NonNull Command command) {
- mProvider.removeCommand_impl(command);
- }
-
- public void removeCommand(int commandCode) {
- // TODO(jaewan): Implement.
- }
-
- public boolean hasCommand(@NonNull Command command) {
- return mProvider.hasCommand_impl(command);
- }
-
- public boolean hasCommand(int code) {
- return mProvider.hasCommand_impl(code);
- }
-
- public @NonNull Set<Command> getCommands() {
- return mProvider.getCommands_impl();
- }
-
- /**
- * @hide
- */
- public @NonNull CommandGroupProvider getProvider() {
- return mProvider;
- }
-
- /**
- * @return new bundle from the CommandGroup
- * @hide
- */
- public @NonNull Bundle toBundle() {
- return mProvider.toBundle_impl();
- }
-
- /**
- * @return new instance of CommandGroup from the bundle
- * @hide
- */
- public static @Nullable CommandGroup fromBundle(Context context, Bundle commands) {
- return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(context, commands);
- }
- }
-
- /**
* Callback to be called for all incoming commands from {@link MediaController2}s.
* <p>
* If it's not set, the session will accept all controllers and all incoming commands by
@@ -594,15 +191,6 @@
*/
// TODO(jaewan): Move this to updatable for default implementation (b/74091963)
public static abstract class SessionCallback {
- private final Context mContext;
-
- public SessionCallback(@NonNull Context context) {
- if (context == null) {
- throw new IllegalArgumentException("context shouldn't be null");
- }
- mContext = context;
- }
-
/**
* Called when a controller is created for this session. Return allowed commands for
* controller. By default it allows all connection requests and commands.
@@ -615,9 +203,9 @@
* @param controller controller information.
* @return allowed commands. Can be {@code null} to reject connection.
*/
- public @Nullable CommandGroup onConnect(@NonNull MediaSession2 session,
+ public @Nullable SessionCommandGroup2 onConnect(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller) {
- CommandGroup commands = new CommandGroup(mContext);
+ SessionCommandGroup2 commands = new SessionCommandGroup2();
commands.addAllPredefinedCommands();
return commands;
}
@@ -639,23 +227,23 @@
* @param controller controller information.
* @param command a command. This method will be called for every single command.
* @return {@code true} if you want to accept incoming command. {@code false} otherwise.
- * @see #COMMAND_CODE_PLAYBACK_PLAY
- * @see #COMMAND_CODE_PLAYBACK_PAUSE
- * @see #COMMAND_CODE_PLAYBACK_STOP
- * @see #COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
- * @see #COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
- * @see #COMMAND_CODE_PLAYBACK_PREPARE
- * @see #COMMAND_CODE_PLAYBACK_FAST_FORWARD
- * @see #COMMAND_CODE_PLAYBACK_REWIND
- * @see #COMMAND_CODE_PLAYBACK_SEEK_TO
- * @see #COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM
- * @see #COMMAND_CODE_PLAYLIST_ADD_ITEM
- * @see #COMMAND_CODE_PLAYLIST_REMOVE_ITEM
- * @see #COMMAND_CODE_PLAYLIST_GET_LIST
- * @see #COMMAND_CODE_PLAYBACK_SET_VOLUME
+ * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PLAY
+ * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PAUSE
+ * @see SessionCommand2#COMMAND_CODE_PLAYBACK_STOP
+ * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
+ * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
+ * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PREPARE
+ * @see SessionCommand2#COMMAND_CODE_SESSION_FAST_FORWARD
+ * @see SessionCommand2#COMMAND_CODE_SESSION_REWIND
+ * @see SessionCommand2#COMMAND_CODE_PLAYBACK_SEEK_TO
+ * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM
+ * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
+ * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REMOVE_ITEM
+ * @see SessionCommand2#COMMAND_CODE_PLAYLIST_GET_LIST
+ * @see SessionCommand2#COMMAND_CODE_SET_VOLUME
*/
public boolean onCommandRequest(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull Command command) {
+ @NonNull ControllerInfo controller, @NonNull SessionCommand2 command) {
return true;
}
@@ -678,7 +266,7 @@
/**
* Called when a controller sent a custom command through
- * {@link MediaController2#sendCustomCommand(Command, Bundle, ResultReceiver)}.
+ * {@link MediaController2#sendCustomCommand(SessionCommand2, Bundle, ResultReceiver)}.
*
* @param session the session for this event
* @param controller controller information
@@ -687,7 +275,7 @@
* @param cb optional result receiver
*/
public void onCustomCommand(@NonNull MediaSession2 session,
- @NonNull ControllerInfo controller, @NonNull Command customCommand,
+ @NonNull ControllerInfo controller, @NonNull SessionCommand2 customCommand,
@Nullable Bundle args, @Nullable ResultReceiver cb) { }
/**
@@ -698,7 +286,7 @@
* @param controller controller information
* @param mediaId media id
* @param extras optional extra bundle
- * @see #COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
+ * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
*/
public void onPlayFromMediaId(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull String mediaId,
@@ -715,7 +303,7 @@
* @param controller controller information
* @param query query string. Can be empty to indicate any suggested media
* @param extras optional extra bundle
- * @see #COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
+ * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
*/
public void onPlayFromSearch(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull String query,
@@ -729,7 +317,7 @@
* @param controller controller information
* @param uri uri
* @param extras optional extra bundle
- * @see #COMMAND_CODE_SESSION_PLAY_FROM_URI
+ * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_URI
*/
public void onPlayFromUri(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull Uri uri,
@@ -753,7 +341,7 @@
* @param controller controller information
* @param mediaId media id to prepare
* @param extras optional extra bundle
- * @see #COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
+ * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
*/
public void onPrepareFromMediaId(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull String mediaId,
@@ -777,7 +365,7 @@
* @param controller controller information
* @param query query string. Can be empty to indicate any suggested media
* @param extras optional extra bundle
- * @see #COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
+ * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
*/
public void onPrepareFromSearch(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull String query,
@@ -801,12 +389,26 @@
* @param controller controller information
* @param uri uri
* @param extras optional extra bundle
- * @see #COMMAND_CODE_SESSION_PREPARE_FROM_URI
+ * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_URI
*/
public void onPrepareFromUri(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull Uri uri, @Nullable Bundle extras) { }
/**
+ * Called when a controller called {@link MediaController2#fastForward()}
+ *
+ * @param session the session for this event
+ */
+ public void onFastForward(@NonNull MediaSession2 session) { }
+
+ /**
+ * Called when a controller called {@link MediaController2#rewind()}
+ *
+ * @param session the session for this event
+ */
+ public void onRewind(@NonNull MediaSession2 session) { }
+
+ /**
* Called when the player's current playing item is changed
* <p>
* When it's called, you should invalidate previous playback information and wait for later
@@ -861,6 +463,17 @@
@NonNull MediaPlayerBase player, float speed) { }
/**
+ * Called to indicate that {@link #seekTo(long)} is completed.
+ *
+ * @param session the session for this event.
+ * @param mpb the player that has completed seeking.
+ * @param position the previous seeking request.
+ * @see #seekTo(long)
+ */
+ public void onSeekCompleted(@NonNull MediaSession2 session, @NonNull MediaPlayerBase mpb,
+ long position) { }
+
+ /**
* Called when a playlist is changed from the {@link MediaPlaylistAgent}.
* <p>
* This is called when the underlying agent has called
@@ -1146,7 +759,7 @@
}
/**
- * Button for a {@link Command} that will be shown by the controller.
+ * Button for a {@link SessionCommand2} that will be shown by the controller.
* <p>
* It's up to the controller's decision to respect or ignore this customization request.
*/
@@ -1166,7 +779,8 @@
*
* @return command or {@code null}
*/
- public @Nullable Command getCommand() {
+ public @Nullable
+ SessionCommand2 getCommand() {
return mProvider.getCommand_impl();
}
@@ -1221,12 +835,11 @@
public static final class Builder {
private final CommandButtonProvider.BuilderProvider mProvider;
- public Builder(@NonNull Context context) {
- mProvider = ApiLoader.getProvider().createMediaSession2CommandButtonBuilder(
- context, this);
+ public Builder() {
+ mProvider = ApiLoader.getProvider().createMediaSession2CommandButtonBuilder(this);
}
- public @NonNull Builder setCommand(@Nullable Command command) {
+ public @NonNull Builder setCommand(@Nullable SessionCommand2 command) {
return mProvider.setCommand_impl(command);
}
@@ -1363,7 +976,8 @@
* expanded row: layout[5] layout[6] layout[7] layout[8] layout[9]
* main row: layout[3] layout[1] layout[0] layout[2] layout[4]
* <p>
- * This API can be called in the {@link SessionCallback#onConnect(MediaSession2, ControllerInfo)}.
+ * This API can be called in the {@link SessionCallback#onConnect(
+ * MediaSession2, ControllerInfo)}.
*
* @param controller controller to specify layout.
* @param layout ordered list of layout.
@@ -1380,7 +994,7 @@
* @param commands new allowed commands
*/
public void setAllowedCommands(@NonNull ControllerInfo controller,
- @NonNull CommandGroup commands) {
+ @NonNull SessionCommandGroup2 commands) {
mProvider.setAllowedCommands_impl(controller, commands);
}
@@ -1390,7 +1004,7 @@
* @param command a command
* @param args optional argument
*/
- public void sendCustomCommand(@NonNull Command command, @Nullable Bundle args) {
+ public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args) {
mProvider.sendCustomCommand_impl(command, args);
}
@@ -1401,8 +1015,9 @@
* @param args optional argument
* @param receiver result receiver for the session
*/
- public void sendCustomCommand(@NonNull ControllerInfo controller, @NonNull Command command,
- @Nullable Bundle args, @Nullable ResultReceiver receiver) {
+ public void sendCustomCommand(@NonNull ControllerInfo controller,
+ @NonNull SessionCommand2 command, @Nullable Bundle args,
+ @Nullable ResultReceiver receiver) {
// Equivalent to the MediaController.sendCustomCommand(Action action, ResultReceiver r);
mProvider.sendCustomCommand_impl(controller, command, args, receiver);
}
@@ -1448,20 +1063,6 @@
}
/**
- * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
- */
- public void fastForward() {
- mProvider.fastForward_impl();
- }
-
- /**
- * Rewinds playback. If playback is already rewinding this may increase the rate.
- */
- public void rewind() {
- mProvider.rewind_impl();
- }
-
- /**
* Move to a new location in the media stream.
*
* @param pos Position to move to, in milliseconds.
@@ -1562,7 +1163,8 @@
* <li>{@link MediaItem2} specified by {@link #setPlaylist(List, MediaMetadata2)} doesn't
* have {@link DataSourceDesc}</li>
* <li>{@link MediaController2#addPlaylistItem(int, MediaItem2)} is called and accepted
- * by {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)}.
+ * by {@link SessionCallback#onCommandRequest(
+ * MediaSession2, ControllerInfo, SessionCommand2)}.
* In that case, an item would be added automatically without the data source.</li>
* </ul>
* <p>
@@ -1574,9 +1176,9 @@
* @param helper a data source missing helper.
* @throws IllegalStateException when the helper is set when the playlist agent is set
* @see #setPlaylist(List, MediaMetadata2)
- * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, Command)
- * @see #COMMAND_CODE_PLAYLIST_ADD_ITEM
- * @see #COMMAND_CODE_PLAYLIST_REPLACE_ITEM
+ * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)
+ * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
+ * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REPLACE_ITEM
*/
public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
mProvider.setOnDataSourceMissingHelper_impl(helper);
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index 85ac9b2..6c3a4bf 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -21,7 +21,6 @@
import android.annotation.Nullable;
import android.app.Notification;
import android.app.Service;
-import android.content.Context;
import android.content.Intent;
import android.media.MediaSession2.ControllerInfo;
import android.media.update.ApiLoader;
@@ -213,16 +212,14 @@
/**
* Default constructor
*
- * @param context context
* @param notificationId notification id to be used for
* {@link android.app.NotificationManager#notify(int, Notification)}.
* @param notification a notification to make session service foreground service. Media
* style notification is recommended here.
*/
- public MediaNotification(@NonNull Context context,
- int notificationId, @NonNull Notification notification) {
+ public MediaNotification(int notificationId, @NonNull Notification notification) {
mProvider = ApiLoader.getProvider().createMediaSessionService2MediaNotification(
- context, this, notificationId, notification);
+ this, notificationId, notification);
}
public int getNotificationId() {
diff --git a/media/java/android/media/Rating2.java b/media/java/android/media/Rating2.java
index 5f7a334..9213190 100644
--- a/media/java/android/media/Rating2.java
+++ b/media/java/android/media/Rating2.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.IntDef;
-import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.Rating2Provider;
import android.os.Bundle;
@@ -29,6 +28,7 @@
import java.lang.annotation.RetentionPolicy;
/**
+ * @hide
* A class to encapsulate rating information used as content metadata.
* A rating is defined by its rating style (see {@link #RATING_HEART},
* {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
@@ -126,12 +126,11 @@
/**
* Create an instance from bundle object, previoulsy created by {@link #toBundle()}
*
- * @param context context
* @param bundle bundle
* @return new Rating2 instance or {@code null} for error
*/
- public static Rating2 fromBundle(@NonNull Context context, @Nullable Bundle bundle) {
- return ApiLoader.getProvider().fromBundle_Rating2(context, bundle);
+ public static Rating2 fromBundle(@Nullable Bundle bundle) {
+ return ApiLoader.getProvider().fromBundle_Rating2(bundle);
}
/**
@@ -146,39 +145,35 @@
* Return a Rating2 instance with no rating.
* Create and return a new Rating2 instance with no rating known for the given
* rating style.
- * @param context context
* @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
* {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
* or {@link #RATING_PERCENTAGE}.
* @return null if an invalid rating style is passed, a new Rating2 instance otherwise.
*/
- public static @Nullable Rating2 newUnratedRating(@NonNull Context context,
- @Style int ratingStyle) {
- return ApiLoader.getProvider().newUnratedRating_Rating2(context, ratingStyle);
+ public static @Nullable Rating2 newUnratedRating(@Style int ratingStyle) {
+ return ApiLoader.getProvider().newUnratedRating_Rating2(ratingStyle);
}
/**
* Return a Rating2 instance with a heart-based rating.
* Create and return a new Rating2 instance with a rating style of {@link #RATING_HEART},
* and a heart-based rating.
- * @param context context
* @param hasHeart true for a "heart selected" rating, false for "heart unselected".
* @return a new Rating2 instance.
*/
- public static @Nullable Rating2 newHeartRating(@NonNull Context context, boolean hasHeart) {
- return ApiLoader.getProvider().newHeartRating_Rating2(context, hasHeart);
+ public static @Nullable Rating2 newHeartRating(boolean hasHeart) {
+ return ApiLoader.getProvider().newHeartRating_Rating2(hasHeart);
}
/**
* Return a Rating2 instance with a thumb-based rating.
* Create and return a new Rating2 instance with a {@link #RATING_THUMB_UP_DOWN}
* rating style, and a "thumb up" or "thumb down" rating.
- * @param context context
* @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
* @return a new Rating2 instance.
*/
- public static @Nullable Rating2 newThumbRating(@NonNull Context context, boolean thumbIsUp) {
- return ApiLoader.getProvider().newThumbRating_Rating2(context, thumbIsUp);
+ public static @Nullable Rating2 newThumbRating(boolean thumbIsUp) {
+ return ApiLoader.getProvider().newThumbRating_Rating2(thumbIsUp);
}
/**
@@ -186,7 +181,6 @@
* Create and return a new Rating2 instance with one of the star-base rating styles
* and the given integer or fractional number of stars. Non integer values can for instance
* be used to represent an average rating value, which might not be an integer number of stars.
- * @param context context
* @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
* {@link #RATING_5_STARS}.
* @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to
@@ -194,26 +188,25 @@
* @return null if the rating style is invalid, or the rating is out of range,
* a new Rating2 instance otherwise.
*/
- public static @Nullable Rating2 newStarRating(@NonNull Context context,
+ public static @Nullable Rating2 newStarRating(
@StarStyle int starRatingStyle, float starRating) {
- return ApiLoader.getProvider().newStarRating_Rating2(context, starRatingStyle, starRating);
+ return ApiLoader.getProvider().newStarRating_Rating2(starRatingStyle, starRating);
}
/**
* Return a Rating2 instance with a percentage-based rating.
* Create and return a new Rating2 instance with a {@link #RATING_PERCENTAGE}
* rating style, and a rating of the given percentage.
- * @param context context
* @param percent the value of the rating
* @return null if the rating is out of range, a new Rating2 instance otherwise.
*/
- public static @Nullable Rating2 newPercentageRating(@NonNull Context context, float percent) {
- return ApiLoader.getProvider().newPercentageRating_Rating2(context, percent);
+ public static @Nullable Rating2 newPercentageRating(float percent) {
+ return ApiLoader.getProvider().newPercentageRating_Rating2(percent);
}
/**
* Return whether there is a rating value available.
- * @return true if the instance was not created with {@link #newUnratedRating(Context, int)}.
+ * @return true if the instance was not created with {@link #newUnratedRating(int)}.
*/
public boolean isRated() {
return mProvider.isRated_impl();
diff --git a/media/java/android/media/SessionCommand2.java b/media/java/android/media/SessionCommand2.java
new file mode 100644
index 0000000..fe86a3a
--- /dev/null
+++ b/media/java/android/media/SessionCommand2.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.net.Uri;
+import android.os.Bundle;
+
+import java.util.List;
+
+/**
+ * @hide
+ * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
+ * <p>
+ * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
+ * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
+ * {@link #getCustomCommand()} shouldn't be {@code null}.
+ */
+public final class SessionCommand2 {
+ /**
+ * Command code for the custom command which can be defined by string action in the
+ * {@link SessionCommand2}.
+ */
+ public static final int COMMAND_CODE_CUSTOM = 0;
+
+ /**
+ * Command code for {@link MediaController2#play()}.
+ * <p>
+ * Command would be sent directly to the player if the session doesn't reject the request
+ * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+ * SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
+
+ /**
+ * Command code for {@link MediaController2#pause()}.
+ * <p>
+ * Command would be sent directly to the player if the session doesn't reject the request
+ * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+ * SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
+
+ /**
+ * Command code for {@link MediaController2#stop()}.
+ * <p>
+ * Command would be sent directly to the player if the session doesn't reject the request
+ * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+ * SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
+
+ /**
+ * Command code for {@link MediaController2#skipToNextItem()}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the {@link SessionCallback#onCommandRequest(
+ * MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
+
+ /**
+ * Command code for {@link MediaController2#skipToPreviousItem()}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the {@link SessionCallback#onCommandRequest(
+ * MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
+
+ /**
+ * Command code for {@link MediaController2#prepare()}.
+ * <p>
+ * Command would be sent directly to the player if the session doesn't reject the request
+ * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+ * SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
+
+ /**
+ * Command code for {@link MediaController2#fastForward()}.
+ */
+ public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 7;
+
+ /**
+ * Command code for {@link MediaController2#rewind()}.
+ */
+ public static final int COMMAND_CODE_SESSION_REWIND = 8;
+
+ /**
+ * Command code for {@link MediaController2#seekTo(long)}.
+ * <p>
+ * Command would be sent directly to the player if the session doesn't reject the request
+ * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+ * SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
+
+ /**
+ * Command code for both {@link MediaController2#setVolumeTo(int, int)}.
+ * <p>
+ * Command would set the device volume or send to the volume provider directly if the session
+ * doesn't reject the request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_SET_VOLUME = 10;
+
+ /**
+ * Command code for both {@link MediaController2#adjustVolume(int, int)}.
+ * <p>
+ * Command would adjust the device volume or send to the volume provider directly if the session
+ * doesn't reject the request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_ADJUST_VOLUME = 11;
+
+ /**
+ * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12;
+
+ /**
+ * Command code for {@link MediaController2#setShuffleMode(int)}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13;
+
+ /**
+ * Command code for {@link MediaController2#setRepeatMode(int)}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14;
+
+ /**
+ * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15;
+
+ /**
+ * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16;
+
+ /**
+ * Command code for {@link MediaController2#replacePlaylistItem(int, MediaItem2)}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17;
+
+ /**
+ * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
+ * information to the controller.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
+
+ /**
+ * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
+
+ /**
+ * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
+ * metadata information to the controller.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20;
+
+ /**
+ * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
+ * <p>
+ * Command would be sent directly to the playlist agent if the session doesn't reject the
+ * request through the
+ * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+ */
+ public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21;
+
+ /**
+ * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
+ */
+ public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
+
+ /**
+ * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
+ */
+ public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
+
+ /**
+ * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
+ */
+ public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
+
+ /**
+ * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
+ */
+ public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
+
+ /**
+ * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
+ */
+ public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
+
+ /**
+ * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
+ */
+ public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
+
+ /**
+ * Command code for {@link MediaController2#setRating(String, Rating2)}.
+ */
+ public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
+
+ // TODO(jaewan): Add javadoc
+ public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
+ public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
+ public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
+ public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
+ public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
+ public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
+ public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
+
+ // TODO(jaewan): Rename and move provider
+ private final MediaSession2Provider.CommandProvider mProvider;
+
+ public SessionCommand2(int commandCode) {
+ mProvider = ApiLoader.getProvider().createMediaSession2Command(
+ this, commandCode, null, null);
+ }
+
+ public SessionCommand2(@NonNull String action, @Nullable Bundle extras) {
+ if (action == null) {
+ throw new IllegalArgumentException("action shouldn't be null");
+ }
+ mProvider = ApiLoader.getProvider().createMediaSession2Command(
+ this, COMMAND_CODE_CUSTOM, action, extras);
+ }
+
+ /**
+ * @hide
+ */
+ public MediaSession2Provider.CommandProvider getProvider() {
+ return mProvider;
+ }
+
+ public int getCommandCode() {
+ return mProvider.getCommandCode_impl();
+ }
+
+ public @Nullable String getCustomCommand() {
+ return mProvider.getCustomCommand_impl();
+ }
+
+ public @Nullable Bundle getExtras() {
+ return mProvider.getExtras_impl();
+ }
+
+ /**
+ * @return a new Bundle instance from the Command
+ * @hide
+ */
+ public Bundle toBundle() {
+ return mProvider.toBundle_impl();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SessionCommand2)) {
+ return false;
+ }
+ return mProvider.equals_impl(((SessionCommand2) obj).mProvider);
+ }
+
+ @Override
+ public int hashCode() {
+ return mProvider.hashCode_impl();
+ }
+
+ /**
+ * @return a new Command instance from the Bundle
+ * @hide
+ */
+ public static SessionCommand2 fromBundle(@NonNull Bundle command) {
+ return ApiLoader.getProvider().fromBundle_MediaSession2Command(command);
+ }
+}
diff --git a/media/java/android/media/SessionCommandGroup2.java b/media/java/android/media/SessionCommandGroup2.java
new file mode 100644
index 0000000..399765e
--- /dev/null
+++ b/media/java/android/media/SessionCommandGroup2.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.os.Bundle;
+
+import java.util.Set;
+
+/**
+ * @hide
+ * Represent set of {@link SessionCommand2}.
+ */
+public final class SessionCommandGroup2 {
+ // TODO(jaewan): Rename and move provider
+ private final MediaSession2Provider.CommandGroupProvider mProvider;
+
+ public SessionCommandGroup2() {
+ mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, null);
+ }
+
+ public SessionCommandGroup2(@Nullable SessionCommandGroup2 others) {
+ mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, others);
+ }
+
+ /**
+ * @hide
+ */
+ public SessionCommandGroup2(@NonNull MediaSession2Provider.CommandGroupProvider provider) {
+ mProvider = provider;
+ }
+
+ public void addCommand(@NonNull SessionCommand2 command) {
+ mProvider.addCommand_impl(command);
+ }
+
+ public void addCommand(int commandCode) {
+ // TODO(jaewna): Implement
+ }
+
+ public void addAllPredefinedCommands() {
+ mProvider.addAllPredefinedCommands_impl();
+ }
+
+ public void removeCommand(@NonNull SessionCommand2 command) {
+ mProvider.removeCommand_impl(command);
+ }
+
+ public void removeCommand(int commandCode) {
+ // TODO(jaewan): Implement.
+ }
+
+ public boolean hasCommand(@NonNull SessionCommand2 command) {
+ return mProvider.hasCommand_impl(command);
+ }
+
+ public boolean hasCommand(int code) {
+ return mProvider.hasCommand_impl(code);
+ }
+
+ public @NonNull
+ Set<SessionCommand2> getCommands() {
+ return mProvider.getCommands_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public @NonNull MediaSession2Provider.CommandGroupProvider getProvider() {
+ return mProvider;
+ }
+
+ /**
+ * @return new bundle from the CommandGroup
+ * @hide
+ */
+ public @NonNull Bundle toBundle() {
+ return mProvider.toBundle_impl();
+ }
+
+ /**
+ * @return new instance of CommandGroup from the bundle
+ * @hide
+ */
+ public static @Nullable SessionCommandGroup2 fromBundle(Bundle commands) {
+ return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(commands);
+ }
+}
diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java
index f088be3..bf2d445 100644
--- a/media/java/android/media/SessionToken2.java
+++ b/media/java/android/media/SessionToken2.java
@@ -150,8 +150,8 @@
* @param bundle
* @return
*/
- public static SessionToken2 fromBundle(@NonNull Context context, @NonNull Bundle bundle) {
- return ApiLoader.getProvider().fromBundle_SessionToken2(context, bundle);
+ public static SessionToken2 fromBundle(@NonNull Bundle bundle) {
+ return ApiLoader.getProvider().fromBundle_SessionToken2(bundle);
}
/**
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 2d96d096..1a4608f 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -18,7 +18,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.VolumeProvider2Provider;
@@ -75,10 +74,9 @@
* @param maxVolume The maximum allowed volume.
* @param currentVolume The current volume on the output.
*/
- public VolumeProvider2(@NonNull Context context, @ControlType int controlType,
- int maxVolume, int currentVolume) {
+ public VolumeProvider2(@ControlType int controlType, int maxVolume, int currentVolume) {
mProvider = ApiLoader.getProvider().createVolumeProvider2(
- context, this, controlType, maxVolume, currentVolume);
+ this, controlType, maxVolume, currentVolume);
}
/**
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index b7f4998..3b12fca 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -407,7 +407,7 @@
List<Bundle> bundles = mService.getSessionTokens(
/* activeSessionOnly */ true, /* sessionServiceOnly */ false,
mContext.getPackageName());
- return toTokenList(mContext, bundles);
+ return toTokenList(bundles);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
return Collections.emptyList();
@@ -430,7 +430,7 @@
List<Bundle> bundles = mService.getSessionTokens(
/* activeSessionOnly */ false, /* sessionServiceOnly */ true,
mContext.getPackageName());
- return toTokenList(mContext, bundles);
+ return toTokenList(bundles);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
return Collections.emptyList();
@@ -455,7 +455,7 @@
List<Bundle> bundles = mService.getSessionTokens(
/* activeSessionOnly */ false, /* sessionServiceOnly */ false,
mContext.getPackageName());
- return toTokenList(mContext, bundles);
+ return toTokenList(bundles);
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
return Collections.emptyList();
@@ -540,11 +540,11 @@
}
}
- private static List<SessionToken2> toTokenList(Context context, List<Bundle> bundles) {
+ private static List<SessionToken2> toTokenList(List<Bundle> bundles) {
List<SessionToken2> tokens = new ArrayList<>();
if (bundles != null) {
for (int i = 0; i < bundles.size(); i++) {
- SessionToken2 token = SessionToken2.fromBundle(context, bundles.get(i));
+ SessionToken2 token = SessionToken2.fromBundle(bundles.get(i));
if (token != null) {
tokens.add(token);
}
@@ -829,7 +829,7 @@
final Context context = mContext;
final OnSessionTokensChangedListener listener = mListener;
if (context != null && listener != null) {
- listener.onSessionTokensChanged(toTokenList(context, bundles));
+ listener.onSessionTokensChanged(toTokenList(bundles));
}
});
}
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 213897d..7234f7b 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -21,7 +21,7 @@
import android.media.MediaController2.PlaybackInfo;
import android.media.MediaItem2;
import android.media.MediaMetadata2;
-import android.media.MediaSession2.Command;
+import android.media.SessionCommand2;
import android.media.Rating2;
import android.media.SessionToken2;
import android.net.Uri;
@@ -52,9 +52,11 @@
void playFromSearch_impl(String query, Bundle extras);
void playFromUri_impl(Uri uri, Bundle extras);
void playFromMediaId_impl(String mediaId, Bundle extras);
+ void fastForward_impl();
+ void rewind_impl();
void setRating_impl(String mediaId, Rating2 rating);
- void sendCustomCommand_impl(Command command, Bundle args, ResultReceiver cb);
+ void sendCustomCommand_impl(SessionCommand2 command, Bundle args, ResultReceiver cb);
List<MediaItem2> getPlaylist_impl();
void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
MediaMetadata2 getPlaylistMetadata_impl();
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index 5a1db3e..4751348 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -23,10 +23,10 @@
import android.media.MediaPlayerBase;
import android.media.MediaPlaylistAgent;
import android.media.MediaSession2;
-import android.media.MediaSession2.Command;
+import android.media.SessionCommand2;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandButton.Builder;
-import android.media.MediaSession2.CommandGroup;
+import android.media.SessionCommandGroup2;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.OnDataSourceMissingHelper;
import android.media.MediaSession2.SessionCallback;
@@ -55,10 +55,10 @@
List<ControllerInfo> getConnectedControllers_impl();
void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout);
void setAudioFocusRequest_impl(AudioFocusRequest afr);
- void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands);
- void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
+ void setAllowedCommands_impl(ControllerInfo controller, SessionCommandGroup2 commands);
+ void sendCustomCommand_impl(ControllerInfo controller, SessionCommand2 command, Bundle args,
ResultReceiver receiver);
- void sendCustomCommand_impl(Command command, Bundle args);
+ void sendCustomCommand_impl(SessionCommand2 command, Bundle args);
void addPlaylistItem_impl(int index, MediaItem2 item);
void removePlaylistItem_impl(MediaItem2 item);
void replacePlaylistItem_impl(int index, MediaItem2 item);
@@ -72,6 +72,7 @@
void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
void clearOnDataSourceMissingHelper_impl();
+ // TODO(jaewan): Rename and move provider
interface CommandProvider {
int getCommandCode_impl();
String getCustomCommand_impl();
@@ -82,25 +83,26 @@
int hashCode_impl();
}
+ // TODO(jaewan): Rename and move provider
interface CommandGroupProvider {
- void addCommand_impl(Command command);
+ void addCommand_impl(SessionCommand2 command);
void addAllPredefinedCommands_impl();
- void removeCommand_impl(Command command);
- boolean hasCommand_impl(Command command);
+ void removeCommand_impl(SessionCommand2 command);
+ boolean hasCommand_impl(SessionCommand2 command);
boolean hasCommand_impl(int code);
- Set<Command> getCommands_impl();
+ Set<SessionCommand2> getCommands_impl();
Bundle toBundle_impl();
}
interface CommandButtonProvider {
- Command getCommand_impl();
+ SessionCommand2 getCommand_impl();
int getIconResId_impl();
String getDisplayName_impl();
Bundle getExtras_impl();
boolean isEnabled_impl();
interface BuilderProvider {
- Builder setCommand_impl(Command command);
+ Builder setCommand_impl(SessionCommand2 command);
Builder setIconResId_impl(int resId);
Builder setDisplayName_impl(String displayName);
Builder setEnabled_impl(boolean enabled);
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 1c0e255..8687b80 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -35,6 +35,8 @@
import android.media.MediaSessionService2;
import android.media.MediaSessionService2.MediaNotification;
import android.media.Rating2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
@@ -67,16 +69,16 @@
ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
- CommandProvider createMediaSession2Command(MediaSession2.Command instance,
+ CommandProvider createMediaSession2Command(SessionCommand2 instance,
int commandCode, String action, Bundle extra);
- MediaSession2.Command fromBundle_MediaSession2Command(Context context, Bundle bundle);
- CommandGroupProvider createMediaSession2CommandGroup(Context context,
- MediaSession2.CommandGroup instance, MediaSession2.CommandGroup others);
- MediaSession2.CommandGroup fromBundle_MediaSession2CommandGroup(Context context, Bundle bundle);
+ SessionCommand2 fromBundle_MediaSession2Command(Bundle bundle);
+ CommandGroupProvider createMediaSession2CommandGroup(SessionCommandGroup2 instance,
+ SessionCommandGroup2 others);
+ SessionCommandGroup2 fromBundle_MediaSession2CommandGroup(Bundle bundle);
ControllerInfoProvider createMediaSession2ControllerInfo(Context context,
MediaSession2.ControllerInfo instance, int uid, int pid,
String packageName, IInterface callback);
- CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(Context context,
+ CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(
MediaSession2.CommandButton.Builder instance);
BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
Context context, MediaSession2.Builder instance);
@@ -88,7 +90,7 @@
SessionToken2 token, Executor executor, BrowserCallback callback);
MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance);
- MediaNotificationProvider createMediaSessionService2MediaNotification(Context context,
+ MediaNotificationProvider createMediaSessionService2MediaNotification(
MediaNotification mediaNotification, int notificationId, Notification notification);
MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
@@ -96,33 +98,32 @@
createMediaLibraryService2Builder(
MediaLibraryService2 service, MediaLibrarySession.Builder instance,
Executor callbackExecutor, MediaLibrarySessionCallback callback);
- LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance,
- String rootId, Bundle extras);
+ LibraryRootProvider createMediaLibraryService2LibraryRoot(LibraryRoot instance, String rootId,
+ Bundle extras);
SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
String packageName, String serviceName, int uid);
- SessionToken2 fromBundle_SessionToken2(Context context, Bundle bundle);
+ SessionToken2 fromBundle_SessionToken2(Bundle bundle);
- MediaItem2Provider.BuilderProvider createMediaItem2Builder(
- Context context, MediaItem2.Builder instance, int flags);
- MediaItem2 fromBundle_MediaItem2(Context context, Bundle bundle);
+ MediaItem2Provider.BuilderProvider createMediaItem2Builder(MediaItem2.Builder instance,
+ int flags);
+ MediaItem2 fromBundle_MediaItem2(Bundle bundle);
- VolumeProvider2Provider createVolumeProvider2(Context context, VolumeProvider2 instance,
- int controlType, int maxVolume, int currentVolume);
+ VolumeProvider2Provider createVolumeProvider2(VolumeProvider2 instance, int controlType,
+ int maxVolume, int currentVolume);
- MediaMetadata2 fromBundle_MediaMetadata2(Context context, Bundle bundle);
+ MediaMetadata2 fromBundle_MediaMetadata2(Bundle bundle);
MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
- Context context, MediaMetadata2.Builder instance);
+ MediaMetadata2.Builder instance);
MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
- Context context, MediaMetadata2.Builder instance, MediaMetadata2 source);
+ MediaMetadata2.Builder instance, MediaMetadata2 source);
- Rating2 newUnratedRating_Rating2(Context context, int ratingStyle);
- Rating2 fromBundle_Rating2(Context context, Bundle bundle);
- Rating2 newHeartRating_Rating2(Context context, boolean hasHeart);
- Rating2 newThumbRating_Rating2(Context context, boolean thumbIsUp);
- Rating2 newStarRating_Rating2(Context context, int starRatingStyle, float starRating);
- Rating2 newPercentageRating_Rating2(Context context, float percent);
+ Rating2 newUnratedRating_Rating2(int ratingStyle);
+ Rating2 fromBundle_Rating2(Bundle bundle);
+ Rating2 newHeartRating_Rating2(boolean hasHeart);
+ Rating2 newThumbRating_Rating2(boolean thumbIsUp);
+ Rating2 newStarRating_Rating2(int starRatingStyle, float starRating);
+ Rating2 newPercentageRating_Rating2(float percent);
- MediaPlaylistAgentProvider createMediaPlaylistAgent(Context context,
- MediaPlaylistAgent instance);
+ MediaPlaylistAgentProvider createMediaPlaylistAgent(MediaPlaylistAgent instance);
}
diff --git a/media/java/android/media/update/TransportControlProvider.java b/media/java/android/media/update/TransportControlProvider.java
index 03944d2..d89a88a 100644
--- a/media/java/android/media/update/TransportControlProvider.java
+++ b/media/java/android/media/update/TransportControlProvider.java
@@ -29,8 +29,6 @@
void skipToNextItem_impl();
void prepare_impl();
- void fastForward_impl();
- void rewind_impl();
void seekTo_impl(long pos);
void skipToPlaylistItem_impl(MediaItem2 item);
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 32a00d5..1244d7f 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -243,11 +243,11 @@
}
};
- public MtpDatabase(Context context, Context userContext, String volumeName,
+ public MtpDatabase(Context context, String volumeName,
String[] subDirectories) {
native_setup();
mContext = context;
- mMediaProvider = userContext.getContentResolver()
+ mMediaProvider = context.getContentResolver()
.acquireContentProviderClient(MediaStore.AUTHORITY);
mVolumeName = volumeName;
mObjectsUri = Files.getMtpObjectsUri(volumeName);
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index f2b1106..8af5ff7 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -18,6 +18,8 @@
import com.android.internal.util.Preconditions;
+import java.io.FileDescriptor;
+
/**
* Java wrapper for MTP/PTP support as USB responder.
* {@hide}
@@ -34,6 +36,7 @@
public MtpServer(
MtpDatabase database,
+ FileDescriptor controlFd,
boolean usePtp,
Runnable onTerminate,
String deviceInfoManufacturer,
@@ -44,6 +47,7 @@
mOnTerminate = Preconditions.checkNotNull(onTerminate);
native_setup(
database,
+ controlFd,
usePtp,
deviceInfoManufacturer,
deviceInfoModel,
@@ -92,6 +96,7 @@
public static native final void native_configure(boolean usePtp);
private native final void native_setup(
MtpDatabase database,
+ FileDescriptor controlFd,
boolean usePtp,
String deviceInfoManufacturer,
String deviceInfoModel,
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index c76cebe..8730e06 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -55,22 +55,17 @@
return (MtpServer*)env->GetLongField(thiz, field_MtpServer_nativeContext);
}
-static void android_mtp_configure(JNIEnv *, jobject, jboolean usePtp) {
- MtpServer::configure(usePtp);
-}
-
static void
-android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jboolean usePtp,
- jstring deviceInfoManufacturer,
- jstring deviceInfoModel,
- jstring deviceInfoDeviceVersion,
- jstring deviceInfoSerialNumber)
+android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jobject jControlFd,
+ jboolean usePtp, jstring deviceInfoManufacturer, jstring deviceInfoModel,
+ jstring deviceInfoDeviceVersion, jstring deviceInfoSerialNumber)
{
const char *deviceInfoManufacturerStr = env->GetStringUTFChars(deviceInfoManufacturer, NULL);
const char *deviceInfoModelStr = env->GetStringUTFChars(deviceInfoModel, NULL);
const char *deviceInfoDeviceVersionStr = env->GetStringUTFChars(deviceInfoDeviceVersion, NULL);
const char *deviceInfoSerialNumberStr = env->GetStringUTFChars(deviceInfoSerialNumber, NULL);
- MtpServer* server = new MtpServer(getMtpDatabase(env, javaDatabase),
+ int controlFd = dup(jniGetFDFromFileDescriptor(env, jControlFd));
+ MtpServer* server = new MtpServer(getMtpDatabase(env, javaDatabase), controlFd,
usePtp,
MtpString((deviceInfoManufacturerStr != NULL) ? deviceInfoManufacturerStr : ""),
MtpString((deviceInfoModelStr != NULL) ? deviceInfoModelStr : ""),
@@ -201,8 +196,7 @@
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
- {"native_configure", "(Z)V", (void *)android_mtp_configure},
- {"native_setup", "(Landroid/mtp/MtpDatabase;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ {"native_setup", "(Landroid/mtp/MtpDatabase;Ljava/io/FileDescriptor;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
(void *)android_mtp_MtpServer_setup},
{"native_run", "()V", (void *)android_mtp_MtpServer_run},
{"native_cleanup", "()V", (void *)android_mtp_MtpServer_cleanup},
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 4db0034..7828c4c 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -42,6 +42,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.webkit.CookieManager;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
@@ -147,6 +148,7 @@
final WebView webview = getWebview();
webview.clearCache(true);
+ CookieManager.getInstance().setAcceptThirdPartyCookies(webview, true);
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 24a6804..584fc34 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -97,6 +97,12 @@
<!-- Summary for Connected wifi network without internet -->
<string name="wifi_connected_no_internet">Connected, no internet</string>
+ <!-- Wi-Fi status indicating that the current network is connected, but has no internet access. -->
+ <string name="wifi_status_no_internet">No internet</string>
+
+ <!-- Wi-Fi status indicating that the current network is connected requires sign in to access the internet. -->
+ <string name="wifi_status_sign_in_required">Sign in required</string>
+
<!-- Summary for networks failing to connect due to association rejection status 17, AP full -->
<string name="wifi_ap_unable_to_handle_new_sta">Access point temporarily full</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index c5c1169..78045f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1154,7 +1154,7 @@
@Nullable
@Speed
- private int roundToClosestSpeedEnum(int speed) {
+ private static int roundToClosestSpeedEnum(int speed) {
if (speed < Speed.SLOW) {
return Speed.NONE;
} else if (speed < (Speed.SLOW + Speed.MODERATE) / 2) {
@@ -1170,21 +1170,31 @@
@Nullable
String getSpeedLabel(@Speed int speed) {
+ return getSpeedLabel(mContext, speed);
+ }
+
+ private static String getSpeedLabel(Context context, int speed) {
switch (speed) {
case Speed.VERY_FAST:
- return mContext.getString(R.string.speed_label_very_fast);
+ return context.getString(R.string.speed_label_very_fast);
case Speed.FAST:
- return mContext.getString(R.string.speed_label_fast);
+ return context.getString(R.string.speed_label_fast);
case Speed.MODERATE:
- return mContext.getString(R.string.speed_label_okay);
+ return context.getString(R.string.speed_label_okay);
case Speed.SLOW:
- return mContext.getString(R.string.speed_label_slow);
+ return context.getString(R.string.speed_label_slow);
case Speed.NONE:
default:
return null;
}
}
+ /** Return the speed label for a {@link ScoredNetwork} at the specified {@code rssi} level. */
+ @Nullable
+ public static String getSpeedLabel(Context context, ScoredNetwork scoredNetwork, int rssi) {
+ return getSpeedLabel(context, roundToClosestSpeedEnum(scoredNetwork.calculateBadge(rssi)));
+ }
+
/** Return true if the current RSSI is reachable, and false otherwise. */
public boolean isReachable() {
return mRssi != UNREACHABLE_RSSI;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 6e12e20..4cd23f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -10,28 +10,90 @@
package com.android.settingslib.wifi;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
+import android.content.Context;
import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkKey;
+import android.net.NetworkRequest;
+import android.net.NetworkScoreManager;
+import android.net.ScoredNetwork;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.android.settingslib.R;
import java.util.List;
-public class WifiStatusTracker {
-
+public class WifiStatusTracker extends ConnectivityManager.NetworkCallback {
+ private final Context mContext;
+ private final WifiNetworkScoreCache mWifiNetworkScoreCache;
private final WifiManager mWifiManager;
+ private final NetworkScoreManager mNetworkScoreManager;
+ private final ConnectivityManager mConnectivityManager;
+ private final WifiNetworkScoreCache.CacheListener mCacheListener =
+ new WifiNetworkScoreCache.CacheListener(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void networkCacheUpdated(List<ScoredNetwork> updatedNetworks) {
+ updateStatusLabel();
+ mCallback.run();
+ }
+ };
+ private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
+ .clearCapabilities().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+ private final ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager
+ .NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(
+ Network network, NetworkCapabilities networkCapabilities) {
+ updateStatusLabel();
+ mCallback.run();
+ }
+ };
+ private final Runnable mCallback;
+
+ private WifiInfo mWifiInfo;
public boolean enabled;
public int state;
public boolean connected;
- public boolean connecting;
public String ssid;
public int rssi;
public int level;
+ public String statusLabel;
- public WifiStatusTracker(WifiManager wifiManager) {
+ public WifiStatusTracker(Context context, WifiManager wifiManager,
+ NetworkScoreManager networkScoreManager, ConnectivityManager connectivityManager,
+ Runnable callback) {
+ mContext = context;
mWifiManager = wifiManager;
+ mWifiNetworkScoreCache = new WifiNetworkScoreCache(context);
+ mNetworkScoreManager = networkScoreManager;
+ mConnectivityManager = connectivityManager;
+ mCallback = callback;
+ }
+
+ public void setListening(boolean listening) {
+ if (listening) {
+ mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
+ mWifiNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK);
+ mWifiNetworkScoreCache.registerListener(mCacheListener);
+ mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+ } else {
+ mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI,
+ mWifiNetworkScoreCache);
+ mWifiNetworkScoreCache.unregisterListener();
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
}
public void handleBroadcast(Intent intent) {
@@ -40,34 +102,59 @@
state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN);
enabled = state == WifiManager.WIFI_STATE_ENABLED;
-
-
- enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
} else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- final NetworkInfo networkInfo = (NetworkInfo)
+ final NetworkInfo networkInfo =
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- connecting = networkInfo != null && !networkInfo.isConnected()
- && networkInfo.isConnectedOrConnecting();
connected = networkInfo != null && networkInfo.isConnected();
- // If Connected grab the signal strength and ssid.
+ mWifiInfo = null;
+ ssid = null;
if (connected) {
- WifiInfo info = mWifiManager.getConnectionInfo();
- if (info != null) {
- ssid = getValidSsid(info);
- } else {
- ssid = null;
+ mWifiInfo = mWifiManager.getConnectionInfo();
+ if (mWifiInfo != null) {
+ ssid = getValidSsid(mWifiInfo);
+ updateRssi(mWifiInfo.getRssi());
+ maybeRequestNetworkScore();
}
- } else if (!connected) {
- ssid = null;
}
+ updateStatusLabel();
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
// Default to -200 as its below WifiManager.MIN_RSSI.
- rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
- level = WifiManager.calculateSignalLevel(rssi, 5);
+ updateRssi(intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200));
+ updateStatusLabel();
}
}
+ private void updateRssi(int newRssi) {
+ rssi = newRssi;
+ level = WifiManager.calculateSignalLevel(rssi, WifiManager.RSSI_LEVELS);
+ }
+
+ private void maybeRequestNetworkScore() {
+ NetworkKey networkKey = NetworkKey.createFromWifiInfo(mWifiInfo);
+ if (mWifiNetworkScoreCache.getScoredNetwork(networkKey) == null) {
+ mNetworkScoreManager.requestScores(new NetworkKey[]{ networkKey });
+ }
+ }
+
+ private void updateStatusLabel() {
+ final NetworkCapabilities networkCapabilities
+ = mConnectivityManager.getNetworkCapabilities(mWifiManager.getCurrentNetwork());
+ if (networkCapabilities != null) {
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
+ statusLabel = mContext.getString(R.string.wifi_status_sign_in_required);
+ return;
+ } else if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+ statusLabel = mContext.getString(R.string.wifi_status_no_internet);
+ return;
+ }
+ }
+
+ ScoredNetwork scoredNetwork =
+ mWifiNetworkScoreCache.getScoredNetwork(NetworkKey.createFromWifiInfo(mWifiInfo));
+ statusLabel = scoredNetwork == null
+ ? null : AccessPoint.getSpeedLabel(mContext, scoredNetwork, rssi);
+ }
+
private String getValidSsid(WifiInfo info) {
String ssid = info.getSSID();
if (ssid != null && !WifiSsid.NONE.equals(ssid)) {
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index ea1ad2d1..ef18725 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -22,6 +22,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/qs_footer_height"
android:elevation="4dp"
+ android:background="@android:color/transparent"
android:baselineAligned="false"
android:clickable="false"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 1c9ce18..72ff653 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -56,9 +56,9 @@
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/qs_footer_height"
android:elevation="4dp"
+ android:background="@android:color/transparent"
/>
-
<include layout="@layout/quick_status_bar_expanded_header" />
<include layout="@layout/qs_footer_impl" />
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index b138df0..74c22b0 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -44,7 +44,7 @@
android:maxLines="2"
android:padding="0dp"
android:gravity="center"
- android:ellipsize="end"
+ android:ellipsize="marquee"
android:textAppearance="@style/TextAppearance.QS.TileLabel"
android:textColor="?android:attr/textColorPrimary"/>
@@ -75,6 +75,7 @@
android:layout_alignEnd="@id/label_group"
android:layout_below="@id/label_group"
android:clickable="false"
+ android:ellipsize="marquee"
android:maxLines="1"
android:padding="0dp"
android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
similarity index 63%
rename from packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
rename to packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 8dc4cb4..aa0d4a0 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -15,13 +15,27 @@
-->
<!-- Extends Framelayout -->
-<com.android.systemui.statusbar.DismissView
+<com.android.systemui.statusbar.FooterView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="8dp"
android:visibility="gone">
- <com.android.systemui.statusbar.DismissViewButton
+ <FrameLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+ <com.android.systemui.statusbar.FooterViewButton
+ style="@android:style/Widget.Material.Button.Borderless"
+ android:id="@+id/manage_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:focusable="true"
+ android:text="@string/manage_notifications_text"
+ android:textColor="?attr/wallpaperTextColor"
+ android:textAllCaps="false"/>
+ <com.android.systemui.statusbar.FooterViewButton
style="@android:style/Widget.Material.Button.Borderless"
android:id="@+id/dismiss_text"
android:layout_width="wrap_content"
@@ -32,4 +46,5 @@
android:text="@string/clear_all_notifications_text"
android:textColor="?attr/wallpaperTextColor"
android:textAllCaps="true"/>
-</com.android.systemui.statusbar.DismissView>
+ </FrameLayout>
+</com.android.systemui.statusbar.FooterView>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index e37ca1c..c59492f 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -24,6 +24,8 @@
<dimen name="brightness_mirror_height">96dp</dimen>
+ <!-- Width for the spacer, used between QS tiles. -->
+ <dimen name="qs_quick_tile_space_width">38dp</dimen>
<dimen name="qs_tile_margin_top">2dp</dimen>
<dimen name="qs_header_tooltip_height">24dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ff3f696..91c8724 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -317,6 +317,8 @@
<dimen name="qs_tile_margin_vertical">24dp</dimen>
<dimen name="qs_tile_margin_top">18dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
+ <!-- Width for the spacer, used between QS tiles. -->
+ <dimen name="qs_quick_tile_space_width">0dp</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
<dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d3a55e8..909c18b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -191,16 +191,14 @@
<string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
<!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
<string name="screenshot_saving_title">Saving screenshot\u2026</string>
- <!-- Notification text displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=100] -->
- <string name="screenshot_saving_text">Screenshot is being saved</string>
<!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
<string name="screenshot_saved_title">Screenshot saved</string>
<!-- Notification text displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=100] -->
<string name="screenshot_saved_text">Tap to view your screenshot</string>
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
- <string name="screenshot_failed_title">Couldn\'t capture screenshot</string>
+ <string name="screenshot_failed_title">Couldn\'t save screenshot</string>
<!-- Notification text displayed when we fail to save a screenshot for unknown reasons. [CHAR LIMIT=100] -->
- <string name="screenshot_failed_to_save_unknown_text">Problem encountered while saving screenshot</string>
+ <string name="screenshot_failed_to_save_unknown_text">Try taking screenshot again</string>
<!-- Notification text displayed when we fail to save a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_save_text">Can\'t save screenshot due to limited storage space</string>
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
@@ -1055,6 +1053,9 @@
<!-- The text to clear all notifications. [CHAR LIMIT=60] -->
<string name="clear_all_notifications_text">Clear all</string>
+ <!-- The text for the manage notifications link. [CHAR LIMIT=40] -->
+ <string name="manage_notifications_text">Manage notifications</string>
+
<!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
<string name="dnd_suppressing_shade_text">Do Not disturb is hiding notifications</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index 62b5004..5b4d652 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -137,6 +137,10 @@
mCancelAction = cancelAction;
}
+ public boolean hasDismissActions() {
+ return mDismissAction != null || mCancelAction != null;
+ }
+
public void cancelDismissAction() {
setOnDismissAction(null, null);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d24675c..1bab36b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -68,7 +68,6 @@
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -88,6 +87,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
@@ -400,16 +400,9 @@
// Hack level over 9000: Because the subscription id is not yet valid when we see the
// first update in handleSimStateChange, we need to force refresh all all SIM states
// so the subscription id for them is consistent.
- ArrayList<SubscriptionInfo> changedSubscriptions = new ArrayList<>();
- for (int i = 0; i < subscriptionInfos.size(); i++) {
- SubscriptionInfo info = subscriptionInfos.get(i);
- boolean changed = refreshSimState(info.getSubscriptionId(), info.getSimSlotIndex());
- if (changed) {
- changedSubscriptions.add(info);
- }
- }
- for (int i = 0; i < changedSubscriptions.size(); i++) {
- SimData data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId());
+ List<Integer> changedSubscriptionIds = refreshSimState(subscriptionInfos);
+ for (int i = 0; i < changedSubscriptionIds.size(); i++) {
+ SimData data = mSimDatas.get(changedSubscriptionIds.get(i));
for (int j = 0; j < mCallbacks.size(); j++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
if (cb != null) {
@@ -1846,34 +1839,56 @@
};
/**
- * @return true if and only if the state has changed for the specified {@code slotId}
+ * @return A list of changed subscriptions, maybe empty but never null
*/
- private boolean refreshSimState(int subId, int slotId) {
+ private List<Integer> refreshSimState(final List<SubscriptionInfo> activeSubscriptionInfos) {
// This is awful. It exists because there are two APIs for getting the SIM status
// that don't return the complete set of values and have different types. In Keyguard we
// need IccCardConstants, but TelephonyManager would only give us
// TelephonyManager.SIM_STATE*, so we retrieve it manually.
final TelephonyManager tele = TelephonyManager.from(mContext);
- int simState = tele.getSimState(slotId);
- State state;
- try {
- state = State.intToState(simState);
- } catch(IllegalArgumentException ex) {
- Log.w(TAG, "Unknown sim state: " + simState);
- state = State.UNKNOWN;
+ ArrayList<Integer> changedSubscriptionIds = new ArrayList<>();
+ HashSet<Integer> activeSubIds = new HashSet<>();
+
+ for (SubscriptionInfo info : activeSubscriptionInfos) {
+ int subId = info.getSubscriptionId();
+ int slotId = info.getSimSlotIndex();
+ int simState = tele.getSimState(slotId);
+ State state;
+ try {
+ state = State.intToState(simState);
+ } catch(IllegalArgumentException ex) {
+ Log.w(TAG, "Unknown sim state: " + simState);
+ state = State.UNKNOWN;
+ }
+
+ SimData data = mSimDatas.get(subId);
+ final boolean changed;
+ if (data == null) {
+ data = new SimData(state, slotId, subId);
+ mSimDatas.put(subId, data);
+ changed = true; // no data yet; force update
+ } else {
+ changed = data.simState != state;
+ data.simState = state;
+ }
+ if (changed) {
+ changedSubscriptionIds.add(subId);
+ }
+
+ activeSubIds.add(subId);
}
- SimData data = mSimDatas.get(subId);
- final boolean changed;
- if (data == null) {
- data = new SimData(state, slotId, subId);
- mSimDatas.put(subId, data);
- changed = true; // no data yet; force update
- } else {
- changed = data.simState != state;
- data.simState = state;
+
+ for (SimData data : mSimDatas.values()) {
+ if (!activeSubIds.contains(data.subId) && data.simState != State.ABSENT) {
+ // for the inactive subscriptions, reset state to ABSENT
+ data.simState = State.ABSENT;
+ changedSubscriptionIds.add(data.subId);
+ }
}
- return changed;
+
+ return changedSubscriptionIds;
}
public static boolean isSimPinSecure(IccCardConstants.State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 2270b60..c9c04d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import android.content.Context;
+import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@@ -179,16 +180,56 @@
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private boolean mListening;
+ /** Size of the QS tile (width & height). */
+ private int mTileDimensionSize;
public HeaderTileLayout(Context context) {
super(context);
setClipChildren(false);
setClipToPadding(false);
- setGravity(Gravity.CENTER_VERTICAL);
+
+ mTileDimensionSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.qs_quick_tile_size);
+
+ setGravity(Gravity.CENTER);
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
@Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ setGravity(Gravity.CENTER);
+ LayoutParams staticSpaceLayoutParams = generateSpaceLayoutParams(
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.qs_quick_tile_space_width));
+
+ // Update space params since they fill any open space in portrait orientation and have
+ // a static width in landscape orientation.
+ final int childViewCount = getChildCount();
+ for (int i = 0; i < childViewCount; i++) {
+ View childView = getChildAt(i);
+ if (childView instanceof Space) {
+ childView.setLayoutParams(staticSpaceLayoutParams);
+ }
+ }
+ }
+
+ /**
+ * Returns {@link LayoutParams} based on the given {@code spaceWidth}. If the width is 0,
+ * then we're going to have the space expand to take up as much space as possible. If the
+ * width is non-zero, we want the inter-tile spacers to be fixed.
+ */
+ private LayoutParams generateSpaceLayoutParams(int spaceWidth) {
+ LayoutParams lp = new LayoutParams(spaceWidth, mTileDimensionSize);
+ if (spaceWidth == 0) {
+ lp.weight = 1;
+ }
+ lp.gravity = Gravity.CENTER;
+ return lp;
+ }
+
+ @Override
public void setListening(boolean listening) {
if (mListening == listening) return;
mListening = listening;
@@ -200,25 +241,22 @@
@Override
public void addTile(TileRecord tile) {
if (getChildCount() != 0) {
- // Add a spacer.
- addView(new Space(mContext), getChildCount(), generateSpaceParams());
+ // Add a spacer between tiles. We want static-width spaces if we're in landscape to
+ // keep the tiles close. For portrait, we stick with spaces that fill up any
+ // available space.
+ LayoutParams spaceLayoutParams = generateSpaceLayoutParams(
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.qs_quick_tile_space_width));
+ addView(new Space(mContext), getChildCount(), spaceLayoutParams);
}
- addView(tile.tileView, getChildCount(), generateLayoutParams());
+
+ addView(tile.tileView, getChildCount(), generateTileLayoutParams());
mRecords.add(tile);
tile.tile.setListening(this, mListening);
}
- private LayoutParams generateSpaceParams() {
- int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
- LayoutParams lp = new LayoutParams(0, size);
- lp.weight = 1;
- lp.gravity = Gravity.CENTER;
- return lp;
- }
-
- private LayoutParams generateLayoutParams() {
- int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
- LayoutParams lp = new LayoutParams(size, size);
+ private LayoutParams generateTileLayoutParams() {
+ LayoutParams lp = new LayoutParams(mTileDimensionSize, mTileDimensionSize);
lp.gravity = Gravity.CENTER;
return lp;
}
@@ -237,8 +275,8 @@
}
private int getChildIndex(QSTileView tileView) {
- final int N = getChildCount();
- for (int i = 0; i < N; i++) {
+ final int childViewCount = getChildCount();
+ for (int i = 0; i < childViewCount; i++) {
if (getChildAt(i) == tileView) {
return i;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index c9c678c..a9defc8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -15,7 +15,6 @@
import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -122,14 +121,14 @@
private void setRipple(RippleDrawable tileBackground) {
mRipple = tileBackground;
if (getWidth() != 0) {
- updateRippleSize(getWidth(), getHeight());
+ updateRippleSize();
}
}
- private void updateRippleSize(int width, int height) {
+ private void updateRippleSize() {
// center the touch feedback on the center of the icon, and dial it down a bit
- final int cx = width / 2;
- final int cy = mIconFrame.getMeasuredHeight() / 2;
+ final int cx = mIconFrame.getMeasuredWidth() / 2 + mIconFrame.getLeft();
+ final int cy = mIconFrame.getMeasuredHeight() / 2 + mIconFrame.getTop();
final int rad = (int) (mIcon.getHeight() * .85f);
mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
}
@@ -151,11 +150,8 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- final int w = getMeasuredWidth();
- final int h = getMeasuredHeight();
-
if (mRipple != null) {
- updateRippleSize(w, h);
+ updateRippleSize();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 4774785..3cb4c71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -32,11 +32,12 @@
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
+
import java.util.Objects;
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
-
+ private static final int DEFAULT_MAX_LINES = 2;
private static final boolean DUAL_TARGET_ALLOWED = false;
private View mDivider;
protected TextView mLabel;
@@ -61,7 +62,7 @@
setId(View.generateViewId());
createLabel();
setOrientation(VERTICAL);
- setGravity(Gravity.CENTER);
+ setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
}
TextView getLabel() {
@@ -72,6 +73,7 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
+ FontSizeUtils.updateFontSize(mSecondLine, R.dimen.qs_tile_text_size);
}
@Override
@@ -85,17 +87,33 @@
mLabelContainer.setClipChildren(false);
mLabelContainer.setClipToPadding(false);
mLabel = mLabelContainer.findViewById(R.id.tile_label);
+ mLabel.setSelected(true); // Allow marquee to work.
mPadLock = mLabelContainer.findViewById(R.id.restricted_padlock);
mDivider = mLabelContainer.findViewById(R.id.underline);
mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator);
mExpandSpace = mLabelContainer.findViewById(R.id.expand_space);
mSecondLine = mLabelContainer.findViewById(R.id.app_label);
mSecondLine.setAlpha(.6f);
-
+ mSecondLine.setSelected(true); // Allow marquee to work.
addView(mLabelContainer);
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mLabel.getMaxLines() != DEFAULT_MAX_LINES) {
+ mLabel.setMaxLines(DEFAULT_MAX_LINES);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ // Remeasure view if the secondary label text will be cut off.
+ if (!TextUtils.isEmpty(mSecondLine.getText())
+ && mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
+ mLabel.setSingleLine();
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
protected void handleStateChanged(QSTile.State state) {
super.handleStateChanged(state);
if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 28fdc11..8a1e4da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -150,8 +150,8 @@
cb = mSignalCallback.mInfo;
}
boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
- boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
- boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null);
+ boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.ssid != null);
+ boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.ssid == null);
boolean enabledChanging = state.value != cb.enabled;
if (enabledChanging) {
mDetailAdapter.setItemsVisible(cb.enabled);
@@ -163,7 +163,7 @@
}
state.slash.isSlashed = false;
boolean isTransient = transientEnabling || cb.isTransient;
- state.secondaryLabel = getSecondaryLabel(isTransient);
+ state.secondaryLabel = getSecondaryLabel(isTransient, cb.statusLabel);
state.state = Tile.STATE_ACTIVE;
state.dualTarget = true;
state.value = transientEnabling || cb.enabled;
@@ -181,7 +181,7 @@
state.label = r.getString(R.string.quick_settings_wifi_label);
} else if (wifiConnected) {
state.icon = ResourceIcon.get(cb.wifiSignalIconId);
- state.label = removeDoubleQuotes(cb.enabledDesc);
+ state.label = removeDoubleQuotes(cb.ssid);
} else if (wifiNotConnected) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disconnected);
state.label = r.getString(R.string.quick_settings_wifi_label);
@@ -194,7 +194,7 @@
if (state.value) {
if (wifiConnected) {
minimalContentDescription.append(cb.wifiSignalContentDescription).append(",");
- minimalContentDescription.append(removeDoubleQuotes(cb.enabledDesc));
+ minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
}
}
state.contentDescription = minimalContentDescription.toString();
@@ -203,10 +203,10 @@
state.expandedAccessibilityClassName = Switch.class.getName();
}
- private CharSequence getSecondaryLabel(boolean isTransient) {
+ private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) {
return isTransient
? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
- : null;
+ : statusLabel;
}
@Override
@@ -246,11 +246,12 @@
boolean enabled;
boolean connected;
int wifiSignalIconId;
- String enabledDesc;
+ String ssid;
boolean activityIn;
boolean activityOut;
String wifiSignalContentDescription;
boolean isTransient;
+ public String statusLabel;
@Override
public String toString() {
@@ -258,7 +259,7 @@
.append("enabled=").append(enabled)
.append(",connected=").append(connected)
.append(",wifiSignalIconId=").append(wifiSignalIconId)
- .append(",enabledDesc=").append(enabledDesc)
+ .append(",ssid=").append(ssid)
.append(",activityIn=").append(activityIn)
.append(",activityOut=").append(activityOut)
.append(",wifiSignalContentDescription=").append(wifiSignalContentDescription)
@@ -272,16 +273,18 @@
@Override
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
- boolean activityIn, boolean activityOut, String description, boolean isTransient) {
+ boolean activityIn, boolean activityOut, String description, boolean isTransient,
+ String statusLabel) {
if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled);
mInfo.enabled = enabled;
mInfo.connected = qsIcon.visible;
mInfo.wifiSignalIconId = qsIcon.icon;
- mInfo.enabledDesc = description;
+ mInfo.ssid = description;
mInfo.activityIn = activityIn;
mInfo.activityOut = activityOut;
mInfo.wifiSignalContentDescription = qsIcon.contentDescription;
mInfo.isTransient = isTransient;
+ mInfo.statusLabel = statusLabel;
if (isShowingDetail()) {
mDetailAdapter.updateItems();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 068fd3f..227f2d2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -189,7 +189,6 @@
mPublicNotificationBuilder =
new Notification.Builder(context, NotificationChannels.SCREENSHOTS_HEADSUP)
.setContentTitle(r.getString(R.string.screenshot_saving_title))
- .setContentText(r.getString(R.string.screenshot_saving_text))
.setSmallIcon(R.drawable.stat_notify_image)
.setCategory(Notification.CATEGORY_PROGRESS)
.setWhen(now)
@@ -203,7 +202,6 @@
.setTicker(r.getString(R.string.screenshot_saving_ticker)
+ (mTickerAddSpace ? " " : ""))
.setContentTitle(r.getString(R.string.screenshot_saving_title))
- .setContentText(r.getString(R.string.screenshot_saving_text))
.setSmallIcon(R.drawable.stat_notify_image)
.setWhen(now)
.setShowWhen(true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 3698c3a0..4388b41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -48,6 +48,11 @@
return findViewById(R.id.no_notifications);
}
+ @Override
+ protected View findSecondaryView() {
+ return null;
+ }
+
public void setTextColor(@ColorInt int color) {
mEmptyText.setTextColor(color);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java b/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
similarity index 68%
rename from packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
index d7c6443..0f4b621 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
@@ -26,11 +26,12 @@
import com.android.systemui.statusbar.stack.ExpandableViewState;
import com.android.systemui.statusbar.stack.StackScrollState;
-public class DismissView extends StackScrollerDecorView {
+public class FooterView extends StackScrollerDecorView {
private final int mClearAllTopPadding;
- private DismissViewButton mDismissButton;
+ private FooterViewButton mDismissButton;
+ private FooterViewButton mManageButton;
- public DismissView(Context context, AttributeSet attrs) {
+ public FooterView(Context context, AttributeSet attrs) {
super(context, attrs);
mClearAllTopPadding = context.getResources().getDimensionPixelSize(
R.dimen.clear_all_padding_top);
@@ -38,21 +39,31 @@
@Override
protected View findContentView() {
+ return findViewById(R.id.content);
+ }
+
+ protected View findSecondaryView() {
return findViewById(R.id.dismiss_text);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mDismissButton = (DismissViewButton) findContentView();
+ mDismissButton = (FooterViewButton) findSecondaryView();
+ mManageButton = findViewById(R.id.manage_text);
}
public void setTextColor(@ColorInt int color) {
+ mManageButton.setTextColor(color);
mDismissButton.setTextColor(color);
}
- public void setOnButtonClickListener(OnClickListener listener) {
- mContent.setOnClickListener(listener);
+ public void setManageButtonClickListener(OnClickListener listener) {
+ mManageButton.setOnClickListener(listener);
+ }
+
+ public void setDismissButtonClickListener(OnClickListener listener) {
+ mDismissButton.setOnClickListener(listener);
}
public boolean isOnEmptySpace(float touchX, float touchY) {
@@ -68,25 +79,26 @@
mDismissButton.setText(R.string.clear_all_notifications_text);
mDismissButton.setContentDescription(
mContext.getString(R.string.accessibility_clear_all));
+ mManageButton.setText(R.string.manage_notifications_text);
}
public boolean isButtonVisible() {
- return mDismissButton.getAlpha() != 0.0f;
+ return mManageButton.getAlpha() != 0.0f;
}
@Override
public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
- return new DismissViewState();
+ return new FooterViewState();
}
- public class DismissViewState extends ExpandableViewState {
+ public class FooterViewState extends ExpandableViewState {
@Override
public void applyToView(View view) {
super.applyToView(view);
- if (view instanceof DismissView) {
- DismissView dismissView = (DismissView) view;
+ if (view instanceof FooterView) {
+ FooterView footerView = (FooterView) view;
boolean visible = this.clipTopAmount < mClearAllTopPadding;
- dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
+ footerView.performVisibilityAnimation(visible && !footerView.willBeGone());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
index b608d67..16ca0f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
@@ -23,21 +23,21 @@
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-public class DismissViewButton extends AlphaOptimizedButton {
+public class FooterViewButton extends AlphaOptimizedButton {
- public DismissViewButton(Context context) {
+ public FooterViewButton(Context context) {
this(context, null);
}
- public DismissViewButton(Context context, AttributeSet attrs) {
+ public FooterViewButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public DismissViewButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ public FooterViewButton(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public DismissViewButton(Context context, AttributeSet attrs, int defStyleAttr,
+ public FooterViewButton(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 3cf7741..e7b768f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -55,7 +55,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
// Intimately tied to the design of res/layout/signal_cluster_view.xml
public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback,
@@ -277,7 +276,8 @@
@Override
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
- boolean activityIn, boolean activityOut, String description, boolean isTransient) {
+ boolean activityIn, boolean activityOut, String description, boolean isTransient,
+ String secondaryLabel) {
mWifiVisible = statusIcon.visible && !mBlockWifi;
mWifiStrengthId = statusIcon.icon;
mWifiDescription = statusIcon.contentDescription;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index badc40d..14a6c42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -31,8 +31,11 @@
public abstract class StackScrollerDecorView extends ExpandableView {
protected View mContent;
+ protected View mSecondaryView;
private boolean mIsVisible;
+ private boolean mIsSecondaryVisible;
private boolean mAnimating;
+ private int mDuration = 260;
public StackScrollerDecorView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -42,6 +45,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mContent = findContentView();
+ mSecondaryView = findSecondaryView();
setInvisible();
}
@@ -57,17 +61,37 @@
}
public void performVisibilityAnimation(boolean nowVisible) {
- animateText(nowVisible, null /* onFinishedRunnable */);
+ animateText(mContent, nowVisible, null /* onFinishedRunnable */);
+ mIsVisible = nowVisible;
}
public void performVisibilityAnimation(boolean nowVisible, Runnable onFinishedRunnable) {
- animateText(nowVisible, onFinishedRunnable);
+ animateText(mContent, nowVisible, onFinishedRunnable);
+ mIsVisible = nowVisible;
+ }
+
+ public void performSecondaryVisibilityAnimation(boolean nowVisible) {
+ performSecondaryVisibilityAnimation(nowVisible, null /* onFinishedRunnable */);
+ }
+
+ public void performSecondaryVisibilityAnimation(boolean nowVisible,
+ Runnable onFinishedRunnable) {
+ animateText(mSecondaryView, nowVisible, onFinishedRunnable);
+ mIsSecondaryVisible = nowVisible;
+ }
+
+ public boolean isSecondaryVisible() {
+ return mSecondaryView != null && (mIsSecondaryVisible || mAnimating);
}
public boolean isVisible() {
return mIsVisible || mAnimating;
}
+ void setDuration(int duration) {
+ mDuration = duration;
+ }
+
/**
* Animate the text to a new visibility.
*
@@ -75,7 +99,10 @@
* @param onFinishedRunnable A runnable which should be run when the animation is
* finished.
*/
- private void animateText(boolean nowVisible, final Runnable onFinishedRunnable) {
+ private void animateText(View view, boolean nowVisible, final Runnable onFinishedRunnable) {
+ if (view == null) {
+ return;
+ }
if (nowVisible != mIsVisible) {
// Animate text
float endValue = nowVisible ? 1.0f : 0.0f;
@@ -86,10 +113,10 @@
interpolator = Interpolators.ALPHA_OUT;
}
mAnimating = true;
- mContent.animate()
+ view.animate()
.alpha(endValue)
.setInterpolator(interpolator)
- .setDuration(260)
+ .setDuration(mDuration)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -99,7 +126,6 @@
}
}
});
- mIsVisible = nowVisible;
} else {
if (onFinishedRunnable != null) {
onFinishedRunnable.run();
@@ -109,7 +135,11 @@
public void setInvisible() {
mContent.setAlpha(0.0f);
+ if (mSecondaryView != null) {
+ mSecondaryView.setAlpha(0.0f);
+ }
mIsVisible = false;
+ mIsSecondaryVisible = false;
}
@Override
@@ -134,7 +164,15 @@
public void cancelAnimation() {
mContent.animate().cancel();
+ if (mSecondaryView != null) {
+ mSecondaryView.animate().cancel();
+ }
}
protected abstract View findContentView();
+
+ /**
+ * Returns a view that might not always appear while the main content view is still visible.
+ */
+ protected abstract View findSecondaryView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index d609ae7..fcd4e8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -330,6 +330,10 @@
}
}
+ public boolean willDismissWithAction() {
+ return mKeyguardView != null && mKeyguardView.hasDismissActions();
+ }
+
protected void ensureView() {
// Removal of the view might be deferred to reduce unlock latency,
// in this case we need to force the removal, otherwise we'll
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 1fbaa47..b4cb088 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -838,10 +838,17 @@
buttonBounds.setEmpty();
return;
}
+ // Temporarily reset the translation back to origin to get the position in window
+ final float posX = view.getTranslationX();
+ final float posY = view.getTranslationY();
+ view.setTranslationX(0);
+ view.setTranslationY(0);
view.getLocationInWindow(mTmpPosition);
buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
mTmpPosition[0] + view.getMeasuredWidth(),
mTmpPosition[1] + view.getMeasuredHeight());
+ view.setTranslationX(posX);
+ view.setTranslationY(posY);
}
private void updateRotatedViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 64e205d..cccda90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2165,18 +2165,18 @@
@Override
protected boolean fullyExpandedClearAllVisible() {
- return mNotificationStackScroller.isDismissViewNotGone()
+ return mNotificationStackScroller.isFooterViewNotGone()
&& mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
}
@Override
protected boolean isClearAllVisible() {
- return mNotificationStackScroller.isDismissViewVisible();
+ return mNotificationStackScroller.isFooterViewVisible();
}
@Override
protected int getClearAllHeight() {
- return mNotificationStackScroller.getDismissViewHeight();
+ return mNotificationStackScroller.getFooterViewHeight();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index cfc0cc6..7a6e98d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -148,7 +148,7 @@
private float mNotificationDensity;
// Scrim blanking callbacks
- private Choreographer.FrameCallback mPendingFrameCallback;
+ private Runnable mPendingFrameCallback;
private Runnable mBlankingTransitionRunnable;
private final WakeLock mWakeLock;
@@ -240,7 +240,7 @@
// Cancel blanking transitions that were pending before we requested a new state
if (mPendingFrameCallback != null) {
- Choreographer.getInstance().removeFrameCallback(mPendingFrameCallback);
+ mScrimBehind.removeCallbacks(mPendingFrameCallback);
mPendingFrameCallback = null;
}
if (getHandler().hasCallbacks(mBlankingTransitionRunnable)) {
@@ -278,7 +278,7 @@
// with too many things at this case, in order to not skip the initial frames.
mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
- } else if (!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD
+ } else if ((!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD)
|| (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
// Scheduling a frame isn't enough when:
// • Leaving doze and we need to modify scrim color immediately
@@ -727,7 +727,7 @@
// Notify callback that the screen is completely black and we're
// ready to change the display power mode
- mPendingFrameCallback = frameTimeNanos -> {
+ mPendingFrameCallback = () -> {
if (mCallback != null) {
mCallback.onDisplayBlanked();
mScreenBlankingCallbackCalled = true;
@@ -743,7 +743,7 @@
// 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;
+ final int delay = mScreenOn ? 32 : 500;
if (DEBUG) {
Log.d(TAG, "Fading out scrims with delay: " + delay);
}
@@ -752,9 +752,15 @@
doOnTheNextFrame(mPendingFrameCallback);
}
+ /**
+ * Executes a callback after the frame has hit the display.
+ * @param callback What to run.
+ */
@VisibleForTesting
- protected void doOnTheNextFrame(Choreographer.FrameCallback callback) {
- Choreographer.getInstance().postFrameCallback(callback);
+ protected void doOnTheNextFrame(Runnable callback) {
+ // Just calling View#postOnAnimation isn't enough because the frame might not have reached
+ // the display yet. A timeout is the safest solution.
+ mScrimBehind.postOnAnimationDelayed(callback, 32 /* delayMillis */);
}
@VisibleForTesting
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 8cd6295..ddb67df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -130,7 +130,6 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.widget.LockPatternUtils;
@@ -185,12 +184,11 @@
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.FooterView;
import com.android.systemui.statusbar.GestureRecorder;
-import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
@@ -237,7 +235,6 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.util.NotificationChannels;
import com.android.systemui.volume.VolumeComponent;
import java.io.FileDescriptor;
@@ -577,7 +574,7 @@
private final LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
protected NotificationIconAreaController mNotificationIconAreaController;
private boolean mReinflateNotificationsOnUserSwitched;
- private boolean mClearAllEnabled;
+ protected boolean mClearAllEnabled;
@Nullable private View mAmbientIndicationContainer;
private SysuiColorExtractor mColorExtractor;
private ScreenLifecycle mScreenLifecycle;
@@ -868,7 +865,7 @@
mVisualStabilityManager.setVisibilityLocationProvider(mStackScroller);
inflateEmptyShadeView();
- inflateDismissView();
+ inflateFooterView();
mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
@@ -1133,8 +1130,8 @@
protected void reevaluateStyles() {
inflateSignalClusters();
- inflateDismissView();
- updateClearAll();
+ inflateFooterView();
+ updateFooter();
inflateEmptyShadeView();
updateEmptyShadeView();
}
@@ -1186,18 +1183,21 @@
mStackScroller.setEmptyShadeView(mEmptyShadeView);
}
- private void inflateDismissView() {
- if (!mClearAllEnabled || mStackScroller == null) {
+ private void inflateFooterView() {
+ if (mStackScroller == null) {
return;
}
- mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
- mDismissView.setOnButtonClickListener(v -> {
+ mFooterView = (FooterView) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_notification_footer, mStackScroller, false);
+ mFooterView.setDismissButtonClickListener(v -> {
mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
clearAllNotifications();
});
- mStackScroller.setDismissView(mDismissView);
+ mFooterView.setManageButtonClickListener(v -> {
+ manageNotifications();
+ });
+ mStackScroller.setFooterView(mFooterView);
}
protected void createUserSwitcher() {
@@ -1211,6 +1211,12 @@
R.layout.super_status_bar, null);
}
+ public void manageNotifications() {
+ Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent, true, true);
+ }
+
public void clearAllNotifications() {
// animate-swipe all dismissable notifications, then animate the shade closed
@@ -1393,7 +1399,7 @@
mViewHierarchyManager.updateNotificationViews();
updateSpeedBumpIndex();
- updateClearAll();
+ updateFooter();
updateEmptyShadeView();
updateQsExpansionEnabled();
@@ -1457,13 +1463,14 @@
mQSPanel.clickTile(tile);
}
- private void updateClearAll() {
- if (!mClearAllEnabled) {
- return;
- }
- boolean showDismissView = mState != StatusBarState.KEYGUARD
+ @VisibleForTesting
+ protected void updateFooter() {
+ boolean showFooterView = mState != StatusBarState.KEYGUARD
+ && mEntryManager.getNotificationData().getActiveNotifications().size() != 0;
+ boolean showDismissView = mClearAllEnabled && mState != StatusBarState.KEYGUARD
&& hasActiveClearableNotifications();
- mStackScroller.updateDismissView(showDismissView);
+
+ mStackScroller.updateFooterView(showFooterView, showDismissView);
}
/**
@@ -4654,9 +4661,12 @@
if (mBouncerShowing) {
// Bouncer needs the front scrim when it's on top of an activity,
- // tapping on a notification or editing QS.
- mScrimController.transitionTo(mIsOccluded || mNotificationPanel.needsScrimming() ?
- ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER);
+ // tapping on a notification, editing QS or being dismissed by
+ // FLAG_DISMISS_KEYGUARD_ACTIVITY.
+ ScrimState state = mIsOccluded || mNotificationPanel.needsScrimming()
+ || mStatusBarKeyguardViewManager.willDismissWithAction() ?
+ ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
+ mScrimController.transitionTo(state);
} else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
} else if (mBrightnessMirrorVisible) {
@@ -4935,7 +4945,7 @@
protected RecentsComponent mRecents;
protected NotificationShelf mNotificationShelf;
- protected DismissView mDismissView;
+ protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
protected AssistManager mAssistManager;
@@ -5408,8 +5418,8 @@
// incremented in the following "changeViewPosition" calls so that its value is correct for
// subsequent calls.
int offsetFromEnd = 1;
- if (mDismissView != null) {
- mStackScroller.changeViewPosition(mDismissView,
+ if (mFooterView != null) {
+ mStackScroller.changeViewPosition(mFooterView,
mStackScroller.getChildCount() - offsetFromEnd++);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 56a7b1b..1a64b00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -140,11 +140,14 @@
}
private void onPanelExpansionChanged(float expansion, boolean tracking) {
- // We don't want to translate the bounce when the keyguard is occluded, because we're in
- // a FLAG_SHOW_WHEN_LOCKED activity and need to conserve the original animation.
- // We also don't want to show the bouncer when the user quickly taps on the display.
+ // We don't want to translate the bounce when:
+ // • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to
+ // conserve the original animation.
+ // • The user quickly taps on the display and we show "swipe up to unlock."
+ // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
final boolean noLongerTracking = mLastTracking != tracking && !tracking;
- if (mOccluded || mNotificationPanelView.isUnlockHintRunning()) {
+ if (mOccluded || mNotificationPanelView.isUnlockHintRunning()
+ || mBouncer.willDismissWithAction()) {
mBouncer.setExpansion(0);
} else if (mShowing && mStatusBar.isKeyguardCurrentlySecure() && !mDozing) {
mBouncer.setExpansion(expansion);
@@ -696,6 +699,10 @@
}
}
+ public boolean willDismissWithAction() {
+ return mBouncer.willDismissWithAction();
+ }
+
private static class DismissWithActionRequest {
final OnDismissAction dismissAction;
final Runnable cancelAction;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 5159e8d..b76d536 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -109,13 +109,13 @@
@Override
public void setWifiIndicators(final boolean enabled, final IconState statusIcon,
final IconState qsIcon, final boolean activityIn, final boolean activityOut,
- final String description, boolean isTransient) {
+ final String description, boolean isTransient, String secondaryLabel) {
post(new Runnable() {
@Override
public void run() {
for (SignalCallback callback : mSignalCallbacks) {
callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
- description, isTransient);
+ description, isTransient, secondaryLabel);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 76e3ad7..51fef7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -47,7 +47,8 @@
public interface SignalCallback {
default void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
- boolean activityIn, boolean activityOut, String description, boolean isTransient) {}
+ boolean activityIn, boolean activityOut, String description, boolean isTransient,
+ String statusLabel) {}
default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 0f65421..e5e576d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -17,12 +17,15 @@
import android.content.Context;
import android.content.Intent;
+import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
+import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -37,7 +40,6 @@
public class WifiSignalController extends
SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
- private final WifiManager mWifiManager;
private final AsyncChannel mWifiChannel;
private final boolean mHasMobileData;
private final WifiStatusTracker mWifiTracker;
@@ -47,12 +49,17 @@
WifiManager wifiManager) {
super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
callbackHandler, networkController);
- mWifiManager = wifiManager;
- mWifiTracker = new WifiStatusTracker(mWifiManager);
+ NetworkScoreManager networkScoreManager =
+ context.getSystemService(NetworkScoreManager.class);
+ ConnectivityManager connectivityManager =
+ context.getSystemService(ConnectivityManager.class);
+ mWifiTracker = new WifiStatusTracker(mContext, wifiManager, networkScoreManager,
+ connectivityManager, this::handleStatusUpdated);
+ mWifiTracker.setListening(true);
mHasMobileData = hasMobileData;
Handler handler = new WifiHandler(Looper.getMainLooper());
mWifiChannel = new AsyncChannel();
- Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
+ Messenger wifiMessenger = wifiManager.getWifiServiceMessenger();
if (wifiMessenger != null) {
mWifiChannel.connect(context, handler, wifiMessenger);
}
@@ -68,7 +75,6 @@
WifiIcons.QS_WIFI_NO_NETWORK,
AccessibilityContentDescriptions.WIFI_NO_CONNECTION
);
-
}
@Override
@@ -87,13 +93,12 @@
if (mCurrentState.inetCondition == 0) {
contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
}
-
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(),
contentDescription);
callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
- wifiDesc, mCurrentState.isTransient);
+ wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
}
/**
@@ -106,6 +111,12 @@
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
mCurrentState.level = mWifiTracker.level;
+ mCurrentState.statusLabel = mWifiTracker.statusLabel;
+ notifyListenersIfNecessary();
+ }
+
+ private void handleStatusUpdated() {
+ mCurrentState.statusLabel = mWifiTracker.statusLabel;
notifyListenersIfNecessary();
}
@@ -150,6 +161,7 @@
static class WifiState extends SignalController.State {
String ssid;
boolean isTransient;
+ String statusLabel;
@Override
public void copyFrom(State s) {
@@ -157,20 +169,26 @@
WifiState state = (WifiState) s;
ssid = state.ssid;
isTransient = state.isTransient;
+ statusLabel = state.statusLabel;
}
@Override
protected void toString(StringBuilder builder) {
super.toString(builder);
- builder.append(',').append("ssid=").append(ssid);
- builder.append(',').append("isTransient=").append(isTransient);
+ builder.append(",ssid=").append(ssid)
+ .append(",isTransient=").append(isTransient)
+ .append(",statusLabel=").append(statusLabel);
}
@Override
public boolean equals(Object o) {
- return super.equals(o)
- && Objects.equals(((WifiState) o).ssid, ssid)
- && (((WifiState) o).isTransient == isTransient);
+ if (!super.equals(o)) {
+ return false;
+ }
+ WifiState other = (WifiState) o;
+ return Objects.equals(other.ssid, ssid)
+ && other.isTransient == isTransient
+ && TextUtils.equals(other.statusLabel, statusLabel);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index dc94203..8f6fa49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -16,7 +16,8 @@
package com.android.systemui.statusbar.stack;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
+ .ExpandAnimationParameters;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -43,6 +44,7 @@
import android.service.notification.StatusBarNotification;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
+import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
@@ -77,10 +79,10 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.FooterView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationGuts;
import com.android.systemui.statusbar.NotificationListContainer;
@@ -99,8 +101,6 @@
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
-import android.support.v4.graphics.ColorUtils;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -109,7 +109,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.function.BiConsumer;
-import java.util.function.Consumer;
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
@@ -226,7 +225,7 @@
private boolean mExpandingNotification;
private boolean mExpandedInThisMotion;
protected boolean mScrollingEnabled;
- protected DismissView mDismissView;
+ protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
private boolean mDismissAllInProgress;
private boolean mFadeNotificationsOnDismiss;
@@ -3838,13 +3837,13 @@
Context context = new ContextThemeWrapper(mContext,
lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
final int textColor = Utils.getColorAttr(context, R.attr.wallpaperTextColor);
- mDismissView.setTextColor(textColor);
+ mFooterView.setTextColor(textColor);
mEmptyShadeView.setTextColor(textColor);
}
public void goToFullShade(long delay) {
- if (mDismissView != null) {
- mDismissView.setInvisible();
+ if (mFooterView != null) {
+ mFooterView.setInvisible();
}
mEmptyShadeView.setInvisible();
mGoToFullShadeNeedsAnimation = true;
@@ -3967,14 +3966,14 @@
return -1;
}
- public void setDismissView(@NonNull DismissView dismissView) {
+ public void setFooterView(@NonNull FooterView footerView) {
int index = -1;
- if (mDismissView != null) {
- index = indexOfChild(mDismissView);
- removeView(mDismissView);
+ if (mFooterView != null) {
+ index = indexOfChild(mFooterView);
+ removeView(mFooterView);
}
- mDismissView = dismissView;
- addView(mDismissView, index);
+ mFooterView = footerView;
+ addView(mFooterView, index);
}
public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
@@ -3992,77 +3991,64 @@
int newVisibility = visible ? VISIBLE : GONE;
if (oldVisibility != newVisibility) {
if (newVisibility != GONE) {
- if (mEmptyShadeView.willBeGone()) {
- mEmptyShadeView.cancelAnimation();
- } else {
- mEmptyShadeView.setInvisible();
- }
if (mStatusBar.areNotificationsHidden()) {
mEmptyShadeView.setText(R.string.dnd_suppressing_shade_text);
} else {
mEmptyShadeView.setText(R.string.empty_shade_text);
}
- mEmptyShadeView.setVisibility(newVisibility);
- mEmptyShadeView.setWillBeGone(false);
- updateContentHeight();
- notifyHeightChangeListener(mEmptyShadeView);
+ showFooterView(mEmptyShadeView);
} else {
- Runnable onFinishedRunnable = new Runnable() {
- @Override
- public void run() {
- mEmptyShadeView.setVisibility(GONE);
- mEmptyShadeView.setWillBeGone(false);
- updateContentHeight();
- notifyHeightChangeListener(mEmptyShadeView);
- }
- };
- if (mAnimationsEnabled && mIsExpanded) {
- mEmptyShadeView.setWillBeGone(true);
- mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable);
- } else {
- mEmptyShadeView.setInvisible();
- onFinishedRunnable.run();
- }
+ hideFooterView(mEmptyShadeView, true);
}
}
}
- public void updateDismissView(boolean visible) {
- if (mDismissView == null) {
+ public void updateFooterView(boolean visible, boolean showDismissView) {
+ if (mFooterView == null) {
return;
}
-
- int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
+ int oldVisibility = mFooterView.willBeGone() ? GONE : mFooterView.getVisibility();
int newVisibility = visible ? VISIBLE : GONE;
if (oldVisibility != newVisibility) {
if (newVisibility != GONE) {
- if (mDismissView.willBeGone()) {
- mDismissView.cancelAnimation();
- } else {
- mDismissView.setInvisible();
- }
- mDismissView.setVisibility(newVisibility);
- mDismissView.setWillBeGone(false);
- updateContentHeight();
- notifyHeightChangeListener(mDismissView);
+ showFooterView(mFooterView);
} else {
- Runnable dimissHideFinishRunnable = new Runnable() {
- @Override
- public void run() {
- mDismissView.setVisibility(GONE);
- mDismissView.setWillBeGone(false);
- updateContentHeight();
- notifyHeightChangeListener(mDismissView);
- }
- };
- if (mDismissView.isButtonVisible() && mIsExpanded && mAnimationsEnabled) {
- mDismissView.setWillBeGone(true);
- mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
- } else {
- dimissHideFinishRunnable.run();
- }
+ hideFooterView(mFooterView, mFooterView.isButtonVisible());
}
}
+ if (mFooterView.isSecondaryVisible() != showDismissView) {
+ mFooterView.performSecondaryVisibilityAnimation(showDismissView);
+ }
+ }
+
+ private void showFooterView(StackScrollerDecorView footerView) {
+ if (footerView.willBeGone()) {
+ footerView.cancelAnimation();
+ } else {
+ footerView.setInvisible();
+ }
+ footerView.setVisibility(VISIBLE);
+ footerView.setWillBeGone(false);
+ updateContentHeight();
+ notifyHeightChangeListener(footerView);
+ }
+
+ private void hideFooterView(StackScrollerDecorView footerView, boolean isButtonVisible) {
+ Runnable onHideFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ footerView.setVisibility(GONE);
+ footerView.setWillBeGone(false);
+ updateContentHeight();
+ notifyHeightChangeListener(footerView);
+ }
+ };
+ if (isButtonVisible && mIsExpanded && mAnimationsEnabled) {
+ footerView.setWillBeGone(true);
+ footerView.performVisibilityAnimation(false, onHideFinishRunnable);
+ } else {
+ onHideFinishRunnable.run();
+ }
}
public void setDismissAllInProgress(boolean dismissAllInProgress) {
@@ -4088,18 +4074,18 @@
}
}
- public boolean isDismissViewNotGone() {
- return mDismissView != null
- && mDismissView.getVisibility() != View.GONE
- && !mDismissView.willBeGone();
+ public boolean isFooterViewNotGone() {
+ return mFooterView != null
+ && mFooterView.getVisibility() != View.GONE
+ && !mFooterView.willBeGone();
}
- public boolean isDismissViewVisible() {
- return mDismissView != null && mDismissView.isVisible();
+ public boolean isFooterViewVisible() {
+ return mFooterView != null && mFooterView.isVisible();
}
- public int getDismissViewHeight() {
- return mDismissView == null ? 0 : mDismissView.getHeight() + mPaddingBetweenElements;
+ public int getFooterViewHeight() {
+ return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
}
public int getEmptyShadeViewHeight() {
@@ -4155,8 +4141,8 @@
}
boolean belowChild = touchY > childTop + child.getActualHeight()
- child.getClipBottomAmount();
- if (child == mDismissView) {
- if(!belowChild && !mDismissView.isOnEmptySpace(touchX - mDismissView.getX(),
+ if (child == mFooterView) {
+ if(!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
touchY - childTop)) {
// We clicked on the dismiss button
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 7c8e0fc..a8d2d98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -22,10 +22,10 @@
import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.R;
-import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.FooterView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -390,7 +390,7 @@
int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
int childHeight = getMaxAllowedChildHeight(child);
childViewState.yTranslation = currentYPosition;
- boolean isDismissView = child instanceof DismissView;
+ boolean isFooterView = child instanceof FooterView;
boolean isEmptyShadeView = child instanceof EmptyShadeView;
childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
@@ -404,7 +404,7 @@
float end = childViewState.yTranslation + childViewState.height + inset;
childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
}
- if (isDismissView) {
+ if (isFooterView) {
childViewState.yTranslation = Math.min(childViewState.yTranslation,
ambientState.getInnerHeight() - childHeight);
} else if (isEmptyShadeView) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java
new file mode 100644
index 0000000..3f85c9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.keyguard;
+
+import static org.mockito.Mockito.mock;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class KeyguardHostViewTest extends SysuiTestCase {
+
+ private KeyguardHostView mKeyguardHostView;
+
+ @Before
+ public void setup() {
+ mKeyguardHostView = new KeyguardHostView(getContext());
+ }
+
+ @Test
+ public void testHasDismissActions() {
+ Assert.assertFalse("Action not set yet", mKeyguardHostView.hasDismissActions());
+ mKeyguardHostView.setOnDismissAction(mock(KeyguardHostView.OnDismissAction.class),
+ null /* cancelAction */);
+ Assert.assertTrue("Action should exist", mKeyguardHostView.hasDismissActions());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
new file mode 100644
index 0000000..96b0255
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.statusbar;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.os.UserHandle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FooterViewTest extends SysuiTestCase {
+
+ FooterView mView;
+
+ @Before
+ public void setUp() {
+ mView = (FooterView) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_notification_footer, null, false);
+ mView.setDuration(0);
+ }
+
+ @Test
+ public void testViewsNotNull() {
+ assertNotNull(mView.findContentView());
+ assertNotNull(mView.findSecondaryView());
+ }
+
+ @Test
+ public void setDismissOnClick() {
+ mView.setDismissButtonClickListener(mock(View.OnClickListener.class));
+ assertTrue(mView.findSecondaryView().hasOnClickListeners());
+ }
+
+ @Test
+ public void setManageOnClick() {
+ mView.setManageButtonClickListener(mock(View.OnClickListener.class));
+ assertTrue(mView.findViewById(R.id.manage_text).hasOnClickListeners());
+ }
+
+ @Test
+ public void testPerformVisibilityAnimation() {
+ mView.setInvisible();
+ assertFalse(mView.isVisible());
+
+ Runnable test = new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(1.0f, mView.findContentView().getAlpha());
+ assertEquals(0.0f, mView.findSecondaryView().getAlpha());
+ assertTrue(mView.isVisible());
+ }
+ };
+ mView.performVisibilityAnimation(true, test);
+ }
+
+ @Test
+ public void testPerformSecondaryVisibilityAnimation() {
+ mView.setInvisible();
+ assertFalse(mView.isSecondaryVisible());
+
+ Runnable test = new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(0.0f, mView.findContentView().getAlpha());
+ assertEquals(1.0f, mView.findSecondaryView().getAlpha());
+ assertTrue(mView.isSecondaryVisible());
+ }
+ };
+ mView.performSecondaryVisibilityAnimation(true, test);
+ }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index a37947d..67453d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -22,6 +22,7 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -307,4 +308,12 @@
mBouncer.isSecure(), mode != KeyguardSecurityModel.SecurityMode.None);
}
}
+
+ @Test
+ public void testWillDismissWithAction() {
+ mBouncer.ensureView();
+ Assert.assertFalse("Action not set yet", mBouncer.willDismissWithAction());
+ when(mKeyguardHostView.hasDismissActions()).thenReturn(true);
+ Assert.assertTrue("Action should exist", mBouncer.willDismissWithAction());
+ }
}
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 45845fc..7743c6b 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
@@ -39,10 +39,8 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.Choreographer;
import android.view.View;
-import com.android.internal.util.Preconditions;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.ScrimView;
@@ -557,8 +555,8 @@
* @param callback What to execute.
*/
@Override
- protected void doOnTheNextFrame(Choreographer.FrameCallback callback) {
- callback.doFrame(0);
+ protected void doOnTheNextFrame(Runnable callback) {
+ callback.run();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index f13fa4e..f81ffd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -18,7 +18,9 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.fail;
@@ -55,6 +57,8 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.View;
import android.view.ViewGroup.LayoutParams;
import com.android.internal.logging.MetricsLogger;
@@ -65,13 +69,15 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.AppOpsListener;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.FooterView;
+import com.android.systemui.statusbar.FooterViewButton;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
@@ -88,7 +94,6 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
@@ -97,12 +102,14 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.function.Predicate;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -558,6 +565,68 @@
verify(mScrimController).setKeyguardOccluded(eq(false));
}
+ @Test
+ public void testInflateFooterView() {
+ mStatusBar.reevaluateStyles();
+ ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
+ verify(mStackScroller).setFooterView(captor.capture());
+
+ assertNotNull(captor.getValue().findViewById(R.id.manage_text).hasOnClickListeners());
+ assertNotNull(captor.getValue().findViewById(R.id.dismiss_text).hasOnClickListeners());
+ }
+
+ @Test
+ public void testUpdateFooter_noNotifications() {
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ assertEquals(0, mEntryManager.getNotificationData().getActiveNotifications().size());
+
+ mStatusBar.updateFooter();
+ verify(mStackScroller).updateFooterView(false, false);
+ }
+
+ @Test
+ public void testUpdateFooter_oneClearableNotification() {
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ ArrayList<Entry> entries = new ArrayList<>();
+ entries.add(mock(Entry.class));
+ when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ when(row.canViewBeDismissed()).thenReturn(true);
+ when(mStackScroller.getChildCount()).thenReturn(1);
+ when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
+
+ mStatusBar.updateFooter();
+ verify(mStackScroller).updateFooterView(true, true);
+ }
+
+ @Test
+ public void testUpdateFooter_oneNonClearableNotification() {
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ ArrayList<Entry> entries = new ArrayList<>();
+ entries.add(mock(Entry.class));
+ when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+
+ mStatusBar.updateFooter();
+ verify(mStackScroller).updateFooterView(true, false);
+ }
+
+ @Test
+ public void testUpdateFooter_atEnd() {
+ // add footer
+ mStatusBar.reevaluateStyles();
+
+ // add notification
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ when(row.isClearable()).thenReturn(true);
+ mStackScroller.addContainerView(row);
+
+ mStatusBar.onUpdateRowStates();
+
+ // move footer to end
+ verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(-1 /* end */));
+ }
+
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
@@ -587,6 +656,7 @@
mScrimController = scrimController;
mFingerprintUnlockController = fingerprintUnlockController;
mActivityLaunchAnimator = launchAnimator;
+ mClearAllEnabled = true;
}
private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 2afb48c..ed0f9ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -18,7 +18,6 @@
import android.os.HandlerThread;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.SubscriptionInfo;
-import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -81,7 +80,8 @@
boolean in = true;
boolean out = true;
String description = "Test";
- mHandler.setWifiIndicators(enabled, status, qs, in, out, description, true);
+ String secondaryLabel = "Secondary label";
+ mHandler.setWifiIndicators(enabled, status, qs, in, out, description, true, secondaryLabel);
waitForCallbacks();
ArgumentCaptor<Boolean> enableArg = ArgumentCaptor.forClass(Boolean.class);
@@ -91,9 +91,10 @@
ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<Boolean> isTransient = ArgumentCaptor.forClass(Boolean.class);
+ ArgumentCaptor<String> secondary = ArgumentCaptor.forClass(String.class);
Mockito.verify(mSignalCallback).setWifiIndicators(enableArg.capture(),
statusArg.capture(), qsArg.capture(), inArg.capture(), outArg.capture(),
- descArg.capture(), isTransient.capture());
+ descArg.capture(), isTransient.capture(), secondary.capture());
assertEquals(enabled, (boolean) enableArg.getValue());
assertEquals(status, statusArg.getValue());
assertEquals(qs, qsArg.getValue());
@@ -101,6 +102,7 @@
assertEquals(out, (boolean) outArg.getValue());
assertEquals(description, descArg.getValue());
assertTrue(isTransient.getValue());
+ assertEquals(secondaryLabel, secondary.getValue());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index fc3de84..d30e777 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -5,8 +5,6 @@
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiSsid;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -20,6 +18,7 @@
import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.when;
@@ -39,6 +38,7 @@
verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
setWifiState(true, testSsid);
+ setWifiLevel(0);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
@@ -160,7 +160,8 @@
ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
- anyBoolean(), any(), any(), inArg.capture(), outArg.capture(), any(), anyBoolean());
+ anyBoolean(), any(), any(), inArg.capture(), outArg.capture(), any(), anyBoolean(),
+ any());
assertEquals("WiFi data in, in quick settings", in, (boolean) inArg.getValue());
assertEquals("WiFi data out, in quick settings", out, (boolean) outArg.getValue());
}
@@ -173,7 +174,7 @@
Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
enabledArg.capture(), any(), iconArg.capture(), anyBoolean(),
- anyBoolean(), descArg.capture(), anyBoolean());
+ anyBoolean(), descArg.capture(), anyBoolean(), any());
IconState iconState = iconArg.getValue();
assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue());
assertEquals("WiFi connected, in quick settings", connected, iconState.visible);
@@ -186,7 +187,7 @@
Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(),
- any(), anyBoolean());
+ any(), anyBoolean(), any());
IconState iconState = iconArg.getValue();
assertEquals("WiFi visible, in status bar", visible, iconState.visible);
assertEquals("WiFi signal, in status bar", icon, iconState.icon);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index 6fa91ff..42069e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -23,10 +23,12 @@
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.FooterView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -94,4 +96,30 @@
verify(view).setText(R.string.empty_shade_text);
}
+
+ @Test
+ public void manageNotifications_visible() {
+ FooterView view = mock(FooterView.class);
+ mStackScroller.setFooterView(view);
+ when(view.willBeGone()).thenReturn(true);
+ when(view.isSecondaryVisible()).thenReturn(true);
+
+ mStackScroller.updateFooterView(true, false);
+
+ verify(view).setVisibility(View.VISIBLE);
+ verify(view).performSecondaryVisibilityAnimation(false);
+ }
+
+ @Test
+ public void clearAll_visible() {
+ FooterView view = mock(FooterView.class);
+ mStackScroller.setFooterView(view);
+ when(view.willBeGone()).thenReturn(true);
+ when(view.isSecondaryVisible()).thenReturn(false);
+
+ mStackScroller.updateFooterView(true, true);
+
+ verify(view).setVisibility(View.VISIBLE);
+ verify(view).performSecondaryVisibilityAnimation(true);
+ }
}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 9417f04..c6878d7 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5547,6 +5547,21 @@
// OS: P
ACTION_ANOMALY_TRIGGERED = 1367;
+ // ACTION: Settings > Condition > Device muted
+ // CATEGORY: SETTINGS
+ // OS: P
+ SETTINGS_CONDITION_DEVICE_MUTED = 1368;
+
+ // ACTION: Settings > Condition > Device vibrate
+ // CATEGORY: SETTINGS
+ // OS: P
+ SETTINGS_CONDITION_DEVICE_VIBRATE = 1369;
+
+ // OPEN: Settings > Connected devices > previously connected devices
+ // CATEGORY: SETTINGS
+ // OS: P
+ PREVIOUSLY_CONNECTED_DEVICES = 1370;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 39d0070..9c74188 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -382,6 +382,9 @@
// WifiWake statistics
optional WifiWakeStats wifi_wake_stats = 94;
+
+ // Histogram counting instances of scans with N many 802.11mc (RTT) supporting APs
+ repeated NumConnectableNetworksBucket observed_80211mc_supporting_aps_in_scan_histogram = 95;
}
// Information that gets logged for every WiFi connection.
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 7bb532e..58f78ec 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -180,9 +180,11 @@
*
* @param structure Assist structure
* @param urlBarIds list of ids; only the first id found will be sanitized.
+ *
+ * @return the node containing the URL bar
*/
@Nullable
- public static void sanitizeUrlBar(@NonNull AssistStructure structure,
+ public static ViewNode sanitizeUrlBar(@NonNull AssistStructure structure,
@NonNull String[] urlBarIds) {
final ViewNode urlBarNode = findViewNode(structure, (node) -> {
return ArrayUtils.contains(urlBarIds, node.getIdEntry());
@@ -191,7 +193,7 @@
final String domain = urlBarNode.getText().toString();
if (domain.isEmpty()) {
if (sDebug) Slog.d(TAG, "sanitizeUrlBar(): empty on " + urlBarNode.getIdEntry());
- return;
+ return null;
}
urlBarNode.setWebDomain(domain);
if (sDebug) {
@@ -199,6 +201,7 @@
+ urlBarNode.getWebDomain());
}
}
+ return urlBarNode;
}
private interface ViewNodeFilter {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1e1de35..bcb0bc4 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -146,6 +146,13 @@
/** Whether the app being autofilled is running in compat mode. */
private final boolean mCompatMode;
+ /** Node representing the URL bar on compat mode. */
+ @GuardedBy("mLock")
+ private ViewNode mUrlBar;
+
+ @GuardedBy("mLock")
+ private boolean mSaveOnAllViewsInvisible;
+
@GuardedBy("mLock")
private final ArrayMap<AutofillId, ViewState> mViewStates = new ArrayMap<>();
@@ -280,7 +287,17 @@
Slog.d(TAG, "url_bars in compat mode: " + Arrays.toString(urlBarIds));
}
if (urlBarIds != null) {
- Helper.sanitizeUrlBar(structure, urlBarIds);
+ mUrlBar = Helper.sanitizeUrlBar(structure, urlBarIds);
+ if (mUrlBar != null) {
+ final AutofillId urlBarId = mUrlBar.getAutofillId();
+ if (sDebug) {
+ Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain "
+ + mUrlBar.getWebDomain());
+ }
+ final ViewState viewState = new ViewState(Session.this, urlBarId,
+ Session.this, ViewState.STATE_URL_BAR);
+ mViewStates.put(urlBarId, viewState);
+ }
}
}
structure.sanitizeForParceling(true);
@@ -1878,14 +1895,15 @@
return;
}
if (sVerbose) {
- Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + action + ", flags=" + flags);
+ Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + actionAsString(action)
+ + ", flags=" + flags);
}
ViewState viewState = mViewStates.get(id);
if (viewState == null) {
if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED
|| action == ACTION_VIEW_ENTERED) {
- if (sVerbose) Slog.v(TAG, "Creating viewState for " + id + " on " + action);
+ if (sVerbose) Slog.v(TAG, "Creating viewState for " + id);
boolean isIgnored = isIgnoredLocked(id);
viewState = new ViewState(this, id, this,
isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
@@ -1895,11 +1913,11 @@
// detectable, and batch-send them when the session is finished (but that will
// require tracking detectable fields on AutofillManager)
if (isIgnored) {
- if (sDebug) Slog.d(TAG, "updateLocked(): ignoring view " + id);
+ if (sDebug) Slog.d(TAG, "updateLocked(): ignoring view " + viewState);
return;
}
} else {
- if (sVerbose) Slog.v(TAG, "Ignored action " + action + " for " + id);
+ if (sVerbose) Slog.v(TAG, "Ignoring specific action when viewState=null");
return;
}
}
@@ -1913,6 +1931,40 @@
requestNewFillResponseLocked(flags);
break;
case ACTION_VALUE_CHANGED:
+ if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
+ // Must cancel the session if the value of the URL bar changed
+ final String currentUrl = mUrlBar == null ? null
+ : mUrlBar.getText().toString().trim();
+ if (currentUrl == null) {
+ // Sanity check - shouldn't happen.
+ wtf(null, "URL bar value changed, but current value is null");
+ return;
+ }
+ if (value == null || ! value.isText()) {
+ // Sanity check - shouldn't happen.
+ wtf(null, "URL bar value changed to null or non-text: %s", value);
+ return;
+ }
+ final String newUrl = value.getTextValue().toString();
+ if (newUrl.equals(currentUrl)) {
+ if (sDebug) Slog.d(TAG, "Ignoring change on URL bar as it's the same");
+ return;
+ }
+ if (mSaveOnAllViewsInvisible) {
+ // We cannot cancel the session because it could hinder Save when all views
+ // are finished, as the URL bar changed callback is usually called before
+ // the virtual views become invisible.
+ if (sDebug) {
+ Slog.d(TAG, "Ignoring change on URL because session will finish when "
+ + "views are gone");
+ }
+ return;
+ }
+ if (sDebug) Slog.d(TAG, "Finishing session because URL bar changed");
+ forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN_COMPAT_MODE);
+ return;
+ }
+
if (value != null && !value.equals(viewState.getCurrentValue())) {
if (value.isEmpty()
&& viewState.getCurrentValue() != null
@@ -1953,6 +2005,12 @@
if (sVerbose && virtualBounds != null) {
Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds);
}
+
+ if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
+ if (sDebug) Slog.d(TAG, "Ignoring VIEW_ENTERED on URL BAR (id=" + id + ")");
+ return;
+ }
+
requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
// Remove the UI if the ViewState has changed.
@@ -2068,7 +2126,7 @@
if (response == null) return;
ArraySet<AutofillId> trackedViews = null;
- boolean saveOnAllViewsInvisible = false;
+ mSaveOnAllViewsInvisible = false;
boolean saveOnFinish = true;
final SaveInfo saveInfo = response.getSaveInfo();
final AutofillId saveTriggerId;
@@ -2081,10 +2139,10 @@
if (mCompatMode) {
flags |= SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE;
}
- saveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
+ mSaveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
// We only need to track views if we want to save once they become invisible.
- if (saveOnAllViewsInvisible) {
+ if (mSaveOnAllViewsInvisible) {
if (trackedViews == null) {
trackedViews = new ArraySet<>();
}
@@ -2129,7 +2187,7 @@
Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds
+ " triggerId: " + saveTriggerId + " saveOnFinish:" + saveOnFinish);
}
- mClient.setTrackedViews(id, toArray(trackedViews), saveOnAllViewsInvisible,
+ mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible,
saveOnFinish, toArray(fillableIds), saveTriggerId);
} catch (RemoteException e) {
Slog.w(TAG, "Cannot set tracked ids", e);
@@ -2421,6 +2479,16 @@
pw.print(prefix); pw.print("mClientState: "); pw.println(
Helper.bundleToString(mClientState));
pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode);
+ pw.print(prefix); pw.print("mUrlBar: ");
+ if (mUrlBar == null) {
+ pw.println("N/A");
+ } else {
+ pw.print("id="); pw.print(mUrlBar.getAutofillId());
+ pw.print(" domain="); pw.print(mUrlBar.getWebDomain());
+ pw.print(" text="); Helper.printlnRedactedText(pw, mUrlBar.getText());
+ }
+ pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
+ mSaveOnAllViewsInvisible);
pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
mRemoteFillService.dump(prefix, pw);
}
@@ -2513,6 +2581,11 @@
*/
@GuardedBy("mLock")
void forceRemoveSelfLocked() {
+ forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN);
+ }
+
+ @GuardedBy("mLock")
+ void forceRemoveSelfLocked(int clientState) {
if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
final boolean isPendingSaveUi = isSaveUiPendingLocked();
@@ -2521,7 +2594,7 @@
mUi.destroyAll(mPendingSaveUi, this, false);
if (!isPendingSaveUi) {
try {
- mClient.setSessionFinished(AutofillManager.STATE_UNKNOWN);
+ mClient.setSessionFinished(clientState);
} catch (RemoteException e) {
Slog.e(TAG, "Error notifying client to finish session", e);
}
@@ -2624,4 +2697,19 @@
Slog.wtf(TAG, message);
}
}
+
+ private static String actionAsString(int action) {
+ switch (action) {
+ case ACTION_START_SESSION:
+ return "START_SESSION";
+ case ACTION_VIEW_ENTERED:
+ return "VIEW_ENTERED";
+ case ACTION_VIEW_EXITED:
+ return "VIEW_EXITED";
+ case ACTION_VALUE_CHANGED:
+ return "VALUE_CHANGED";
+ default:
+ return "UNKNOWN_" + action;
+ }
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 03c5850..97ab97b 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -67,6 +67,8 @@
public static final int STATE_IGNORED = 0x080;
/** User manually request autofill in this view, after it was already autofilled. */
public static final int STATE_RESTARTED_SESSION = 0x100;
+ /** View is the URL bar of a package on compat mode. */
+ public static final int STATE_URL_BAR = 0x200;
public final AutofillId id;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 3d7b21d..379658f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2668,6 +2668,9 @@
final int userId = UserHandle.getUserId(Binder.getCallingUid());
final UserEnvironment userEnv = new UserEnvironment(userId);
+ // Ignore requests to create directories while storage is locked
+ if (!isUserKeyUnlocked(userId)) return;
+
// Validate that reported package name belongs to caller
final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
Context.APP_OPS_SERVICE);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f0acf7..5131a93 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -281,6 +281,7 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageManager;
@@ -1266,6 +1267,8 @@
}
}
+ boolean mSystemProvidersInstalled;
+
CoreSettingsObserver mCoreSettingsObserver;
FontScaleSettingObserver mFontScaleSettingObserver;
@@ -4183,12 +4186,14 @@
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
- if (!app.info.isAllowedToUseHiddenApi() &&
- !disableHiddenApiChecks &&
- !mHiddenApiBlacklist.isDisabled()) {
- // This app is not allowed to use undocumented and private APIs, or blacklisting is
- // enabled. Set up its runtime with the appropriate flag.
- runtimeFlags |= Zygote.ENABLE_HIDDEN_API_CHECKS;
+ if (!disableHiddenApiChecks && !mHiddenApiBlacklist.isDisabled()) {
+ @HiddenApiEnforcementPolicy int policy =
+ app.info.getHiddenApiEnforcementPolicy();
+ int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
+ if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
+ throw new IllegalStateException("Invalid API policy: " + policy);
+ }
+ runtimeFlags |= policyBits;
}
String invokeWith = null;
@@ -12110,6 +12115,14 @@
"Attempt to launch content provider before system ready");
}
+ // If system providers are not installed yet we aggressively crash to avoid
+ // creating multiple instance of these providers and then bad things happen!
+ if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp()
+ && "system".equals(cpi.processName)) {
+ throw new IllegalStateException("Cannot access system provider: '"
+ + cpi.authority + "' before system providers are installed!");
+ }
+
// Make sure that the user who owns this provider is running. If not,
// we don't want to allow it to run.
if (!mUserController.isUserRunning(userId, 0)) {
@@ -12663,6 +12676,10 @@
mSystemThread.installSystemProviders(providers);
}
+ synchronized (this) {
+ mSystemProvidersInstalled = true;
+ }
+
mConstants.start(mContext.getContentResolver());
mCoreSettingsObserver = new CoreSettingsObserver(this);
mFontScaleSettingObserver = new FontScaleSettingObserver();
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 4313d17..711d40b 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -60,8 +60,14 @@
int[] backlightRange = resources.getIntArray(
com.android.internal.R.array.config_screenBrightnessBacklight);
+ float[] minimumBrightnessCurveLux = getLuxLevels(resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveLux));
+ float[] minimumBrightnessCurveNits = getFloatArray(resources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveNits));
+
if (isValidMapping(nitsRange, backlightRange)
- && isValidMapping(luxLevels, brightnessLevelsNits)) {
+ && isValidMapping(luxLevels, brightnessLevelsNits)
+ && isValidMapping(minimumBrightnessCurveLux, minimumBrightnessCurveNits)) {
int minimumBacklight = resources.getInteger(
com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
int maximumBacklight = resources.getInteger(
@@ -73,7 +79,8 @@
}
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
builder.setCurve(luxLevels, brightnessLevelsNits);
- return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange);
+ return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange,
+ minimumBrightnessCurveLux, minimumBrightnessCurveNits);
} else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight);
} else {
@@ -448,8 +455,11 @@
private float mUserLux;
private float mUserBrightness;
+ private final Spline mMinimumBrightnessCurve;
+
public PhysicalMappingStrategy(BrightnessConfiguration config,
- float[] nits, int[] backlight) {
+ float[] nits, int[] backlight, float[] minimumBrightnessCurveLux,
+ float[] minimumBrightnessCurveNits) {
Preconditions.checkArgument(nits.length != 0 && backlight.length != 0,
"Nits and backlight arrays must not be empty!");
Preconditions.checkArgument(nits.length == backlight.length,
@@ -469,6 +479,9 @@
normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
}
+ mMinimumBrightnessCurve = Spline.createSpline(
+ minimumBrightnessCurveLux, minimumBrightnessCurveNits);
+
mNitsToBacklightSpline = createSpline(nits, normalizedBacklight);
mBacklightToNitsSpline = createSpline(normalizedBacklight, nits);
@@ -484,7 +497,7 @@
if (config.equals(mConfig)) {
return false;
}
-
+ validateBrightnessConfiguration(config);
Pair<float[], float[]> curve = config.getCurve();
mBrightnessSpline = createSpline(curve.first /*lux*/, curve.second /*nits*/);
mConfig = config;
@@ -549,5 +562,24 @@
pw.println(" mUserLux=" + mUserLux);
pw.println(" mUserBrightness=" + mUserBrightness);
}
+
+ private void validateBrightnessConfiguration(BrightnessConfiguration config) {
+ Pair<float[], float[]> curve = config.getCurve();
+ Spline brightnessSpline = Spline.createSpline(curve.first, curve.second);
+ if (isBrightnessSplineTooDark(brightnessSpline)) {
+ throw new IllegalArgumentException("brightness curve is too dark");
+ }
+ }
+
+ private boolean isBrightnessSplineTooDark(Spline brightnessSpline) {
+ float[] lux = mDefaultConfig.getCurve().first;
+ for (int i = 0; i < lux.length; i++) {
+ if (brightnessSpline.interpolate(lux[i]) <
+ mMinimumBrightnessCurve.interpolate(lux[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index ff8b88b..1784ef1 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -500,7 +500,7 @@
}
public void onSwitchUser(@UserIdInt int newUserId) {
- handleSettingsChange();
+ handleSettingsChange(true /* userSwitch */);
mBrightnessTracker.onSwitchUser(newUserId);
}
@@ -1420,8 +1420,12 @@
mHandler.post(mOnStateChangedRunnable);
}
- private void handleSettingsChange() {
+ private void handleSettingsChange(boolean userSwitch) {
mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
+ if (userSwitch) {
+ // Don't treat user switches as user initiated change.
+ mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
+ }
mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
// We don't bother with a pending variable for VR screen brightness since we just
// immediately adapt to it.
@@ -1735,7 +1739,7 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
- handleSettingsChange();
+ handleSettingsChange(false /* userSwitch */);
}
}
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index abe55bb..8365fd2 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -160,7 +160,7 @@
private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
// Only consider doing this for prefetching jobs
- if ((jobStatus.getJob().getFlags() & JobInfo.FLAG_IS_PREFETCH) == 0) {
+ if (!jobStatus.getJob().isPrefetch()) {
return false;
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 7c56f4d..f617964 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -21,6 +21,7 @@
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
@@ -524,6 +525,10 @@
public void onCleanupUser(int userId) {
hideEncryptionNotification(new UserHandle(userId));
+ // User is stopped with its CE key evicted. Require strong auth next time to be able to
+ // unlock the user's storage. Use STRONG_AUTH_REQUIRED_AFTER_BOOT since stopping and
+ // restarting a user later is equivalent to rebooting the device.
+ requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_BOOT, userId);
}
public void onStartUser(final int userId) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index f0bcb81..e0487ea 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -382,10 +382,26 @@
Preconditions.checkNotNull(secretTypes, "secretTypes is null");
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
- long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
- if (updatedRows > 0) {
- mDatabase.setShouldCreateSnapshot(userId, uid, true);
+
+ int[] currentSecretTypes = mDatabase.getRecoverySecretTypes(userId, uid);
+ if (Arrays.equals(secretTypes, currentSecretTypes)) {
+ Log.v(TAG, "Not updating secret types - same as old value.");
+ return;
}
+
+ long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
+ if (updatedRows < 1) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR,
+ "Database error trying to set secret types.");
+ }
+
+ if (currentSecretTypes.length == 0) {
+ Log.i(TAG, "Initialized secret types.");
+ return;
+ }
+
+ Log.i(TAG, "Updated secret types. Snapshot pending.");
+ mDatabase.setShouldCreateSnapshot(userId, uid, true);
}
/**
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 7348b84..01f0d74 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1532,7 +1532,7 @@
@Override
public boolean createSession2(Bundle sessionToken) {
final int uid = Binder.getCallingUid();
- final SessionToken2 token = SessionToken2.fromBundle(getContext(), sessionToken);
+ final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
if (token == null || token.getUid() != uid) {
Log.w(TAG, "onSessionCreated failed, expected caller uid=" + token.getUid()
+ " but from uid=" + uid);
@@ -1571,7 +1571,7 @@
@Override
public void destroySession2(Bundle sessionToken) {
final int uid = Binder.getCallingUid();
- final SessionToken2 token = SessionToken2.fromBundle(getContext(), sessionToken);
+ final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
if (token == null || token.getUid() != uid) {
Log.w(TAG, "onSessionDestroyed failed, expected caller uid=" + token.getUid()
+ " but from uid=" + uid);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index efca159..8febecf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1141,7 +1141,7 @@
// TODO: support shared UIDs
if (maxBytes > 0 && maxBytes > totalBytes / 2) {
final String[] packageNames = mContext.getPackageManager().getPackagesForUid(maxUid);
- if (packageNames.length == 1) {
+ if (packageNames != null && packageNames.length == 1) {
try {
return mContext.getPackageManager().getApplicationInfo(packageNames[0],
MATCH_ANY_USER | MATCH_DISABLED_COMPONENTS | MATCH_DIRECT_BOOT_AWARE
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index 2714d5e..d793842 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -107,6 +107,10 @@
* Reload watchlist by reading config file.
*/
public void reloadConfig() {
+ if (!mXmlFile.exists()) {
+ // No config file
+ return;
+ }
try (FileInputStream stream = new FileInputStream(mXmlFile)){
final List<byte[]> crc32DomainList = new ArrayList<>();
final List<byte[]> sha256DomainList = new ArrayList<>();
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
index f5ba889..e20a510 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
@@ -87,6 +87,10 @@
}
public void reloadSettings() {
+ if (!mXmlFile.exists()) {
+ // No settings config
+ return;
+ }
try (FileInputStream stream = mXmlFile.openRead()){
XmlPullParser parser = Xml.newPullParser();
parser.setInput(stream, StandardCharsets.UTF_8.name());
@@ -97,7 +101,7 @@
mPrivacySecretKey = parseSecretKey(parser);
}
}
- Log.i(TAG, "Reload watchlist settings done");
+ Slog.i(TAG, "Reload watchlist settings done");
} catch (IllegalStateException | NullPointerException | NumberFormatException |
XmlPullParserException | IOException | IndexOutOfBoundsException e) {
Slog.e(TAG, "Failed parsing xml", e);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 9e7ad47..61c6be7 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -47,6 +47,8 @@
import dalvik.system.DexFile;
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_NONE;
+
import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
@@ -532,7 +534,10 @@
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 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;
+ // TODO we should pass the actual flag value to dexopt, rather than assuming blacklist
+ int hiddenApiFlag = info.getHiddenApiEnforcementPolicy() == HIDDEN_API_ENFORCEMENT_NONE
+ ? 0
+ : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
// Avoid generating CompactDex for modes that are latency critical.
final int compilationReason = options.getCompilationReason();
boolean generateCompactDex = true;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3e47ea6..a7eac55 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -385,6 +385,11 @@
*/
private int mSurfaceSize;
+ /**
+ * Sequence number for the current layout pass.
+ */
+ int mLayoutSeq = 0;
+
/** Temporary float array to retrieve 3x3 matrix values. */
private final float[] mTmpFloats = new float[9];
@@ -554,7 +559,7 @@
w.prelayout();
final boolean firstLayout = !w.isLaidOut();
mService.mPolicy.layoutWindowLw(w, null, mDisplayFrames);
- w.mLayoutSeq = mService.mLayoutSeq;
+ w.mLayoutSeq = mLayoutSeq;
// If this is the first layout, we need to initialize the last inset values as
// otherwise we'd immediately cause an unnecessary resize.
@@ -593,7 +598,7 @@
w.mLayoutNeeded = false;
w.prelayout();
mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
- w.mLayoutSeq = mService.mLayoutSeq;
+ w.mLayoutSeq = mLayoutSeq;
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
+ " mContainingFrame=" + w.mContainingFrame
+ " mDisplayFrame=" + w.mDisplayFrame);
@@ -2219,6 +2224,9 @@
pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
pw.println();
+ pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
+
+ pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -2927,9 +2935,9 @@
mService.mScreenRect.set(0, 0, dw, dh);
}
- int seq = mService.mLayoutSeq + 1;
+ int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
- mService.mLayoutSeq = seq;
+ mLayoutSeq = seq;
// Used to indicate that we have processed the dream window and all additional windows are
// behind it.
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 19d6691..87a7a74 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -51,6 +51,7 @@
import com.google.android.collect.Sets;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -400,9 +401,11 @@
if (mainWindow == null) {
return null;
}
+ final Rect insets = new Rect(mainWindow.mContentInsets);
+ InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
!mTask.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
- mainWindow.mContentInsets, mTask.getPrefixOrderIndex(), position, bounds,
+ insets, mTask.getPrefixOrderIndex(), position, bounds,
mTask.getWindowConfiguration(), mIsRecentTaskInvisible);
return mTarget;
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index c590067..88577d0 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -37,6 +37,7 @@
import com.android.internal.util.FastPrintWriter;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -235,9 +236,11 @@
|| mCapturedLeash == null) {
return null;
}
+ final Rect insets = new Rect(mainWindow.mContentInsets);
+ InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
mCapturedLeash, !mAppWindowToken.fillsParent(),
- mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
+ mainWindow.mWinAnimator.mLastClipRect, insets,
mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
task.getWindowConfiguration(), false /*isNotInRecents*/);
return mTarget;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 970a8d7..5f9d679 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -27,7 +27,6 @@
import android.app.ActivityManager.TaskSnapshot;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.os.Environment;
@@ -45,6 +44,7 @@
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter;
+import com.android.server.wm.utils.InsetUtils;
import com.google.android.collect.Sets;
@@ -273,7 +273,7 @@
return null;
}
return new TaskSnapshot(buffer, top.getConfiguration().orientation,
- getInsetsFromTaskBounds(mainWindow, task),
+ getInsets(mainWindow),
isLowRamDevice /* reduced */, scaleFraction /* scale */,
true /* isRealSnapshot */);
}
@@ -282,11 +282,11 @@
return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
}
- private Rect getInsetsFromTaskBounds(WindowState state, Task task) {
+ private Rect getInsets(WindowState state) {
// XXX(b/72757033): These are insets relative to the window frame, but we're really
// interested in the insets relative to the task bounds.
- Rect insets = minRect(state.mContentInsets, state.mStableInsets);
- insets = maxRect(insets, state.mAppToken.getLetterboxInsets());
+ final Rect insets = minRect(state.mContentInsets, state.mStableInsets);
+ InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets());
return insets;
}
@@ -297,13 +297,6 @@
Math.min(rect1.bottom, rect2.bottom));
}
- private Rect maxRect(Rect rect1, Rect rect2) {
- return new Rect(Math.max(rect1.left, rect2.left),
- Math.max(rect1.top, rect2.top),
- Math.max(rect1.right, rect2.right),
- Math.max(rect1.bottom, rect2.bottom));
- }
-
/**
* Retrieves all closing tasks based on the list of closing apps during an app transition.
*/
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 56b314f..449aa2c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -596,8 +596,6 @@
boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
- int mLayoutSeq = 0;
-
// Last systemUiVisibility we received from status bar.
int mLastStatusBarVisibility = 0;
// Last systemUiVisibility we dispatched to windows.
@@ -6344,8 +6342,7 @@
if (mInputMethodTarget != null) {
pw.print(" mInputMethodTarget="); pw.println(mInputMethodTarget);
}
- pw.print(" mInTouchMode="); pw.print(mInTouchMode);
- pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
+ pw.print(" mInTouchMode="); pw.println(mInTouchMode);
pw.print(" mLastDisplayFreezeDuration=");
TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw);
if ( mLastFinishedFreezeSource != null) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 993556f..30bbfe0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1236,7 +1236,7 @@
*/
void updateResizingWindowIfNeeded() {
final WindowStateAnimator winAnimator = mWinAnimator;
- if (!mHasSurface || mService.mLayoutSeq != mLayoutSeq || isGoneForLayoutLw()) {
+ if (!mHasSurface || getDisplayContent().mLayoutSeq != mLayoutSeq || isGoneForLayoutLw()) {
return;
}
@@ -1360,6 +1360,15 @@
return mToken.getDisplayContent();
}
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ super.onDisplayChanged(dc);
+ // Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
+ if (dc != null) {
+ mLayoutSeq = dc.mLayoutSeq - 1;
+ }
+ }
+
DisplayInfo getDisplayInfo() {
final DisplayContent displayContent = getDisplayContent();
return displayContent != null ? displayContent.getDisplayInfo() : null;
diff --git a/services/core/java/com/android/server/wm/utils/InsetUtils.java b/services/core/java/com/android/server/wm/utils/InsetUtils.java
new file mode 100644
index 0000000..b4a998a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/InsetUtils.java
@@ -0,0 +1,38 @@
+/*
+ * 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.server.wm.utils;
+
+import android.graphics.Rect;
+
+/**
+ * Utility methods to handle insets represented as rects.
+ */
+public class InsetUtils {
+
+ private InsetUtils() {
+ }
+
+ /**
+ * Adds {@code insetsToAdd} to {@code inOutInsets}.
+ */
+ public static void addInsets(Rect inOutInsets, Rect insetsToAdd) {
+ inOutInsets.left += insetsToAdd.left;
+ inOutInsets.top += insetsToAdd.top;
+ inOutInsets.right += insetsToAdd.right;
+ inOutInsets.bottom += insetsToAdd.bottom;
+ }
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 427b9d2..89efe12 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -86,6 +86,7 @@
"libhardware_legacy",
"libhidlbase",
"libkeystore_binder",
+ "libmtp",
"libnativehelper",
"libutils",
"libui",
diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp
index f37f870..ff1ec04 100644
--- a/services/core/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp
@@ -21,6 +21,7 @@
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
+#include "MtpDescriptors.h"
#include <stdio.h>
#include <asm/byteorder.h>
@@ -118,6 +119,38 @@
return result;
}
+static jobject android_server_UsbDeviceManager_openControl(JNIEnv *env, jobject /* thiz */, jstring jFunction) {
+ const char *function = env->GetStringUTFChars(jFunction, NULL);
+ bool ptp = false;
+ int fd = -1;
+ if (!strcmp(function, "ptp")) {
+ ptp = true;
+ }
+ if (!strcmp(function, "mtp") || ptp) {
+ fd = TEMP_FAILURE_RETRY(open(ptp ? FFS_PTP_EP0 : FFS_MTP_EP0, O_RDWR));
+ if (fd < 0) {
+ ALOGE("could not open control for %s %s", function, strerror(errno));
+ goto error;
+ }
+ if (!writeDescriptors(fd, ptp)) {
+ goto error;
+ }
+ }
+
+ if (function != NULL) {
+ env->ReleaseStringUTFChars(jFunction, function);
+ }
+ return jniCreateFileDescriptor(env, fd);
+error:
+ if (fd != -1) {
+ close(fd);
+ }
+ if (function != NULL) {
+ env->ReleaseStringUTFChars(jFunction, function);
+ }
+ return NULL;
+}
+
static const JNINativeMethod method_table[] = {
{ "nativeGetAccessoryStrings", "()[Ljava/lang/String;",
(void*)android_server_UsbDeviceManager_getAccessoryStrings },
@@ -127,6 +160,8 @@
(void*)android_server_UsbDeviceManager_isStartRequested },
{ "nativeGetAudioMode", "()I",
(void*)android_server_UsbDeviceManager_getAudioMode },
+ { "nativeOpenControl", "(Ljava/lang/String;)Ljava/io/FileDescriptor;",
+ (void*)android_server_UsbDeviceManager_openControl },
};
int register_android_server_UsbDeviceManager(JNIEnv *env)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2e07703..1e216a3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -566,9 +566,7 @@
}
public static class DevicePolicyData {
- @NonNull PasswordMetrics mActivePasswordMetrics = new PasswordMetrics();
int mFailedPasswordAttempts = 0;
- boolean mPasswordStateHasBeenSetSinceBoot = false;
boolean mPasswordValidAtLastCheckpoint = true;
int mUserHandle;
@@ -628,6 +626,8 @@
}
final SparseArray<DevicePolicyData> mUserData = new SparseArray<>();
+ @GuardedBy("DevicePolicyManagerService.this")
+ final SparseArray<PasswordMetrics> mUserPasswordMetrics = new SparseArray<>();
final Handler mHandler;
final Handler mBackgroundHandler;
@@ -2158,6 +2158,16 @@
}
/**
+ * Provides PasswordMetrics object corresponding to the given user.
+ * @param userHandle the user for whom to provide metrics.
+ * @return the user password metrics, or {@code null} if none have been associated with
+ * the user yet (for example, if the device has booted but not been unlocked).
+ */
+ PasswordMetrics getUserPasswordMetricsLocked(int userHandle) {
+ return mUserPasswordMetrics.get(userHandle);
+ }
+
+ /**
* Creates and loads the policy data from xml for data that is shared between
* various profiles of a user. In contrast to {@link #getUserData(int)}
* it allows access to data of users other than the calling user.
@@ -2191,6 +2201,10 @@
if (policy != null) {
mUserData.remove(userHandle);
}
+ if (mUserPasswordMetrics.get(userHandle) != null) {
+ mUserPasswordMetrics.remove(userHandle);
+ }
+
File policyFile = new File(mInjector.environmentGetUserSystemDirectory(userHandle),
DEVICE_POLICIES_XML);
policyFile.delete();
@@ -3907,9 +3921,15 @@
private void updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
final int credentialOwner = getCredentialOwner(userHandle, parent);
DevicePolicyData policy = getUserData(credentialOwner);
+ PasswordMetrics metrics = getUserPasswordMetricsLocked(credentialOwner);
+ if (metrics == null) {
+ Slog.wtf(LOG_TAG, "Should have had a valid password metrics for updating checkpoint " +
+ "validity.");
+ metrics = new PasswordMetrics();
+ }
policy.mPasswordValidAtLastCheckpoint =
isPasswordSufficientForUserWithoutCheckpointLocked(
- policy.mActivePasswordMetrics, userHandle, parent);
+ metrics, userHandle, parent);
saveSettingsLocked(credentialOwner);
}
@@ -4532,8 +4552,11 @@
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, parent));
- return isActivePasswordSufficientForUserLocked(policy, userHandle, parent);
+ int credentialOwner = getCredentialOwner(userHandle, parent);
+ DevicePolicyData policy = getUserDataUnchecked(credentialOwner);
+ PasswordMetrics metrics = getUserPasswordMetricsLocked(credentialOwner);
+ return isActivePasswordSufficientForUserLocked(
+ policy.mPasswordValidAtLastCheckpoint, metrics, userHandle, parent);
}
}
@@ -4559,25 +4582,31 @@
synchronized (this) {
final int targetUser = getProfileParentId(userHandle);
enforceUserUnlocked(targetUser, false);
- DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, false));
- return isActivePasswordSufficientForUserLocked(policy, targetUser, false);
+ int credentialOwner = getCredentialOwner(userHandle, false);
+ DevicePolicyData policy = getUserDataUnchecked(credentialOwner);
+ PasswordMetrics metrics = getUserPasswordMetricsLocked(credentialOwner);
+ return isActivePasswordSufficientForUserLocked(
+ policy.mPasswordValidAtLastCheckpoint, metrics, targetUser, false);
}
}
private boolean isActivePasswordSufficientForUserLocked(
- DevicePolicyData policy, int userHandle, boolean parent) {
- if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()
- && !policy.mPasswordStateHasBeenSetSinceBoot) {
+ boolean passwordValidAtLastCheckpoint, PasswordMetrics metrics, int userHandle,
+ boolean parent) {
+ if (!mInjector.storageManagerIsFileBasedEncryptionEnabled() && (metrics == null)) {
// Before user enters their password for the first time after a reboot, return the
// value of this flag, which tells us whether the password was valid the last time
// settings were saved. If DPC changes password requirements on boot so that the
// current password no longer meets the requirements, this value will be stale until
// the next time the password is entered.
- return policy.mPasswordValidAtLastCheckpoint;
+ return passwordValidAtLastCheckpoint;
}
- return isPasswordSufficientForUserWithoutCheckpointLocked(
- policy.mActivePasswordMetrics, userHandle, parent);
+ if (metrics == null) {
+ Slog.wtf(LOG_TAG, "FBE device, should have been unlocked and had valid metrics.");
+ metrics = new PasswordMetrics();
+ }
+ return isPasswordSufficientForUserWithoutCheckpointLocked(metrics, userHandle, parent);
}
/**
@@ -6123,10 +6152,8 @@
}
validateQualityConstant(metrics.quality);
- DevicePolicyData policy = getUserData(userHandle);
synchronized (this) {
- policy.mActivePasswordMetrics = metrics;
- policy.mPasswordStateHasBeenSetSinceBoot = true;
+ mUserPasswordMetrics.put(userHandle, metrics);
}
}
@@ -7640,6 +7667,7 @@
if (!mHasFeature) {
return DevicePolicyManager.STATE_USER_UNMANAGED;
}
+ enforceManageUsers();
int userHandle = mInjector.userHandleGetCallingUserId();
return getUserProvisioningState(userHandle);
}
@@ -7833,8 +7861,7 @@
ApplicationInfo appInfo = userContext.getApplicationInfo();
CharSequence result = null;
if (appInfo != null) {
- PackageManager pm = userContext.getPackageManager();
- result = pm.getApplicationLabel(appInfo);
+ result = appInfo.loadUnsafeLabel(userContext.getPackageManager());
}
return result != null ? result.toString() : null;
} finally {
@@ -11816,6 +11843,7 @@
@Override
public boolean isDeviceProvisioned() {
+ enforceManageUsers();
synchronized (this) {
return getUserDataUnchecked(UserHandle.USER_SYSTEM).mUserSetupComplete;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 0268519..8366114 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -723,6 +723,11 @@
mSystemUpdatePolicy.saveToXml(out);
out.endTag(null, TAG_SYSTEM_UPDATE_POLICY);
}
+
+ if (mSystemUpdateInfo != null) {
+ mSystemUpdateInfo.writeToXml(out, TAG_PENDING_OTA_INFO);
+ }
+
if (mSystemUpdateFreezeStart != null || mSystemUpdateFreezeEnd != null) {
out.startTag(null, TAG_FREEZE_PERIOD_RECORD);
if (mSystemUpdateFreezeStart != null) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 43490d3..a64efb7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2387,12 +2387,12 @@
}
public void testGetUserProvisioningState_defaultResult() {
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
}
public void testSetUserProvisioningState_permission() throws Exception {
setupProfileOwner();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
@@ -2407,6 +2407,7 @@
public void testSetUserProvisioningState_noManagement() {
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
assertExpectException(IllegalStateException.class,
/* messageRegex= */ "change provisioning state unless a .* owner is set",
() -> dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
@@ -2417,7 +2418,6 @@
public void testSetUserProvisioningState_deviceOwnerFromSetupWizard() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
@@ -2428,7 +2428,6 @@
throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
@@ -2438,7 +2437,6 @@
public void testSetUserProvisioningState_deviceOwnerWithoutSetupWizard() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
@@ -2447,7 +2445,6 @@
public void testSetUserProvisioningState_managedProfileFromSetupWizard_primaryUser()
throws Exception {
setupProfileOwner();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
DevicePolicyManager.STATE_USER_PROFILE_COMPLETE,
@@ -2457,7 +2454,6 @@
public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile()
throws Exception {
setupProfileOwner();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
@@ -2466,7 +2462,6 @@
public void testSetUserProvisioningState_managedProfileWithoutSetupWizard() throws Exception {
setupProfileOwner();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
@@ -2474,7 +2469,6 @@
public void testSetUserProvisioningState_illegalTransitionOutOfFinalized1() throws Exception {
setupProfileOwner();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
assertExpectException(IllegalStateException.class,
/* messageRegex= */ "Cannot move to user provisioning state",
@@ -2486,7 +2480,6 @@
public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState()
throws Exception {
setupProfileOwner();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
assertExpectException(IllegalStateException.class,
/* messageRegex= */ "Cannot move to user provisioning state",
@@ -2496,6 +2489,9 @@
}
private void exerciseUserProvisioningTransitions(int userId, int... states) {
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
+
assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
for (int state : states) {
dpm.setUserProvisioningState(state, userId);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index fb25cf3..d922df3 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -85,6 +85,9 @@
255
};
+ private static final int[] MINIMUM_BRIGHTNESS_CURVE_LUX = { 2000, 4000 };
+ private static final float[] MINIMUM_BRIGHTNESS_CURVE_NITS = { 1.0f, 50.0f, 90.0f };
+
private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
private static final int[] BACKLIGHT_RANGE = { 1, 255 };
@@ -381,6 +384,19 @@
com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
.thenReturn(mockBrightnessLevelNits);
+ int[] mockMinimumBrightnessCurveLux = new int[MINIMUM_BRIGHTNESS_CURVE_LUX.length];
+ for (int i = 0; i < mockMinimumBrightnessCurveLux.length; i++) {
+ mockMinimumBrightnessCurveLux[i] = (int) MINIMUM_BRIGHTNESS_CURVE_LUX[i];
+ }
+ when(mockResources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveLux))
+ .thenReturn(mockMinimumBrightnessCurveLux);
+ TypedArray mockMinimumBrightnessCurveNits = createFloatTypedArray(
+ MINIMUM_BRIGHTNESS_CURVE_NITS);
+ when(mockResources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveNits))
+ .thenReturn(mockMinimumBrightnessCurveNits);
+
TypedArray mockNitsRange = createFloatTypedArray(nitsRange);
when(mockResources.obtainTypedArray(
com.android.internal.R.array.config_screenBrightnessNits))
@@ -419,4 +435,78 @@
return mockArray;
}
+ private float[] getNearMinimumNits(float epsilon) {
+ float[] lux = new float[MINIMUM_BRIGHTNESS_CURVE_LUX.length + 1];
+ for (int i = 0; i < MINIMUM_BRIGHTNESS_CURVE_LUX.length; i++) {
+ lux[i+1] = MINIMUM_BRIGHTNESS_CURVE_LUX[i];
+ }
+ Spline minimumBrightnessCurve = Spline.createSpline(lux, MINIMUM_BRIGHTNESS_CURVE_NITS);
+ float[] nits = new float[LUX_LEVELS.length];
+ for (int i = 0; i < nits.length; i++) {
+ nits[i] = minimumBrightnessCurve.interpolate(LUX_LEVELS[i]) + epsilon;
+ }
+ return nits;
+ }
+
+ @Test
+ public void testCreateWithTooDarkBrightnessConfigurationThrowsException() {
+ float[] nits = getNearMinimumNits(-0.1f);
+ Resources res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+ Exception thrown = null;
+ try {
+ BrightnessMappingStrategy.create(res);
+ } catch (IllegalArgumentException e) {
+ thrown = e;
+ }
+ assertNotNull("Failed to throw IllegalArgumentException", thrown);
+ }
+
+ @Test
+ public void testCreationWithBrightEnoughBrightnessConfigurationDoesNotThrowException() {
+ float[] nits = getNearMinimumNits(0);
+ Resources res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+ assertNotNull("Failed to create BrightnessMappingStrategy",
+ BrightnessMappingStrategy.create(res));
+ }
+
+ @Test
+ public void testSettingTooDarkBrightnessConfigurationThrowsException() {
+ Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
+ BACKLIGHT_RANGE);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+ assertNotNull("Failed to create BrightnessMappingStrategy", strategy);
+ float[] lux = new float[LUX_LEVELS.length];
+ for (int i = 0; i < lux.length; i++) {
+ lux[i] = LUX_LEVELS[i];
+ }
+ float[] nits = getNearMinimumNits(-0.1f);
+ BrightnessConfiguration config = new BrightnessConfiguration.Builder()
+ .setCurve(lux, nits)
+ .build();
+ Exception thrown = null;
+ try {
+ strategy.setBrightnessConfiguration(config);
+ } catch (IllegalArgumentException e) {
+ thrown = e;
+ }
+ assertNotNull("Failed to throw IllegalArgumentException", thrown);
+ }
+
+ @Test
+ public void testSettingBrightEnouhgBrightnessConfigurationDoesNotThrowException() {
+ Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
+ BACKLIGHT_RANGE);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+ assertNotNull("Failed to create BrightnessMappingStrategy", strategy);
+ float[] lux = new float[LUX_LEVELS.length];
+ for (int i = 0; i < lux.length; i++) {
+ lux[i] = LUX_LEVELS[i];
+ }
+ float[] nits = getNearMinimumNits(0);
+ BrightnessConfiguration config = new BrightnessConfiguration.Builder()
+ .setCurve(lux, nits)
+ .build();
+ assertTrue("failed to set brightness configuration",
+ strategy.setBrightnessConfiguration(config));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index b7ce59d..06b94cb 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -879,7 +879,7 @@
}
@Test
- public void setRecoverySecretTypes() throws Exception {
+ public void setRecoverySecretTypes_updatesSecretTypes() throws Exception {
int[] types1 = new int[]{11, 2000};
int[] types2 = new int[]{1, 2, 3};
int[] types3 = new int[]{};
@@ -898,6 +898,41 @@
}
@Test
+ public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfIniting() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ int[] secretTypes = new int[] { 101 };
+
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+ }
+
+ @Test
+ public void setRecoverySecretTypes_doesNotSetSnapshotPendingIfSettingSameValue()
+ throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ int[] secretTypes = new int[] { 101 };
+
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
+
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+ }
+
+ @Test
+ public void setRecoverySecretTypes_setsSnapshotPendingIfUpdatingValue() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 101 });
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 102 });
+
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+ }
+
+ @Test
public void setRecoverySecretTypes_throwsIfNullTypes() throws Exception {
try {
mRecoverableKeyStoreManager.setRecoverySecretTypes(/*types=*/ null);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index ead817a..c772956 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -7,23 +7,19 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.locksettings.recoverablekeystore.TestData;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.security.cert.CertificateException;
import java.util.ArrayList;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RecoverySnapshotStorageTest {
- private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT = new KeyChainSnapshot.Builder()
- .setCounterId(1)
- .setSnapshotVersion(1)
- .setServerParams(new byte[0])
- .setMaxAttempts(10)
- .setEncryptedRecoveryKeyBlob(new byte[0])
- .setKeyChainProtectionParams(new ArrayList<>())
- .setWrappedApplicationKeys(new ArrayList<>())
- .build();
+ private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
+ createMinimalKeyChainSnapshot();
private final RecoverySnapshotStorage mRecoverySnapshotStorage = new RecoverySnapshotStorage();
@@ -50,4 +46,21 @@
assertNull(mRecoverySnapshotStorage.get(1000));
}
+
+ private static KeyChainSnapshot createMinimalKeyChainSnapshot() {
+ try {
+ return new KeyChainSnapshot.Builder()
+ .setCounterId(1)
+ .setSnapshotVersion(1)
+ .setServerParams(new byte[0])
+ .setMaxAttempts(10)
+ .setEncryptedRecoveryKeyBlob(new byte[0])
+ .setKeyChainProtectionParams(new ArrayList<>())
+ .setWrappedApplicationKeys(new ArrayList<>())
+ .setTrustedHardwareCertPath(TestData.CERT_PATH_1)
+ .build();
+ } catch (CertificateException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index b645700..7f1bcac 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -30,8 +30,10 @@
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -436,6 +438,20 @@
}
@Test
+ public void testLayoutSeq_assignedDuringLayout() throws Exception {
+ synchronized (sWm.getWindowManagerLock()) {
+
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+ assertThat(win.mLayoutSeq, is(dc.mLayoutSeq));
+ }
+ }
+
+ @Test
@SuppressLint("InlinedApi")
public void testOrientationDefinedByKeyguard() {
final DisplayContent dc = createNewDisplay();
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 56b7d9f..1248eae 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -40,9 +40,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -312,6 +315,17 @@
assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
}
+ @Test
+ public void testLayoutSeqResetOnReparent() throws Exception {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ app.mLayoutSeq = 1;
+ mDisplayContent.mLayoutSeq = 1;
+
+ app.onDisplayChanged(mDisplayContent);
+
+ assertThat(app.mLayoutSeq, not(is(mDisplayContent.mLayoutSeq)));
+ }
+
private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
reset(mPowerManagerWrapper);
final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
new file mode 100644
index 0000000..d0f0fe3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.server.wm.utils;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class InsetUtilsTest {
+
+ @Test
+ public void testAdd() throws Exception {
+ final Rect rect1 = new Rect(10, 20, 30, 40);
+ final Rect rect2 = new Rect(50, 60, 70, 80);
+ InsetUtils.addInsets(rect1, rect2);
+ assertEquals(new Rect(60, 80, 100, 120), rect1);
+ }
+}
+
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index ef0780a..9f8042c 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -83,6 +83,7 @@
import com.android.server.LocalServices;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
@@ -181,6 +182,7 @@
private final UEventObserver mUEventObserver;
private static Set<Integer> sBlackListedInterfaces;
+ private HashMap<Long, FileDescriptor> mControlFds;
static {
sBlackListedInterfaces = new HashSet<>();
@@ -270,6 +272,18 @@
Slog.i(TAG, "USB GADGET HAL not present in the device", e);
}
+ mControlFds = new HashMap<>();
+ FileDescriptor mtpFd = nativeOpenControl(UsbManager.USB_FUNCTION_MTP);
+ if (mtpFd == null) {
+ Slog.e(TAG, "Failed to open control for mtp");
+ }
+ mControlFds.put(UsbManager.FUNCTION_MTP, mtpFd);
+ FileDescriptor ptpFd = nativeOpenControl(UsbManager.USB_FUNCTION_PTP);
+ if (mtpFd == null) {
+ Slog.e(TAG, "Failed to open control for mtp");
+ }
+ mControlFds.put(UsbManager.FUNCTION_PTP, ptpFd);
+
boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
if (secureAdbEnabled && !dataEncrypted) {
@@ -704,8 +718,7 @@
return false;
}
- protected void updateUsbStateBroadcastIfNeeded(long functions,
- boolean configChanged) {
+ protected void updateUsbStateBroadcastIfNeeded(long functions) {
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -716,7 +729,6 @@
intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
isUsbTransferAllowed() && isUsbDataTransferActive(mCurrentFunctions));
- intent.putExtra(UsbManager.USB_CONFIG_CHANGED, configChanged);
long remainingFunctions = functions;
while (remainingFunctions != 0) {
@@ -726,7 +738,7 @@
}
// send broadcast intent only if the USB state has changed
- if (!isUsbStateChanged(intent) && !configChanged) {
+ if (!isUsbStateChanged(intent)) {
if (DEBUG) {
Slog.d(TAG, "skip broadcasting " + intent + " extras: " + intent.getExtras());
}
@@ -798,8 +810,7 @@
updateUsbNotification(false);
updateAdbNotification(false);
if (mBootCompleted) {
- updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
- false);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
if ((mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) != 0) {
updateCurrentAccessory();
@@ -812,7 +823,7 @@
&& mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
setScreenUnlockedFunctions();
} else {
- setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled);
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
}
}
updateUsbFunctions();
@@ -847,8 +858,7 @@
updateUsbNotification(false);
if (mBootCompleted) {
if (mHostConnected || prevHostConnected) {
- updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
- false);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
} else {
mPendingBootBroadcast = true;
@@ -994,7 +1004,7 @@
protected void finishBoot() {
if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
if (mPendingBootBroadcast) {
- updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), false);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
mPendingBootBroadcast = false;
}
if (!mScreenLocked
@@ -1597,7 +1607,7 @@
/**
* Start up dependent services.
*/
- updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), true);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
if (!waitForState(oemFunctions)) {
@@ -1943,7 +1953,7 @@
if (mBootCompleted && isUsbDataTransferActive(functions)) {
// Start up dependent services.
- updateUsbStateBroadcastIfNeeded(functions, true);
+ updateUsbStateBroadcastIfNeeded(functions);
}
}
}
@@ -1979,6 +1989,22 @@
return mHandler.getEnabledFunctions();
}
+ /**
+ * Returns a dup of the control file descriptor for the given function.
+ */
+ public ParcelFileDescriptor getControlFd(long usbFunction) {
+ FileDescriptor fd = mControlFds.get(usbFunction);
+ if (fd == null) {
+ return null;
+ }
+ try {
+ return ParcelFileDescriptor.dup(fd);
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not dup fd for " + usbFunction);
+ return null;
+ }
+ }
+
public long getScreenUnlockedFunctions() {
return mHandler.getScreenUnlockedFunctions();
}
@@ -2063,6 +2089,8 @@
private native ParcelFileDescriptor nativeOpenAccessory();
+ private native FileDescriptor nativeOpenControl(String usbFunction);
+
private native boolean nativeIsStartRequested();
private native int nativeGetAudioMode();
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index c1a7591..e92bd74 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -304,6 +304,13 @@
return null;
}
+ /* Returns a dup of the control file descriptor for the given function. */
+ @Override
+ public ParcelFileDescriptor getControlFd(long function) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_MTP, null);
+ return mDeviceManager.getControlFd(function);
+ }
+
@Override
public void setDevicePackage(UsbDevice device, String packageName, int userId) {
device = Preconditions.checkNotNull(device);
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 08f8bb6..890a6ea 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -18,11 +18,14 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* CellIdentity represents the identity of a unique cell. This is the base class for
@@ -84,8 +87,16 @@
/** @hide */
protected final String mMncStr;
+ // long alpha Operator Name String or Enhanced Operator Name String
/** @hide */
- protected CellIdentity(String tag, int type, String mcc, String mnc) {
+ protected final String mAlphaLong;
+ // short alpha Operator Name String or Enhanced Operator Name String
+ /** @hide */
+ protected final String mAlphaShort;
+
+ /** @hide */
+ protected CellIdentity(String tag, int type, String mcc, String mnc, String alphal,
+ String alphas) {
mTag = tag;
mType = type;
@@ -113,6 +124,8 @@
mMncStr = null;
log("invalid MNC format: " + mnc);
}
+ mAlphaLong = alphal;
+ mAlphaShort = alphas;
}
/** Implement the Parcelable interface */
@@ -138,6 +151,40 @@
}
/**
+ * @return The long alpha tag associated with the current scan result (may be the operator
+ * name string or extended operator name string). May be null if unknown.
+ */
+ @Nullable
+ public CharSequence getOperatorAlphaLong() {
+ return mAlphaLong;
+ }
+
+ /**
+ * @return The short alpha tag associated with the current scan result (may be the operator
+ * name string or extended operator name string). May be null if unknown.
+ */
+ @Nullable
+ public CharSequence getOperatorAlphaShort() {
+ return mAlphaShort;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof CellIdentity)) {
+ return false;
+ }
+
+ CellIdentity o = (CellIdentity) other;
+ return TextUtils.equals(mAlphaLong, o.mAlphaLong)
+ && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAlphaLong, mAlphaShort, mMccStr, mMncStr, mType);
+ }
+
+ /**
* Used by child classes for parceling.
*
* @hide
@@ -147,6 +194,8 @@
dest.writeInt(type);
dest.writeString(mMccStr);
dest.writeString(mMncStr);
+ dest.writeString(mAlphaLong);
+ dest.writeString(mAlphaShort);
}
/**
@@ -154,7 +203,8 @@
* @hide
*/
protected CellIdentity(String tag, int type, Parcel source) {
- this(tag, type, source.readString(), source.readString());
+ this(tag, type, source.readString(), source.readString(),
+ source.readString(), source.readString());
}
/** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 713ac00..58a2c45 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -49,23 +49,17 @@
* to +90 degrees).
*/
private final int mLatitude;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
/**
* @hide
*/
public CellIdentityCdma() {
- super(TAG, TYPE_CDMA, null, null);
+ super(TAG, TYPE_CDMA, null, null, null, null);
mNetworkId = Integer.MAX_VALUE;
mSystemId = Integer.MAX_VALUE;
mBasestationId = Integer.MAX_VALUE;
mLongitude = Integer.MAX_VALUE;
mLatitude = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
@@ -100,7 +94,7 @@
*/
public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
String alphas) {
- super(TAG, TYPE_CDMA, null, null);
+ super(TAG, TYPE_CDMA, null, null, alphal, alphas);
mNetworkId = nid;
mSystemId = sid;
mBasestationId = bid;
@@ -110,8 +104,6 @@
} else {
mLongitude = mLatitude = Integer.MAX_VALUE;
}
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityCdma(CellIdentityCdma cid) {
@@ -178,28 +170,10 @@
return mLatitude;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
@Override
public int hashCode() {
return Objects.hash(mNetworkId, mSystemId, mBasestationId, mLatitude, mLongitude,
- mAlphaLong, mAlphaShort);
+ super.hashCode());
}
@Override
@@ -219,8 +193,7 @@
&& mBasestationId == o.mBasestationId
&& mLatitude == o.mLatitude
&& mLongitude == o.mLongitude
- && TextUtils.equals(mAlphaLong, o.mAlphaLong)
- && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ && super.equals(other);
}
@Override
@@ -246,8 +219,6 @@
dest.writeInt(mBasestationId);
dest.writeInt(mLongitude);
dest.writeInt(mLatitude);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -258,8 +229,6 @@
mBasestationId = in.readInt();
mLongitude = in.readInt();
mLatitude = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index aae7929..c697880 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -37,22 +37,16 @@
private final int mArfcn;
// 6-bit Base Station Identity Code
private final int mBsic;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
/**
* @hide
*/
public CellIdentityGsm() {
- super(TAG, TYPE_GSM, null, null);
+ super(TAG, TYPE_GSM, null, null, null, null);
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mArfcn = Integer.MAX_VALUE;
mBsic = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
* public constructor
@@ -98,16 +92,13 @@
*/
public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
String mncStr, String alphal, String alphas) {
- super(TAG, TYPE_GSM, mccStr, mncStr);
+ super(TAG, TYPE_GSM, mccStr, mncStr, alphal, alphas);
mLac = lac;
mCid = cid;
mArfcn = arfcn;
// In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
// for inbound parcels
mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
-
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityGsm(CellIdentityGsm cid) {
@@ -188,24 +179,6 @@
return mMncStr;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
/** @hide */
@Override
public int getChannelNumber() {
@@ -223,7 +196,7 @@
@Override
public int hashCode() {
- return Objects.hash(mMccStr, mMncStr, mLac, mCid, mAlphaLong, mAlphaShort);
+ return Objects.hash(mLac, mCid, super.hashCode());
}
@Override
@@ -243,8 +216,7 @@
&& mBsic == o.mBsic
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
- && TextUtils.equals(mAlphaLong, o.mAlphaLong)
- && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ && super.equals(other);
}
@Override
@@ -270,8 +242,6 @@
dest.writeInt(mCid);
dest.writeInt(mArfcn);
dest.writeInt(mBsic);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -281,8 +251,6 @@
mCid = in.readInt();
mArfcn = in.readInt();
mBsic = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 9b3ef56..177fced 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -37,10 +37,6 @@
private final int mTac;
// 18-bit Absolute RF Channel Number
private final int mEarfcn;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
// cell bandwidth, in kHz
private final int mBandwidth;
@@ -48,14 +44,12 @@
* @hide
*/
public CellIdentityLte() {
- super(TAG, TYPE_LTE, null, null);
+ super(TAG, TYPE_LTE, null, null, null, null);
mCi = Integer.MAX_VALUE;
mPci = Integer.MAX_VALUE;
mTac = Integer.MAX_VALUE;
mEarfcn = Integer.MAX_VALUE;
mBandwidth = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
@@ -105,14 +99,12 @@
*/
public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
String mncStr, String alphal, String alphas) {
- super(TAG, TYPE_LTE, mccStr, mncStr);
+ super(TAG, TYPE_LTE, mccStr, mncStr, alphal, alphas);
mCi = ci;
mPci = pci;
mTac = tac;
mEarfcn = earfcn;
mBandwidth = bandwidth;
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityLte(CellIdentityLte cid) {
@@ -198,24 +190,6 @@
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
/** @hide */
@Override
public int getChannelNumber() {
@@ -224,7 +198,7 @@
@Override
public int hashCode() {
- return Objects.hash(mMccStr, mMncStr, mCi, mPci, mTac, mAlphaLong, mAlphaShort);
+ return Objects.hash(mCi, mPci, mTac, super.hashCode());
}
@Override
@@ -245,8 +219,7 @@
&& mBandwidth == o.mBandwidth
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
- && TextUtils.equals(mAlphaLong, o.mAlphaLong)
- && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ && super.equals(other);
}
@Override
@@ -274,8 +247,6 @@
dest.writeInt(mTac);
dest.writeInt(mEarfcn);
dest.writeInt(mBandwidth);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -286,8 +257,6 @@
mTac = in.readInt();
mEarfcn = in.readInt();
mBandwidth = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 7475c74..18ab6d4 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -35,21 +35,15 @@
private final int mCid;
// 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
private final int mCpid;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
/**
* @hide
*/
public CellIdentityTdscdma() {
- super(TAG, TYPE_TDSCDMA, null, null);
+ super(TAG, TYPE_TDSCDMA, null, null, null, null);
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mCpid = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
@@ -76,12 +70,10 @@
* @hide
*/
public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
- super(TAG, TYPE_TDSCDMA, mcc, mnc);
+ super(TAG, TYPE_TDSCDMA, mcc, mnc, null, null);
mLac = lac;
mCid = cid;
mCpid = cpid;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
@@ -97,12 +89,10 @@
*/
public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
String alphal, String alphas) {
- super(TAG, TYPE_TDSCDMA, mcc, mnc);
+ super(TAG, TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
mLac = lac;
mCid = cid;
mCpid = cpid;
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityTdscdma(CellIdentityTdscdma cid) {
@@ -151,31 +141,9 @@
return mCpid;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- *
- * @hide
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- *
- * @hide
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
@Override
public int hashCode() {
- return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid, mAlphaLong, mAlphaShort);
+ return Objects.hash(mLac, mCid, mCpid, super.hashCode());
}
@Override
@@ -194,8 +162,7 @@
&& mLac == o.mLac
&& mCid == o.mCid
&& mCpid == o.mCpid
- && mAlphaLong == o.mAlphaLong
- && mAlphaShort == o.mAlphaShort;
+ && super.equals(other);
}
@Override
@@ -219,8 +186,6 @@
dest.writeInt(mLac);
dest.writeInt(mCid);
dest.writeInt(mCpid);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -229,8 +194,6 @@
mLac = in.readInt();
mCid = in.readInt();
mCpid = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 52fa54f..984483e 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -37,22 +37,16 @@
private final int mPsc;
// 16-bit UMTS Absolute RF Channel Number
private final int mUarfcn;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
/**
* @hide
*/
public CellIdentityWcdma() {
- super(TAG, TYPE_TDSCDMA, null, null);
+ super(TAG, TYPE_TDSCDMA, null, null, null, null);
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mPsc = Integer.MAX_VALUE;
mUarfcn = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
* public constructor
@@ -99,13 +93,11 @@
*/
public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
String mccStr, String mncStr, String alphal, String alphas) {
- super(TAG, TYPE_WCDMA, mccStr, mncStr);
+ super(TAG, TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
mLac = lac;
mCid = cid;
mPsc = psc;
mUarfcn = uarfcn;
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityWcdma(CellIdentityWcdma cid) {
@@ -179,27 +171,9 @@
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
@Override
public int hashCode() {
- return Objects.hash(mMccStr, mMncStr, mLac, mCid, mPsc, mAlphaLong, mAlphaShort);
+ return Objects.hash(mLac, mCid, mPsc, super.hashCode());
}
/**
@@ -232,8 +206,7 @@
&& mUarfcn == o.mUarfcn
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
- && TextUtils.equals(mAlphaLong, o.mAlphaLong)
- && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ && super.equals(other);
}
@Override
@@ -259,8 +232,6 @@
dest.writeInt(mCid);
dest.writeInt(mPsc);
dest.writeInt(mUarfcn);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -270,8 +241,6 @@
mCid = in.readInt();
mPsc = in.readInt();
mUarfcn = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}