summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PREUPLOAD.cfg2
-rw-r--r--api/current.txt52
-rw-r--r--api/removed.txt4
-rw-r--r--api/test-current.txt3
-rw-r--r--cmds/statsd/Android.mk4
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h13
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp14
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.h13
-rw-r--r--cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp7
-rw-r--r--cmds/statsd/src/anomaly/DurationAnomalyTracker.h7
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.cpp4
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h3
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp22
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h4
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h2
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h6
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp18
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h4
-rw-r--r--cmds/statsd/src/metrics/duration_helper/DurationTracker.h13
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp2
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h2
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp119
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h2
-rw-r--r--cmds/statsd/src/packages/UidMap.cpp68
-rw-r--r--cmds/statsd/src/packages/UidMap.h29
-rw-r--r--cmds/statsd/tests/StatsLogProcessor_test.cpp27
-rw-r--r--cmds/statsd/tests/UidMap_test.cpp29
-rw-r--r--cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp241
-rw-r--r--cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp486
-rw-r--r--cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp2
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp2
-rw-r--r--cmds/statsd/tests/metrics/OringDurationTracker_test.cpp99
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp4
-rw-r--r--config/hiddenapi-light-greylist.txt3
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java20
-rw-r--r--core/java/android/app/slice/SliceManager.java5
-rw-r--r--core/java/android/app/slice/SliceProvider.java66
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java61
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java83
-rw-r--r--core/java/android/content/pm/PackageManager.java1
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl6
-rw-r--r--core/java/android/hardware/usb/UsbManager.java27
-rw-r--r--core/java/android/net/TrafficStats.java32
-rw-r--r--core/java/android/os/Build.java2
-rw-r--r--core/java/android/os/FileUtils.java9
-rw-r--r--core/java/android/os/SystemClock.java2
-rw-r--r--core/java/android/provider/Settings.java10
-rw-r--r--core/java/android/security/keystore/recovery/KeyChainProtectionParams.java18
-rw-r--r--core/java/android/security/keystore/recovery/KeyChainSnapshot.java8
-rw-r--r--core/java/android/security/keystore/recovery/RecoveryController.java2
-rw-r--r--core/java/android/text/method/LinkMovementMethod.java11
-rw-r--r--core/java/android/util/DataUnit.java2
-rw-r--r--core/java/android/view/autofill/AutofillManager.java52
-rw-r--r--core/java/android/widget/Editor.java32
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java29
-rw-r--r--core/java/android/widget/TextView.java111
-rw-r--r--core/java/com/android/internal/os/Zygote.java17
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java2
-rw-r--r--core/java/com/android/server/NetworkManagementSocketTagger.java4
-rw-r--r--core/proto/android/server/jobscheduler.proto4
-rw-r--r--core/proto/android/server/powermanagerservice.proto132
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--core/tests/coretests/src/android/widget/TextViewTest.java79
-rw-r--r--data/keyboards/Android.mk2
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedImageDrawable.java16
-rw-r--r--media/java/android/media/ExifInterface.java31
-rw-r--r--media/java/android/media/MediaController2.java40
-rw-r--r--media/java/android/media/MediaItem2.java10
-rw-r--r--media/java/android/media/MediaLibraryService2.java10
-rw-r--r--media/java/android/media/MediaMetadata2.java15
-rw-r--r--media/java/android/media/MediaPlayerBase.java38
-rw-r--r--media/java/android/media/MediaPlaylistAgent.java5
-rw-r--r--media/java/android/media/MediaSession2.java534
-rw-r--r--media/java/android/media/MediaSessionService2.java7
-rw-r--r--media/java/android/media/Rating2.java35
-rw-r--r--media/java/android/media/SessionCommand2.java336
-rw-r--r--media/java/android/media/SessionCommandGroup2.java106
-rw-r--r--media/java/android/media/SessionToken2.java4
-rw-r--r--media/java/android/media/VolumeProvider2.java6
-rw-r--r--media/java/android/media/session/MediaSessionManager.java12
-rw-r--r--media/java/android/media/update/MediaController2Provider.java6
-rw-r--r--media/java/android/media/update/MediaSession2Provider.java24
-rw-r--r--media/java/android/media/update/StaticProvider.java53
-rw-r--r--media/java/android/media/update/TransportControlProvider.java2
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java4
-rw-r--r--media/java/android/mtp/MtpServer.java5
-rw-r--r--media/jni/android_mtp_MtpServer.cpp18
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java51
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java6
-rw-r--r--proto/src/metrics_constants.proto11
-rw-r--r--services/autofill/java/com/android/server/autofill/Helper.java7
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java108
-rw-r--r--services/autofill/java/com/android/server/autofill/ViewState.java2
-rw-r--r--services/core/java/com/android/server/PruneInstantAppsJobService.java11
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java29
-rw-r--r--services/core/java/com/android/server/am/RecentTasks.java4
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java28
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java5
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java20
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java4
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java7
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java125
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java413
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java3
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_UsbDeviceManager.cpp35
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java64
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java145
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java532
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java52
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java106
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java7
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java42
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java39
-rw-r--r--telephony/java/android/telephony/CellIdentity.java54
-rw-r--r--telephony/java/android/telephony/CellIdentityCdma.java39
-rw-r--r--telephony/java/android/telephony/CellIdentityGsm.java40
-rw-r--r--telephony/java/android/telephony/CellIdentityLte.java39
-rw-r--r--telephony/java/android/telephony/CellIdentityTdscdma.java47
-rw-r--r--telephony/java/android/telephony/CellIdentityWcdma.java39
-rw-r--r--telephony/java/android/telephony/LocationAccessPolicy.java20
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java28
-rw-r--r--tests/UsbTests/res/raw/usbdescriptors_massstorage.binbin0 -> 50 bytes
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java97
-rw-r--r--tools/stringslint/stringslint.py162
-rwxr-xr-xtools/stringslint/stringslint_sha.sh4
143 files changed, 4448 insertions, 1678 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2a67b7557f2c..6c3951da3883 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -9,3 +9,5 @@ checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPL
services/usb/
api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREUPLOAD_COMMIT}
+
+strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
diff --git a/api/current.txt b/api/current.txt
index 5e7fa591385e..be5e3e70d0a2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7289,6 +7289,7 @@ package android.app.slice {
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public android.app.PendingIntent onCreatePermissionRequest(android.net.Uri);
method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public void onSlicePinned(android.net.Uri);
@@ -24479,29 +24480,6 @@ package android.media {
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);
@@ -27277,6 +27255,7 @@ package android.net {
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();
@@ -27296,7 +27275,7 @@ package android.net {
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;
@@ -32057,7 +32036,6 @@ package android.os {
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;
@@ -33093,8 +33071,6 @@ package android.os {
}
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();
@@ -41675,6 +41651,8 @@ package android.telephony {
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;
}
@@ -41684,8 +41662,6 @@ package android.telephony {
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;
@@ -41701,8 +41677,6 @@ package android.telephony {
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;
@@ -41717,8 +41691,6 @@ package android.telephony {
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);
@@ -41743,8 +41715,6 @@ package android.telephony {
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);
@@ -45070,18 +45040,6 @@ package android.util {
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 1228fd18c71a..64a9c3f6666b 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -261,6 +261,10 @@ package android.net {
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 e84d204919f0..d32372517431 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -240,6 +240,7 @@ package android.content.pm {
public abstract class PackageManager {
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public abstract int getInstallReason(java.lang.String, android.os.UserHandle);
+ method public abstract java.lang.String[] getNamesForUids(int[]);
method public abstract java.lang.String getPermissionControllerPackageName();
method public abstract boolean isPermissionReviewModeEnabled();
field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
@@ -481,6 +482,7 @@ package android.net {
package android.os {
public static class Build.VERSION {
+ field public static final int FIRST_SDK_INT;
field public static final int RESOURCES_SDK_INT;
}
@@ -585,6 +587,7 @@ package android.provider {
field public static final java.lang.String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions";
field public static final java.lang.String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
field public static final java.lang.String LOW_POWER_MODE = "low_power";
+ field public static final java.lang.String LOW_POWER_MODE_STICKY = "low_power_sticky";
field public static final java.lang.String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 556709b686b0..1aef0c4c43c5 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -206,7 +206,9 @@ LOCAL_SRC_FILES := \
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 1be4dc5d58c7..a07a35587b11 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -69,6 +69,11 @@ public:
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 @@ private:
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 49de1ac417bc..f0960e3c8cc4 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -66,6 +66,9 @@ size_t AnomalyTracker::index(int64_t bucketNum) const {
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 @@ void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) {
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 @@ void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs,
}
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 d3da7dcf241b..ae0af64cfdd3 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -113,6 +113,13 @@ public:
}
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 @@ protected:
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 @@ protected:
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 79067eb982bf..cdc425124708 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -38,11 +38,10 @@ DurationAnomalyTracker::~DurationAnomalyTracker() {
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 92bb2bc1515b..53155d9a5240 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -52,6 +52,13 @@ public:
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/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 7a55f6065e88..ef637df68ed3 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -92,6 +92,10 @@ const int FIELD_ID_UID_MAP_BYTES_USED = 3;
const int FIELD_ID_UID_MAP_DROPPED_SNAPSHOTS = 4;
const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 5;
+const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
+ {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
+};
+
// TODO: add stats for pulled atoms.
StatsdStats::StatsdStats() {
mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index a4f64ddfd3a5..767588808d45 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -81,6 +81,9 @@ public:
const static int kDimensionKeySizeSoftLimit = 300;
const static int kDimensionKeySizeHardLimit = 500;
+ // Per atom dimension key size limit
+ static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap;
+
const static int kMaxConfigCount = 10;
const static int kMaxAlertCountPerConfig = 100;
const static int kMaxConditionCountPerConfig = 200;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 49034ac25c64..f08d54a9f41b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -17,9 +17,9 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
+#include "../guardrail/StatsdStats.h"
#include "GaugeMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_log_util.h"
+#include "../stats_log_util.h"
#include <cutils/log.h>
@@ -60,12 +60,20 @@ const int FIELD_ID_WALL_CLOCK_ATOM_TIMESTAMP = 5;
GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
const int conditionIndex,
- const sp<ConditionWizard>& wizard,
- const int pullTagId, const uint64_t startTimeNs,
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const uint64_t startTimeNs,
shared_ptr<StatsPullerManager> statsPullerManager)
: MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
mStatsPullerManager(statsPullerManager),
- mPullTagId(pullTagId) {
+ mPullTagId(pullTagId),
+ mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
+ StatsdStats::kAtomDimensionKeySizeLimitMap.end()
+ ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first
+ : StatsdStats::kDimensionKeySizeSoftLimit),
+ mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
+ StatsdStats::kAtomDimensionKeySizeLimitMap.end()
+ ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second
+ : StatsdStats::kDimensionKeySizeHardLimit) {
mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
int64_t bucketSizeMills = 0;
@@ -305,11 +313,11 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
return false;
}
// 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ if (mCurrentSlicedBucket->size() > mDimensionSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+ if (newTupleCount > mDimensionHardLimit) {
ALOGE("GaugeMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.toString().c_str());
return true;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index dd6aff4130de..4b543f808de2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -150,6 +150,10 @@ private:
static const size_t kBucketSize = sizeof(GaugeBucket{});
+ const size_t mDimensionSoftLimit;
+
+ const size_t mDimensionHardLimit;
+
FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index ea45f43a2d29..4983f96eff03 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -188,7 +188,7 @@ protected:
// 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 46a9b34c21a1..05ce84d7ea8f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -178,6 +178,12 @@ private:
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/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 767260d26351..ed4d7e4ffc9c 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -18,8 +18,8 @@
#include "Log.h"
#include "ValueMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_log_util.h"
+#include "../guardrail/StatsdStats.h"
+#include "../stats_log_util.h"
#include <cutils/log.h>
#include <limits.h>
@@ -68,7 +68,15 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric
: MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
mValueField(metric.value_field()),
mStatsPullerManager(statsPullerManager),
- mPullTagId(pullTagId) {
+ mPullTagId(pullTagId),
+ mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
+ StatsdStats::kAtomDimensionKeySizeLimitMap.end()
+ ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first
+ : StatsdStats::kDimensionKeySizeSoftLimit),
+ mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
+ StatsdStats::kAtomDimensionKeySizeLimitMap.end()
+ ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second
+ : StatsdStats::kDimensionKeySizeHardLimit) {
// TODO: valuemetric for pushed events may need unlimited bucket length
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
@@ -266,11 +274,11 @@ bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
return false;
}
- if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ if (mCurrentSlicedBucket.size() > mDimensionSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedBucket.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+ if (newTupleCount > mDimensionHardLimit) {
ALOGE("ValueMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.toString().c_str());
return true;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index be57183bd926..c4477b3cff4e 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -149,6 +149,10 @@ private:
static const size_t kBucketSize = sizeof(ValueBucket{});
+ const size_t mDimensionSoftLimit;
+
+ const size_t mDimensionHardLimit;
+
FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 991a76a0d34a..ddfb8cc0a5d3 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -109,7 +109,7 @@ public:
// 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 @@ public:
}
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 c9547cf4bf41..df9e6aea578b 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -313,7 +313,7 @@ void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, b
}
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 0452d373d413..32d42fad138e 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -57,7 +57,7 @@ public:
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 b418a85a045c..da79217567c7 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -166,13 +166,13 @@ bool OringDurationTracker::flushCurrentBucket(
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 @@ bool OringDurationTracker::flushCurrentBucket(
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 @@ void OringDurationTracker::onConditionChanged(bool condition, const uint64_t tim
}
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();
- // As we move into the future, old buckets get overwritten (so their old data is erased).
+ // 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);
-
- // How much of the threshold is still unaccounted after considering pastNs.
- int64_t leftNs = thresholdNs - pastNs;
-
- // First deal with the remainder of the current bucket.
- if (leftNs <= currRemainingBucketSizeNs) { // Predict the anomaly will occur in this bucket.
- return eventTimestampNs + leftNs;
+ int64_t pastNs = currentBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
+
+ // The refractory period end timestamp for dimension mEventKey.
+ const int64_t refractoryPeriodEndNs =
+ anomalyTracker.getRefractoryPeriodEndsSec(mEventKey) * NS_PER_SEC;
+
+ // 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 610e3ea79aa1..ca8abfe1e82d 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -56,7 +56,7 @@ public:
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/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 1cb20bc732d8..8c8152da51ec 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -116,32 +116,16 @@ void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
lock_guard<mutex> lock(mMutex); // Exclusively lock for updates.
mMap.clear();
- ProtoOutputStream proto;
- uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+ vector<const SnapshotPackageInfo> infos;
for (size_t j = 0; j < uid.size(); j++) {
string package = string(String8(packageName[j]).string());
mMap.insert(make_pair(uid[j], AppData(package, versionCode[j])));
- proto.write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, package);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, (int)versionCode[j]);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, (int)uid[j]);
+ infos.emplace_back(package, versionCode[j], uid[j]);
}
- proto.end(token);
-
- // Copy ProtoOutputStream output to
- auto iter = proto.data();
- size_t pos = 0;
- vector<char> outData(proto.size());
- while (iter.readBuffer() != NULL) {
- size_t toRead = iter.currentToRead();
- std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
- pos += toRead;
- iter.rp()->move(toRead);
- }
- SnapshotRecord record(timestamp, outData);
- mSnapshots.push_back(record);
- mBytesUsed += proto.size() + kBytesTimestampField;
+ mSnapshots.emplace_back(timestamp, infos);
+
+ mBytesUsed += mSnapshots.back().bytes;
ensureBytesUsedBelowLimit();
StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
StatsdStats::getInstance().setUidMapSnapshots(mSnapshots.size());
@@ -212,7 +196,7 @@ void UidMap::ensureBytesUsedBelowLimit() {
while (mBytesUsed > limit) {
ALOGI("Bytes used %zu is above limit %zu, need to delete something", mBytesUsed, limit);
if (mSnapshots.size() > 0) {
- mBytesUsed -= mSnapshots.front().bytes.size() + kBytesTimestampField;
+ mBytesUsed -= mSnapshots.front().bytes;
mSnapshots.pop_front();
StatsdStats::getInstance().noteUidMapDropped(1, 0);
} else if (mChanges.size() > 0) {
@@ -365,8 +349,14 @@ void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
count++;
proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP,
(long long)record.timestampNs);
- proto->write(FIELD_TYPE_MESSAGE | FIELD_ID_SNAPSHOT_PACKAGE_INFO, record.bytes.data(),
- record.bytes.size());
+ for (const SnapshotPackageInfo& info : record.infos) {
+ uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+ proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, info.package);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, info.version);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, info.uid);
+ proto->end(token);
+ }
proto->end(snapshotsToken);
}
}
@@ -380,7 +370,7 @@ void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
int64_t cutoff_nanos = newMin;
for (auto it_snapshots = mSnapshots.begin(); it_snapshots != mSnapshots.end();) {
if (it_snapshots->timestampNs < cutoff_nanos) {
- mBytesUsed -= it_snapshots->bytes.size() + kBytesTimestampField;
+ mBytesUsed -= it_snapshots->bytes;
it_snapshots = mSnapshots.erase(it_snapshots);
} else {
++it_snapshots;
@@ -399,31 +389,13 @@ void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
// Produce another snapshot. This results in extra data being uploaded but
// helps ensure we can re-construct the UID->app name, versionCode mapping
// in server.
- ProtoOutputStream snapshotProto;
- uint64_t token = snapshotProto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+ vector<const SnapshotPackageInfo> infos;
for (const auto& it : mMap) {
- snapshotProto.write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME,
- it.second.packageName);
- snapshotProto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
- (int)it.second.versionCode);
- snapshotProto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID,
- (int)it.first);
+ infos.emplace_back(it.second.packageName, it.second.versionCode, it.first);
}
- snapshotProto.end(token);
-
- // Copy ProtoOutputStream output to
- auto iter = snapshotProto.data();
- vector<char> snapshotData(snapshotProto.size());
- size_t pos = 0;
- while (iter.readBuffer() != NULL) {
- size_t toRead = iter.currentToRead();
- std::memcpy(&(snapshotData[pos]), iter.readBuffer(), toRead);
- pos += toRead;
- iter.rp()->move(toRead);
- }
- mSnapshots.emplace_back(timestamp, snapshotData);
- mBytesUsed += kBytesTimestampField + snapshotData.size();
+
+ mSnapshots.emplace_back(timestamp, infos);
+ mBytesUsed += mSnapshots.back().bytes;
}
}
StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 9dc73f4859c6..a3632d218724 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -74,17 +74,35 @@ const unsigned int kBytesChangeRecord = sizeof(struct ChangeRecord);
// less because of varint encoding).
const unsigned int kBytesTimestampField = 10;
+struct SnapshotPackageInfo {
+ const string package;
+ const int32_t version;
+ const int32_t uid;
+ SnapshotPackageInfo(const string& package, const int32_t version, const int32_t uid)
+ : package(package), version(version), uid(uid) {
+ }
+};
+
+const unsigned int kBytesSnapshotInfo = sizeof(struct SnapshotPackageInfo);
+
// When calling appendUidMap, we retrieve all the snapshots since the last
// timestamp we called appendUidMap for this configuration key.
struct SnapshotRecord {
const int64_t timestampNs;
- // For performance reasons, we convert the package_info field (which is a
- // repeated field of PackageInfo messages).
- vector<char> bytes;
+ // All the package info known.
+ vector<const SnapshotPackageInfo> infos;
+
+ // Tracks the number of bytes this snapshot consumes.
+ uint32_t bytes;
- SnapshotRecord(const int64_t timestampNs, vector<char> bytes)
- : timestampNs(timestampNs), bytes(bytes) {
+ SnapshotRecord(const int64_t timestampNs, vector<const SnapshotPackageInfo>& infos)
+ : timestampNs(timestampNs), infos(infos) {
+ bytes = 0;
+ for (auto info : infos) {
+ bytes += info.package.size() + kBytesSnapshotInfo;
+ }
+ bytes += kBytesTimestampField;
}
};
@@ -210,6 +228,7 @@ private:
// Allows unit-test to access private methods.
FRIEND_TEST(UidMapTest, TestClearingOutput);
+ FRIEND_TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot);
FRIEND_TEST(UidMapTest, TestMemoryComputed);
FRIEND_TEST(UidMapTest, TestMemoryGuardrail);
};
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index efed42e2613c..4b9a87d78817 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -14,6 +14,7 @@
#include "StatsLogProcessor.h"
#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
@@ -122,6 +123,32 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
EXPECT_EQ(0, broadcastCount);
}
+TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) {
+ // Setup simple config key corresponding to empty config.
+ sp<UidMap> m = new UidMap();
+ m->updateMap({1, 2}, {1, 2}, {String16("p1"), String16("p2")});
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
+ int broadcastCount = 0;
+ StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+ ConfigKey key(3, 4);
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT");
+ p.OnConfigUpdated(key, config);
+
+ // Expect to get no metrics, but snapshot specified above in uidmap.
+ vector<uint8_t> bytes;
+ p.onDumpReport(key, 1, &bytes);
+
+ ConfigMetricsReportList output;
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_TRUE(output.reports_size() > 0);
+ auto uidmap = output.reports(0).uid_map();
+ EXPECT_TRUE(uidmap.snapshots_size() > 0);
+ EXPECT_EQ(2, uidmap.snapshots(0).package_info_size());
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index c9492eb609f9..a9b67e01d88c 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -173,6 +173,33 @@ static void protoOutputStreamToUidMapping(ProtoOutputStream* proto, UidMapping*
results->ParseFromArray(bytes.data(), bytes.size());
}
+// Test that uid map returns at least one snapshot even if we already obtained
+// this snapshot from a previous call to getData.
+TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot) {
+ UidMap m;
+ // Initialize single config key.
+ ConfigKey config1(1, StringToId("config1"));
+ m.OnConfigUpdated(config1);
+ vector<int32_t> uids;
+ vector<int64_t> versions;
+ vector<String16> apps;
+ uids.push_back(1000);
+ apps.push_back(String16(kApp2.c_str()));
+ versions.push_back(5);
+ m.updateMap(1, uids, versions, apps);
+
+ // Set the last timestamp for this config key to be newer.
+ m.mLastUpdatePerConfigKey[config1] = 2;
+
+ ProtoOutputStream proto;
+ m.appendUidMap(3, config1, &proto);
+
+ // Check there's still a uidmap attached this one.
+ UidMapping results;
+ protoOutputStreamToUidMapping(&proto, &results);
+ EXPECT_EQ(1, results.snapshots_size());
+}
+
TEST(UidMapTest, TestClearingOutput) {
UidMap m;
@@ -199,7 +226,7 @@ TEST(UidMapTest, TestClearingOutput) {
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
- // It should be cleared now
+ // We have to keep at least one snapshot in memory at all times.
EXPECT_EQ(1U, m.mSnapshots.size());
proto.clear();
m.appendUidMap(2, config1, &proto);
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 000000000000..93ecde50955b
--- /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 000000000000..e924b0303946
--- /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 2287c2bfc890..c2334d8a3bed 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 @@ StatsdConfig CreateDurationMetricConfig_NoLink_CombinationCondition(
} // 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 5ef84e6ac6ce..a75d6c81e9e9 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -40,7 +40,7 @@ namespace statsd {
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 9b27f3cde3bd..13cdb0b7cb20 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -44,7 +44,7 @@ const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
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 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
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 0f785dff8e81..ce44a35cbf21 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -447,7 +447,9 @@ std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
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 8a9c73820520..0afc1cec7606 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -330,8 +330,6 @@ Landroid/app/StatusBarManager;->getService()Lcom/android/internal/statusbar/ISta
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;->getFsUuid()Ljava/lang/String;
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 4cb7f89cec5c..3015398e97ee 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8610,6 +8610,7 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
@UserProvisioningState
public int getUserProvisioningState() {
throwIfParentInstance("getUserProvisioningState");
@@ -8754,6 +8755,7 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean isDeviceProvisioned() {
try {
return mService.isDeviceProvisioned();
@@ -9418,7 +9420,21 @@ public class DevicePolicyManager {
* <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 @@ public class DevicePolicyManager {
* {@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/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 67a72ec33fd8..1afe53d8a7b5 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -32,9 +32,11 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -392,6 +394,9 @@ public class SliceManager {
*/
public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
try {
+ if (UserHandle.isSameApp(uid, Process.myUid())) {
+ return;
+ }
if (pkg == null) {
throw new SecurityException("No pkg specified");
}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index dd892937be71..bf856b74f067 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -37,7 +37,6 @@ import android.os.Handler;
import android.os.Process;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -146,18 +145,6 @@ public abstract class SliceProvider extends ContentProvider {
* @hide
*/
public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
- /**
- * @hide
- */
- public static final String EXTRA_OVERRIDE_PKG = "override_pkg";
- /**
- * @hide
- */
- public static final String EXTRA_OVERRIDE_UID = "override_uid";
- /**
- * @hide
- */
- public static final String EXTRA_OVERRIDE_PID = "override_pid";
private static final boolean DEBUG = false;
@@ -257,6 +244,23 @@ public abstract class SliceProvider extends ContentProvider {
"This provider has not implemented intent to uri mapping");
}
+ /**
+ * Called when an app requests a slice it does not have write permission
+ * to the uri for.
+ * <p>
+ * The return value will be the action on a slice that prompts the user that
+ * the calling app wants to show slices from this app. The default implementation
+ * launches a dialog that allows the user to grant access to this slice. Apps
+ * that do not want to allow this user grant, can override this and instead
+ * launch their own dialog with different behavior.
+ *
+ * @param sliceUri the Uri of the slice attempting to be bound.
+ * @see #getCallingPackage()
+ */
+ public @NonNull PendingIntent onCreatePermissionRequest(Uri sliceUri) {
+ return createPermissionIntent(getContext(), sliceUri, getCallingPackage());
+ }
+
@Override
public final int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
@@ -312,17 +316,7 @@ public abstract class SliceProvider extends ContentProvider {
String callingPackage = getCallingPackage();
int callingUid = Binder.getCallingUid();
int callingPid = Binder.getCallingPid();
- if (extras.containsKey(EXTRA_OVERRIDE_PKG)) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Only the system can override calling pkg");
- }
- // This is safe because we would grant SYSTEM_UID access to all slices
- // and want to allow it to bind slices as if it were a less privileged app
- // to check their permission levels.
- callingPackage = extras.getString(EXTRA_OVERRIDE_PKG);
- callingUid = extras.getInt(EXTRA_OVERRIDE_UID);
- callingPid = extras.getInt(EXTRA_OVERRIDE_PID);
- }
+
Slice s = handleBindSlice(uri, supportedSpecs, callingPackage, callingUid, callingPid);
Bundle b = new Bundle();
b.putParcelable(EXTRA_SLICE, s);
@@ -406,13 +400,11 @@ public abstract class SliceProvider extends ContentProvider {
// SliceManager#bindSlice.
String pkg = callingPkg != null ? callingPkg
: getContext().getPackageManager().getNameForUid(callingUid);
- if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
- try {
- mSliceManager.enforceSlicePermission(sliceUri, pkg,
- callingPid, callingUid);
- } catch (SecurityException e) {
- return createPermissionSlice(getContext(), sliceUri, pkg);
- }
+ try {
+ mSliceManager.enforceSlicePermission(sliceUri, pkg,
+ callingPid, callingUid);
+ } catch (SecurityException e) {
+ return createPermissionSlice(getContext(), sliceUri, pkg);
}
mCallback = "onBindSlice";
Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
@@ -426,10 +418,18 @@ public abstract class SliceProvider extends ContentProvider {
/**
* @hide
*/
- public static Slice createPermissionSlice(Context context, Uri sliceUri,
+ public Slice createPermissionSlice(Context context, Uri sliceUri,
String callingPackage) {
+ PendingIntent action;
+ mCallback = "onCreatePermissionRequest";
+ Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+ try {
+ action = onCreatePermissionRequest(sliceUri);
+ } finally {
+ Handler.getMain().removeCallbacks(mAnr);
+ }
return new Slice.Builder(sliceUri)
- .addAction(createPermissionIntent(context, sliceUri, callingPackage),
+ .addAction(action,
new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
.addText(getPermissionString(context, callingPackage), null,
Collections.emptyList())
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 656188fbdfb8..6aeb94da1175 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -38,7 +38,7 @@ public interface BluetoothProfile {
* This extra represents the current connection state of the profile of the
* Bluetooth device.
*/
- public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
+ String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
/**
* Extra for the connection state intents of the individual profiles.
@@ -46,123 +46,130 @@ public interface BluetoothProfile {
* This extra represents the previous connection state of the profile of the
* Bluetooth device.
*/
- public static final String EXTRA_PREVIOUS_STATE =
+ String EXTRA_PREVIOUS_STATE =
"android.bluetooth.profile.extra.PREVIOUS_STATE";
/** The profile is in disconnected state */
- public static final int STATE_DISCONNECTED = 0;
+ int STATE_DISCONNECTED = 0;
/** The profile is in connecting state */
- public static final int STATE_CONNECTING = 1;
+ int STATE_CONNECTING = 1;
/** The profile is in connected state */
- public static final int STATE_CONNECTED = 2;
+ int STATE_CONNECTED = 2;
/** The profile is in disconnecting state */
- public static final int STATE_DISCONNECTING = 3;
+ int STATE_DISCONNECTING = 3;
/**
* Headset and Handsfree profile
*/
- public static final int HEADSET = 1;
+ int HEADSET = 1;
/**
* A2DP profile.
*/
- public static final int A2DP = 2;
+ int A2DP = 2;
/**
* Health Profile
*/
- public static final int HEALTH = 3;
+ int HEALTH = 3;
/**
* HID Host
*
* @hide
*/
- public static final int HID_HOST = 4;
+ int HID_HOST = 4;
/**
* PAN Profile
*
* @hide
*/
- public static final int PAN = 5;
+ int PAN = 5;
/**
* PBAP
*
* @hide
*/
- public static final int PBAP = 6;
+ int PBAP = 6;
/**
* GATT
*/
- public static final int GATT = 7;
+ int GATT = 7;
/**
* GATT_SERVER
*/
- public static final int GATT_SERVER = 8;
+ int GATT_SERVER = 8;
/**
* MAP Profile
*
* @hide
*/
- public static final int MAP = 9;
+ int MAP = 9;
/*
* SAP Profile
* @hide
*/
- public static final int SAP = 10;
+ int SAP = 10;
/**
* A2DP Sink Profile
*
* @hide
*/
- public static final int A2DP_SINK = 11;
+ int A2DP_SINK = 11;
/**
* AVRCP Controller Profile
*
* @hide
*/
- public static final int AVRCP_CONTROLLER = 12;
+ int AVRCP_CONTROLLER = 12;
+
+ /**
+ * AVRCP Target Profile
+ *
+ * @hide
+ */
+ int AVRCP = 13;
/**
* Headset Client - HFP HF Role
*
* @hide
*/
- public static final int HEADSET_CLIENT = 16;
+ int HEADSET_CLIENT = 16;
/**
* PBAP Client
*
* @hide
*/
- public static final int PBAP_CLIENT = 17;
+ int PBAP_CLIENT = 17;
/**
* MAP Messaging Client Equipment (MCE)
*
* @hide
*/
- public static final int MAP_CLIENT = 18;
+ int MAP_CLIENT = 18;
/**
* HID Device
*/
- public static final int HID_DEVICE = 19;
+ int HID_DEVICE = 19;
/**
* Object Push Profile (OPP)
*
* @hide
*/
- public static final int OPP = 20;
+ int OPP = 20;
/**
* Hearing Aid Device
@@ -185,7 +192,7 @@ public interface BluetoothProfile {
*
* @hide
**/
- public static final int PRIORITY_AUTO_CONNECT = 1000;
+ int PRIORITY_AUTO_CONNECT = 1000;
/**
* Default priority for devices that allow incoming
@@ -194,7 +201,7 @@ public interface BluetoothProfile {
* @hide
**/
@SystemApi
- public static final int PRIORITY_ON = 100;
+ int PRIORITY_ON = 100;
/**
* Default priority for devices that does not allow incoming
@@ -203,14 +210,14 @@ public interface BluetoothProfile {
* @hide
**/
@SystemApi
- public static final int PRIORITY_OFF = 0;
+ int PRIORITY_OFF = 0;
/**
* Default priority when not set or when the device is unpaired
*
* @hide
*/
- public static final int PRIORITY_UNDEFINED = -1;
+ int PRIORITY_UNDEFINED = -1;
/**
* Get connected devices for this specific profile.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 387a836e6961..e85058df8250 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1101,6 +1101,58 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
/** @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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
appComponentFactory = orig.appComponentFactory;
compileSdkVersion = orig.compileSdkVersion;
compileSdkVersionCodename = orig.compileSdkVersionCodename;
+ mHiddenApiPolicy = orig.mHiddenApiPolicy;
}
public String toString() {
@@ -1459,6 +1512,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(compileSdkVersion);
dest.writeString(compileSdkVersionCodename);
dest.writeString(appComponentFactory);
+ dest.writeInt(mHiddenApiPolicy);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1529,6 +1583,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
compileSdkVersion = source.readInt();
compileSdkVersionCodename = source.readString();
appComponentFactory = source.readString();
+ mHiddenApiPolicy = source.readInt();
}
/**
@@ -1599,13 +1654,31 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
}
+ 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/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 114c485e08cd..314eb985a3e1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3711,6 +3711,7 @@ public abstract class PackageManager {
*
* @hide
*/
+ @TestApi
public abstract @Nullable String[] getNamesForUids(int[] uids);
/**
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 91bbdc7dde80..6d9c913dfab7 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -105,6 +105,12 @@ interface IUsbManager
/* 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 572c58564c8d..46142e35038d 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -192,14 +192,6 @@ public class UsbManager {
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 @@ public class UsbManager {
}
/**
+ * 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 7922276edb75..40d53b741d6b 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 @@ public class TrafficStats {
/**
* 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 df6ce8e85601..e22c65fee4cf 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -242,7 +242,9 @@ public class Build {
* 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 1160415ce212..88d6e847b644 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -411,6 +411,9 @@ public class FileUtils {
checkpoint = 0;
}
}
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
return progress;
}
@@ -440,6 +443,9 @@ public class FileUtils {
checkpoint = 0;
}
}
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
return progress;
}
@@ -479,6 +485,9 @@ public class FileUtils {
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 0f70427e6dc0..b254166303bc 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -269,6 +269,7 @@ public final class SystemClock {
* 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 @@ public final class SystemClock {
* 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 a013d3d66d3b..b58e5b3b7981 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11004,13 +11004,21 @@ public final class Settings {
public static final String SHOW_PROCESSES = "show_processes";
/**
- * If 1 low power mode is enabled.
+ * If 1 low power mode (aka battery saver) is enabled.
* @hide
*/
@TestApi
public static final String LOW_POWER_MODE = "low_power";
/**
+ * If 1, battery saver ({@link #LOW_POWER_MODE}) will be re-activated after the device
+ * is unplugged from a charger or rebooted.
+ * @hide
+ */
+ @TestApi
+ public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
+
+ /**
* Battery level [1-100] at which low power mode automatically turns on.
* If 0, it will not automatically turn on.
* @hide
diff --git a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
index d42424e61030..2a66206e805e 100644
--- a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -94,21 +94,6 @@ public final class KeyChainProtectionParams implements Parcelable {
private KeyDerivationParams mKeyDerivationParams;
private byte[] mSecret; // Derived from user secret. The field must have limited visibility.
- /**
- * @param secret Constructor creates a reference to the secret. Caller must use
- * @link {#clearSecret} to overwrite its value in memory.
- * @hide
- */
- public KeyChainProtectionParams(@UserSecretType int userSecretType,
- @LockScreenUiFormat int lockScreenUiFormat,
- @NonNull KeyDerivationParams keyDerivationParams,
- @NonNull byte[] secret) {
- mUserSecretType = userSecretType;
- mLockScreenUiFormat = lockScreenUiFormat;
- mKeyDerivationParams = Preconditions.checkNotNull(keyDerivationParams);
- mSecret = Preconditions.checkNotNull(secret);
- }
-
private KeyChainProtectionParams() {
}
@@ -158,6 +143,7 @@ public final class KeyChainProtectionParams implements Parcelable {
/**
* Sets user secret type.
+ * Default value is {@link TYPE_LOCKSCREEN}.
*
* @see TYPE_LOCKSCREEN
* @param userSecretType The secret type
@@ -185,7 +171,7 @@ public final class KeyChainProtectionParams implements Parcelable {
/**
* Sets parameters of the key derivation function.
*
- * @param keyDerivationParams Key derivation Params
+ * @param keyDerivationParams Key derivation parameters
* @return This builder.
*/
public Builder setKeyDerivationParams(@NonNull KeyDerivationParams
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index ccb627e1181d..24ff182ab4bd 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -252,12 +252,12 @@ public final class KeyChainSnapshot implements Parcelable {
/**
* Sets UI and key derivation parameters
*
- * @param recoveryMetadata The UI and key derivation parameters
+ * @param keyChainProtectionParams The UI and key derivation parameters
* @return This builder.
*/
public Builder setKeyChainProtectionParams(
- @NonNull List<KeyChainProtectionParams> recoveryMetadata) {
- mInstance.mKeyChainProtectionParams = recoveryMetadata;
+ @NonNull List<KeyChainProtectionParams> keyChainProtectionParams) {
+ mInstance.mKeyChainProtectionParams = keyChainProtectionParams;
return this;
}
@@ -292,7 +292,7 @@ public final class KeyChainSnapshot implements Parcelable {
*/
@NonNull public KeyChainSnapshot build() {
Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams,
- "recoveryMetadata");
+ "keyChainProtectionParams");
Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
"entryRecoveryData");
Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 61b4dd883b58..ab52d32ce963 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -159,7 +159,7 @@ public class RecoveryController {
* Gets a new instance of the class.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public static RecoveryController getInstance(Context context) {
+ @NonNull public static RecoveryController getInstance(@NonNull Context context) {
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
return new RecoveryController(lockSettings, KeyStore.getInstance());
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index 31ed549260a0..f33235889388 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.os.Build;
import android.text.Layout;
import android.text.NoCopySpan;
import android.text.Selection;
@@ -35,6 +36,8 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
private static final int UP = 2;
private static final int DOWN = 3;
+ private static final int HIDE_FLOATING_TOOLBAR_DELAY_MS = 200;
+
@Override
public boolean canSelectArbitrarily() {
return true;
@@ -65,7 +68,7 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
return super.up(widget, buffer);
}
-
+
@Override
protected boolean down(TextView widget, Spannable buffer) {
if (action(DOWN, widget, buffer)) {
@@ -215,6 +218,12 @@ public class LinkMovementMethod extends ScrollingMovementMethod {
if (action == MotionEvent.ACTION_UP) {
links[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
+ if (widget.getContext().getApplicationInfo().targetSdkVersion
+ > Build.VERSION_CODES.O_MR1) {
+ // Selection change will reposition the toolbar. Hide it for a few ms for a
+ // smoother transition.
+ widget.hideFloatingToolbar(HIDE_FLOATING_TOOLBAR_DELAY_MS);
+ }
Selection.setSelection(buffer,
buffer.getSpanStart(links[0]),
buffer.getSpanEnd(links[0]));
diff --git a/core/java/android/util/DataUnit.java b/core/java/android/util/DataUnit.java
index ea4266ecb790..cf045b8aec06 100644
--- a/core/java/android/util/DataUnit.java
+++ b/core/java/android/util/DataUnit.java
@@ -29,6 +29,8 @@ import java.util.concurrent.TimeUnit;
* "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 5bee87c2af2a..01fd09031d1e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -274,6 +274,16 @@ public final class AutofillManager {
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 @@ public final class AutofillManager {
* 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 @@ public final class AutofillManager {
@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/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 92285c77e61c..3775835aa6d8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -289,6 +289,7 @@ public class Editor {
boolean mShowSoftInputOnFocus = true;
private boolean mPreserveSelection;
private boolean mRestartActionModeOnNextRefresh;
+ private boolean mRequestingLinkActionMode;
private SelectionActionModeHelper mSelectionActionModeHelper;
@@ -2127,6 +2128,7 @@ public class Editor {
return;
}
stopTextActionMode();
+ mRequestingLinkActionMode = true;
getSelectionActionModeHelper().startLinkActionModeAsync(start, end);
}
@@ -2212,7 +2214,9 @@ public class Editor {
mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
final boolean selectionStarted = mTextActionMode != null;
- if (selectionStarted && !mTextView.isTextSelectable() && mShowSoftInputOnFocus) {
+ if (selectionStarted
+ && mTextView.isTextEditable() && !mTextView.isTextSelectable()
+ && mShowSoftInputOnFocus) {
// Show the IME to be able to replace text, except when selecting non editable text.
final InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
@@ -2322,10 +2326,14 @@ public class Editor {
if (!selectAllGotFocus && text.length() > 0) {
// Move cursor
final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
- Selection.setSelection((Spannable) text, offset);
- if (mSpellChecker != null) {
- // When the cursor moves, the word that was typed may need spell check
- mSpellChecker.onSelectionChanged();
+
+ final boolean shouldInsertCursor = !mRequestingLinkActionMode;
+ if (shouldInsertCursor) {
+ Selection.setSelection((Spannable) text, offset);
+ if (mSpellChecker != null) {
+ // When the cursor moves, the word that was typed may need spell check
+ mSpellChecker.onSelectionChanged();
+ }
}
if (!extractedTextModeWillBeStarted()) {
@@ -2335,16 +2343,17 @@ public class Editor {
mTextView.removeCallbacks(mInsertionActionModeRunnable);
}
- mShowSuggestionRunnable = new Runnable() {
- public void run() {
- replace();
- }
- };
+ mShowSuggestionRunnable = this::replace;
+
// removeCallbacks is performed on every touch
mTextView.postDelayed(mShowSuggestionRunnable,
ViewConfiguration.getDoubleTapTimeout());
} else if (hasInsertionController()) {
- getInsertionController().show();
+ if (shouldInsertCursor) {
+ getInsertionController().show();
+ } else {
+ getInsertionController().hide();
+ }
}
}
}
@@ -4170,6 +4179,7 @@ public class Editor {
}
mAssistClickHandlers.clear();
+ mRequestingLinkActionMode = false;
}
@Override
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6e855ba3f8f7..05204d03f60a 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -71,7 +71,7 @@ public final class SelectionActionModeHelper {
private final TextClassificationHelper mTextClassificationHelper;
private final TextClassificationConstants mTextClassificationSettings;
- private TextClassification mTextClassification;
+ @Nullable private TextClassification mTextClassification;
private AsyncTask mTextClassificationAsyncTask;
private final SelectionTracker mSelectionTracker;
@@ -124,7 +124,8 @@ public final class SelectionActionModeHelper {
: mTextClassificationHelper::classifyText,
mSmartSelectSprite != null
? this::startSelectionActionModeWithSmartSelectAnimation
- : this::startSelectionActionMode)
+ : this::startSelectionActionMode,
+ mTextClassificationHelper::getOriginalSelection)
.execute();
}
}
@@ -143,7 +144,8 @@ public final class SelectionActionModeHelper {
mTextView,
mTextClassificationHelper.getTimeoutDuration(),
mTextClassificationHelper::classifyText,
- this::startLinkActionMode)
+ this::startLinkActionMode,
+ mTextClassificationHelper::getOriginalSelection)
.execute();
}
}
@@ -158,7 +160,8 @@ public final class SelectionActionModeHelper {
mTextView,
mTextClassificationHelper.getTimeoutDuration(),
mTextClassificationHelper::classifyText,
- this::invalidateActionMode)
+ this::invalidateActionMode,
+ mTextClassificationHelper::getOriginalSelection)
.execute();
}
}
@@ -822,6 +825,7 @@ public final class SelectionActionModeHelper {
private final int mTimeOutDuration;
private final Supplier<SelectionResult> mSelectionResultSupplier;
private final Consumer<SelectionResult> mSelectionResultCallback;
+ private final Supplier<SelectionResult> mTimeOutResultSupplier;
private final TextView mTextView;
private final String mOriginalText;
@@ -830,16 +834,19 @@ public final class SelectionActionModeHelper {
* @param timeOut time in milliseconds to timeout the query if it has not completed
* @param selectionResultSupplier fetches the selection results. Runs on a background thread
* @param selectionResultCallback receives the selection results. Runs on the UiThread
+ * @param timeOutResultSupplier default result if the task times out
*/
TextClassificationAsyncTask(
@NonNull TextView textView, int timeOut,
@NonNull Supplier<SelectionResult> selectionResultSupplier,
- @NonNull Consumer<SelectionResult> selectionResultCallback) {
+ @NonNull Consumer<SelectionResult> selectionResultCallback,
+ @NonNull Supplier<SelectionResult> timeOutResultSupplier) {
super(textView != null ? textView.getHandler() : null);
mTextView = Preconditions.checkNotNull(textView);
mTimeOutDuration = timeOut;
mSelectionResultSupplier = Preconditions.checkNotNull(selectionResultSupplier);
mSelectionResultCallback = Preconditions.checkNotNull(selectionResultCallback);
+ mTimeOutResultSupplier = Preconditions.checkNotNull(timeOutResultSupplier);
// Make a copy of the original text.
mOriginalText = getText(mTextView).toString();
}
@@ -863,7 +870,7 @@ public final class SelectionActionModeHelper {
private void onTimeOut() {
if (getStatus() == Status.RUNNING) {
- onPostExecute(null);
+ onPostExecute(mTimeOutResultSupplier.get());
}
cancel(true);
}
@@ -963,6 +970,10 @@ public final class SelectionActionModeHelper {
return performClassification(selection);
}
+ public SelectionResult getOriginalSelection() {
+ return new SelectionResult(mSelectionStart, mSelectionEnd, null, null);
+ }
+
/**
* Maximum time (in milliseconds) to wait for a textclassifier result before timing out.
*/
@@ -1025,14 +1036,14 @@ public final class SelectionActionModeHelper {
private static final class SelectionResult {
private final int mStart;
private final int mEnd;
- private final TextClassification mClassification;
+ @Nullable private final TextClassification mClassification;
@Nullable private final TextSelection mSelection;
SelectionResult(int start, int end,
- TextClassification classification, @Nullable TextSelection selection) {
+ @Nullable TextClassification classification, @Nullable TextSelection selection) {
mStart = start;
mEnd = end;
- mClassification = Preconditions.checkNotNull(classification);
+ mClassification = classification;
mSelection = selection;
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index c366a9129d34..7b1acb15cc15 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -644,8 +644,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
private Layout mSavedMarqueeModeLayout;
+ // Do not update following mText/mSpannable/mPrecomputed except for setTextInternal()
@ViewDebug.ExportedProperty(category = "text")
- private CharSequence mText;
+ private @Nullable CharSequence mText;
+ private @Nullable Spannable mSpannable;
+ private @Nullable PrecomputedText mPrecomputed;
+
private CharSequence mTransformed;
private BufferType mBufferType = BufferType.NORMAL;
@@ -874,7 +878,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
- mText = "";
+ setTextInternal("");
final Resources res = getResources();
final CompatibilityInfo compat = res.getCompatibilityInfo();
@@ -1615,6 +1619,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ // Update mText and mPrecomputed
+ private void setTextInternal(@Nullable CharSequence text) {
+ mText = text;
+ mSpannable = (text instanceof Spannable) ? (Spannable) text : null;
+ mPrecomputed = (text instanceof PrecomputedText) ? (PrecomputedText) text : null;
+ }
+
/**
* Specify whether this widget should automatically scale the text to try to perfectly fit
* within the layout bounds by using the default auto-size configuration.
@@ -1973,9 +1984,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
}
- } else if (mText instanceof Spannable) {
+ } else if (mSpannable != null) {
// Reset the selection.
- Selection.setSelection((Spannable) mText, getSelectionEnd());
+ Selection.setSelection(mSpannable, getSelectionEnd());
}
}
}
@@ -2359,7 +2370,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mMovement != movement) {
mMovement = movement;
- if (movement != null && !(mText instanceof Spannable)) {
+ if (movement != null && mSpannable == null) {
setText(mText);
}
@@ -2409,8 +2420,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return;
}
if (mTransformation != null) {
- if (mText instanceof Spannable) {
- ((Spannable) mText).removeSpan(mTransformation);
+ if (mSpannable != null) {
+ mSpannable.removeSpan(mTransformation);
}
}
@@ -5254,7 +5265,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
((Editable) mText).append(text, start, end);
if (mAutoLinkMask != 0) {
- boolean linksWereAdded = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
+ boolean linksWereAdded = Linkify.addLinks(mSpannable, mAutoLinkMask);
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
if (linksWereAdded && mLinksClickable && !textCanBeSelected()) {
@@ -5413,7 +5424,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (ss.selStart >= 0 && ss.selEnd >= 0) {
- if (mText instanceof Spannable) {
+ if (mSpannable != null) {
int len = mText.length();
if (ss.selStart > len || ss.selEnd > len) {
@@ -5426,7 +5437,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd
+ " out of range for " + restored + "text " + mText);
} else {
- Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);
+ Selection.setSelection(mSpannable, ss.selStart, ss.selEnd);
if (ss.frozenWithFocus) {
createEditorIfNeeded();
@@ -5688,7 +5699,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* movement method, because setMovementMethod() may call
* setText() again to try to upgrade the buffer type.
*/
- mText = text;
+ setTextInternal(text);
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
@@ -5699,7 +5710,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
mBufferType = type;
- mText = text;
+ setTextInternal(text);
if (mTransformation == null) {
mTransformed = text;
@@ -5825,8 +5836,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setText(text, type);
if (start >= 0 || end >= 0) {
- if (mText instanceof Spannable) {
- Selection.setSelection((Spannable) mText,
+ if (mSpannable != null) {
+ Selection.setSelection(mSpannable,
Math.max(0, Math.min(start, len)),
Math.max(0, Math.min(end, len)));
}
@@ -6020,7 +6031,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (!isSuggestionsEnabled()) {
- mText = removeSuggestionSpans(mText);
+ setTextInternal(removeSuggestionSpans(mText));
}
InputMethodManager imm = InputMethodManager.peekInstance();
@@ -6948,8 +6959,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public boolean hasOverlappingRendering() {
// horizontal fading edge causes SaveLayerAlpha, which doesn't support alpha modulation
return ((getBackground() != null && getBackground().getCurrent() != null)
- || mText instanceof Spannable || hasSelection()
- || isHorizontalFadingEdgeEnabled());
+ || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled());
}
/**
@@ -7399,11 +7409,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
- if (mText instanceof Spannable && mLinksClickable) {
+ if (mSpannable != null && mLinksClickable) {
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
final int offset = getOffsetForPosition(x, y);
- final ClickableSpan[] clickables = ((Spannable) mText).getSpans(offset, offset,
+ final ClickableSpan[] clickables = mSpannable.getSpans(offset, offset,
ClickableSpan.class);
if (clickables.length > 0) {
return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HAND);
@@ -7496,10 +7506,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} else if (which == KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD) {
// mMovement is not null from doKeyDown
- mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
+ mMovement.onKeyUp(this, mSpannable, keyCode, up);
while (--repeatCount > 0) {
- mMovement.onKeyDown(this, (Spannable) mText, keyCode, down);
- mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
+ mMovement.onKeyDown(this, mSpannable, keyCode, down);
+ mMovement.onKeyUp(this, mSpannable, keyCode, up);
}
}
@@ -7694,8 +7704,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean doDown = true;
if (otherEvent != null) {
try {
- boolean handled = mMovement.onKeyOther(this, (Spannable) mText,
- otherEvent);
+ boolean handled = mMovement.onKeyOther(this, mSpannable, otherEvent);
doDown = false;
if (handled) {
return KEY_EVENT_HANDLED;
@@ -7706,7 +7715,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
if (doDown) {
- if (mMovement.onKeyDown(this, (Spannable) mText, keyCode, event)) {
+ if (mMovement.onKeyDown(this, mSpannable, keyCode, event)) {
if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
mPreventDefaultMovement = true;
}
@@ -7848,7 +7857,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (mMovement != null && mLayout != null) {
- if (mMovement.onKeyUp(this, (Spannable) mText, keyCode, event)) {
+ if (mMovement.onKeyUp(this, mSpannable, keyCode, event)) {
return true;
}
}
@@ -8314,13 +8323,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Returns true if DynamicLayout is required
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean useDynamicLayout() {
+ return isTextSelectable() || (mSpannable != null && mPrecomputed == null);
+ }
+
+ /**
* @hide
*/
protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,
Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
boolean useSaved) {
Layout result = null;
- if (mText instanceof Spannable) {
+ if (useDynamicLayout()) {
final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,
wantWidth)
.setDisplayText(mTransformed)
@@ -9262,7 +9281,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (newStart != start) {
- Selection.setSelection((Spannable) mText, newStart);
+ Selection.setSelection(mSpannable, newStart);
return true;
}
@@ -9999,9 +10018,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mEditor != null) mEditor.onFocusChanged(focused, direction);
if (focused) {
- if (mText instanceof Spannable) {
- Spannable sp = (Spannable) mText;
- MetaKeyKeyListener.resetMetaState(sp);
+ if (mSpannable != null) {
+ MetaKeyKeyListener.resetMetaState(mSpannable);
}
}
@@ -10039,7 +10057,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
public void clearComposingText() {
if (mText instanceof Spannable) {
- BaseInputConnection.removeComposingSpans((Spannable) mText);
+ BaseInputConnection.removeComposingSpans(mSpannable);
}
}
@@ -10095,7 +10113,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean handled = false;
if (mMovement != null) {
- handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
+ handled |= mMovement.onTouchEvent(this, mSpannable, event);
}
final boolean textIsSelectable = isTextSelectable();
@@ -10103,7 +10121,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// The LinkMovementMethod which should handle taps on links has not been installed
// on non editable text that support text selection.
// We reproduce its behavior here to open links for these.
- ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(),
+ ClickableSpan[] links = mSpannable.getSpans(getSelectionStart(),
getSelectionEnd(), ClickableSpan.class);
if (links.length > 0) {
@@ -10138,7 +10156,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public boolean onGenericMotionEvent(MotionEvent event) {
if (mMovement != null && mText instanceof Spannable && mLayout != null) {
try {
- if (mMovement.onGenericMotionEvent(this, (Spannable) mText, event)) {
+ if (mMovement.onGenericMotionEvent(this, mSpannable, event)) {
return true;
}
} catch (AbstractMethodError ex) {
@@ -10199,8 +10217,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public boolean onTrackballEvent(MotionEvent event) {
- if (mMovement != null && mText instanceof Spannable && mLayout != null) {
- if (mMovement.onTrackballEvent(this, (Spannable) mText, event)) {
+ if (mMovement != null && mSpannable != null && mLayout != null) {
+ if (mMovement.onTrackballEvent(this, mSpannable, event)) {
return true;
}
}
@@ -11121,7 +11139,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mText != null) {
int updatedTextLength = mText.length();
if (updatedTextLength > 0) {
- Selection.setSelection((Spannable) mText, updatedTextLength);
+ Selection.setSelection(mSpannable, updatedTextLength);
}
}
} return true;
@@ -11593,6 +11611,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ /** @hide */
+ public void hideFloatingToolbar(int durationMs) {
+ if (mEditor != null) {
+ mEditor.hideFloatingToolbar(durationMs);
+ }
+ }
+
boolean canUndo() {
return mEditor != null && mEditor.canUndo();
}
@@ -11687,10 +11712,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean selectAllText() {
if (mEditor != null) {
// Hide the toolbar before changing the selection to avoid flickering.
- mEditor.hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
+ hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
}
final int length = mText.length();
- Selection.setSelection((Spannable) mText, 0, length);
+ Selection.setSelection(mSpannable, 0, length);
return length > 0;
}
@@ -11718,7 +11743,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (paste != null) {
if (!didFirst) {
- Selection.setSelection((Spannable) mText, max);
+ Selection.setSelection(mSpannable, max);
((Editable) mText).replace(min, max, paste);
didFirst = true;
} else {
@@ -11740,7 +11765,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
selectedText = TextUtils.trimToParcelableSize(selectedText);
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
getContext().startActivity(Intent.createChooser(sharingIntent, null));
- Selection.setSelection((Spannable) mText, getSelectionEnd());
+ Selection.setSelection(mSpannable, getSelectionEnd());
}
}
@@ -11815,7 +11840,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case DragEvent.ACTION_DRAG_LOCATION:
if (mText instanceof Spannable) {
final int offset = getOffsetForPosition(event.getX(), event.getY());
- Selection.setSelection((Spannable) mText, offset);
+ Selection.setSelection(mSpannable, offset);
}
return true;
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 28a7c1204071..cbd3ad5980f5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -53,10 +53,21 @@ public final class Zygote {
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 66035f499b94..9467eccd3ff7 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -652,7 +652,7 @@ public class ZygoteInit {
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 03f2bc10b551..2959667e046f 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -67,6 +67,10 @@ public final class NetworkManagementSocketTagger extends SocketTagger {
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 122e5c48214d..8d4b56ca3619 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -514,7 +514,7 @@ message JobPackageHistoryProto {
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 @@ message JobStatusDumpProto {
// 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/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index c58de563692b..eb60942ba849 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -118,61 +118,60 @@ message PowerManagerServiceDumpProto {
// True if the sandman has just been summoned for the first time since entering
// the dreaming or dozing state. Indicates whether a new dream should begin.
optional bool is_sandman_summoned = 23;
- // If true, the device is in low power mode.
- optional bool is_low_power_mode_enabled = 24;
// True if the battery level is currently considered low.
- optional bool is_battery_level_low = 25;
+ optional bool is_battery_level_low = 24;
// True if we are currently in light device idle mode.
- optional bool is_light_device_idle_mode = 26;
+ optional bool is_light_device_idle_mode = 25;
// True if we are currently in device idle mode.
- optional bool is_device_idle_mode = 27;
+ optional bool is_device_idle_mode = 26;
// Set of app ids that we will always respect the wake locks for.
- repeated int32 device_idle_whitelist = 28;
+ repeated int32 device_idle_whitelist = 27;
// Set of app ids that are temporarily allowed to acquire wakelocks due to
// high-pri message
- repeated int32 device_idle_temp_whitelist = 29;
+ repeated int32 device_idle_temp_whitelist = 28;
// Timestamp of the last time the device was awoken.
- optional int64 last_wake_time_ms = 30;
+ optional int64 last_wake_time_ms = 29;
// Timestamp of the last time the device was put to sleep.
- optional int64 last_sleep_time_ms = 31;
+ optional int64 last_sleep_time_ms = 30;
// Timestamp of the last call to user activity.
- optional int64 last_user_activity_time_ms = 32;
- optional int64 last_user_activity_time_no_change_lights_ms = 33;
+ optional int64 last_user_activity_time_ms = 31;
+ optional int64 last_user_activity_time_no_change_lights_ms = 32;
// Timestamp of last interactive power hint.
- optional int64 last_interactive_power_hint_time_ms = 34;
+ optional int64 last_interactive_power_hint_time_ms = 33;
// Timestamp of the last screen brightness boost.
- optional int64 last_screen_brightness_boost_time_ms = 35;
+ optional int64 last_screen_brightness_boost_time_ms = 34;
// True if screen brightness boost is in progress.
- optional bool is_screen_brightness_boost_in_progress = 36;
+ optional bool is_screen_brightness_boost_in_progress = 35;
// True if the display power state has been fully applied, which means the
// display is actually on or actually off or whatever was requested.
- optional bool is_display_ready = 37;
+ optional bool is_display_ready = 36;
// True if the wake lock suspend blocker has been acquired.
- optional bool is_holding_wake_lock_suspend_blocker = 38;
+ optional bool is_holding_wake_lock_suspend_blocker = 37;
// The suspend blocker used to keep the CPU alive when the display is on, the
// display is getting ready or there is user activity (in which case the
// display must be on).
- optional bool is_holding_display_suspend_blocker = 39;
+ optional bool is_holding_display_suspend_blocker = 38;
// Settings and configuration
- optional PowerServiceSettingsAndConfigurationDumpProto settings_and_configuration = 40;
+ optional PowerServiceSettingsAndConfigurationDumpProto settings_and_configuration = 39;
// Sleep timeout in ms. This can be -1.
- optional sint32 sleep_timeout_ms = 41;
+ optional sint32 sleep_timeout_ms = 40;
// Screen off timeout in ms
- optional int32 screen_off_timeout_ms = 42;
+ optional int32 screen_off_timeout_ms = 41;
// Screen dim duration in ms
- optional int32 screen_dim_duration_ms = 43;
+ optional int32 screen_dim_duration_ms = 42;
// We are currently in the middle of a batch change of uids.
- optional bool are_uids_changing = 44;
+ optional bool are_uids_changing = 43;
// Some uids have actually changed while mUidsChanging was true.
- optional bool are_uids_changed = 45;
+ optional bool are_uids_changed = 44;
// List of UIDs and their states
- repeated UidStateProto uid_states = 46;
- optional .android.os.LooperProto looper = 47;
+ repeated UidStateProto uid_states = 45;
+ optional .android.os.LooperProto looper = 46;
// List of all wake locks acquired by applications.
- repeated WakeLockProto wake_locks = 48;
+ repeated WakeLockProto wake_locks = 47;
// List of all suspend blockers.
- repeated SuspendBlockerProto suspend_blockers = 49;
- optional WirelessChargerDetectorProto wireless_charger_detector = 50;
+ repeated SuspendBlockerProto suspend_blockers = 48;
+ optional WirelessChargerDetectorProto wireless_charger_detector = 49;
+ optional BatterySaverStateMachineProto battery_saver_state_machine = 50;
}
// A com.android.server.power.PowerManagerService.SuspendBlockerImpl object.
@@ -270,51 +269,80 @@ message PowerServiceSettingsAndConfigurationDumpProto {
optional bool are_dreams_activate_on_dock_setting = 17;
// True if doze should not be started until after the screen off transition.
optional bool is_doze_after_screen_off_config = 18;
- // If true, the device is in low power mode.
- optional bool is_low_power_mode_setting = 19;
- // Current state of whether the settings are allowing auto low power mode.
- optional bool is_auto_low_power_mode_configured = 20;
- // The user turned off low power mode below the trigger level
- optional bool is_auto_low_power_mode_snoozing = 21;
// The minimum screen off timeout, in milliseconds.
- optional int32 minimum_screen_off_timeout_config_ms = 22;
+ optional int32 minimum_screen_off_timeout_config_ms = 19;
// The screen dim duration, in milliseconds.
- optional int32 maximum_screen_dim_duration_config_ms = 23;
+ optional int32 maximum_screen_dim_duration_config_ms = 20;
// The maximum screen dim time expressed as a ratio relative to the screen off timeout.
- optional float maximum_screen_dim_ratio_config = 24;
+ optional float maximum_screen_dim_ratio_config = 21;
// The screen off timeout setting value in milliseconds.
- optional int32 screen_off_timeout_setting_ms = 25;
+ optional int32 screen_off_timeout_setting_ms = 22;
// The sleep timeout setting value in milliseconds. Default value is -1.
- optional sint32 sleep_timeout_setting_ms = 26;
+ optional sint32 sleep_timeout_setting_ms = 23;
// The maximum allowable screen off timeout according to the device administration policy.
- optional int32 maximum_screen_off_timeout_from_device_admin_ms = 27;
- optional bool is_maximum_screen_off_timeout_from_device_admin_enforced_locked = 28;
+ optional int32 maximum_screen_off_timeout_from_device_admin_ms = 24;
+ optional bool is_maximum_screen_off_timeout_from_device_admin_enforced_locked = 25;
// The stay on while plugged in setting.
// A set of battery conditions under which to make the screen stay on.
- optional StayOnWhilePluggedInProto stay_on_while_plugged_in = 29;
+ optional StayOnWhilePluggedInProto stay_on_while_plugged_in = 26;
// The screen brightness mode.
- optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 30;
+ optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 27;
// The screen brightness setting override from the window manager
// to allow the current foreground activity to override the brightness.
// Use -1 to disable.
- optional sint32 screen_brightness_override_from_window_manager = 31;
+ optional sint32 screen_brightness_override_from_window_manager = 28;
// The user activity timeout override from the window manager
// to allow the current foreground activity to override the user activity
// timeout. Use -1 to disable.
- optional sint64 user_activity_timeout_override_from_window_manager_ms = 32;
+ optional sint64 user_activity_timeout_override_from_window_manager_ms = 29;
// The window manager has determined the user to be inactive via other means.
// Set this to false to disable.
- optional bool is_user_inactive_override_from_window_manager = 33;
+ optional bool is_user_inactive_override_from_window_manager = 30;
// The screen state to use while dozing.
- optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 34;
+ optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 31;
// The screen brightness to use while dozing.
- optional float dozed_screen_brightness_override_from_dream_manager = 35;
+ optional float dozed_screen_brightness_override_from_dream_manager = 32;
// Screen brightness settings limits.
- optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 36;
+ optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 33;
// True if double tap to wake is enabled
- optional bool is_double_tap_wake_enabled = 37;
+ optional bool is_double_tap_wake_enabled = 34;
// True if we are currently in VR Mode.
- optional bool is_vr_mode_enabled = 38;
+ optional bool is_vr_mode_enabled = 35;
// True if Sidekick is controlling the display and we shouldn't change its power mode.
- optional bool draw_wake_lock_override_from_sidekick = 39;
+ optional bool draw_wake_lock_override_from_sidekick = 36;
}
+
+message BatterySaverStateMachineProto {
+ // Whether battery saver is enabled.
+ optional bool enabled = 1;
+
+ // Whether system has booted.
+ optional bool boot_completed = 2;
+
+ // Whether settings have been loaded already.
+ optional bool settings_loaded = 3;
+
+ // Whether battery status has been set at least once.
+ optional bool battery_status_set = 4;
+
+ // Whether automatic battery saver has been canceled by the user.
+ optional bool battery_saver_snoozing = 5;
+
+ // Whether the device is connected to any power source.
+ optional bool is_powered = 6;
+
+ // Current battery level in %, 0-100.
+ optional int32 battery_level = 7;
+
+ // Whether battery level is low or not.
+ optional bool is_battery_level_low = 8;
+
+ // The value of Global.LOW_POWER_MODE.
+ optional bool setting_battery_saver_enabled = 9;
+
+ // The value of Global.LOW_POWER_MODE_STICKY.
+ optional bool setting_battery_saver_enabled_sticky = 10;
+
+ // The value of Global.LOW_POWER_MODE_TRIGGER_LEVEL.
+ optional int32 setting_battery_saver_trigger_threshold = 11;
+} \ No newline at end of file
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a504ab9dda2d..6ef773a14e44 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -266,6 +266,7 @@ public class SettingsBackupTest {
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT,
Settings.Global.LOW_POWER_MODE,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
+ Settings.Global.LOW_POWER_MODE_STICKY,
Settings.Global.LTE_SERVICE_FORCED,
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index 2b5b27b527b7..4f1efbfed5f7 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -17,6 +17,7 @@
package android.widget;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
@@ -32,9 +33,11 @@ import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.text.GetChars;
import android.text.Layout;
+import android.text.PrecomputedText;
import android.text.Selection;
import android.text.Spannable;
import android.view.View;
+import android.widget.TextView.BufferType;
import org.junit.Before;
import org.junit.Rule;
@@ -241,6 +244,82 @@ public class TextViewTest {
mTextView.onTextContextMenuItem(TextView.ID_CUT);
}
+ @Test
+ public void testUseDynamicLayout() {
+ mTextView = new TextView(mActivity);
+ mTextView.setTextIsSelectable(true);
+ String text = "HelloWorld";
+ PrecomputedText precomputed =
+ PrecomputedText.create(text, mTextView.getTextMetricsParams());
+
+ mTextView.setTextIsSelectable(false);
+ mTextView.setText(text);
+ assertFalse(mTextView.useDynamicLayout());
+
+ mTextView.setTextIsSelectable(true);
+ mTextView.setText(text);
+ assertTrue(mTextView.useDynamicLayout());
+
+ mTextView.setTextIsSelectable(false);
+ mTextView.setText(precomputed);
+ assertFalse(mTextView.useDynamicLayout());
+
+ mTextView.setTextIsSelectable(true);
+ mTextView.setText(precomputed);
+ assertTrue(mTextView.useDynamicLayout());
+ }
+
+ @Test
+ public void testUseDynamicLayout_SPANNABLE() {
+ mTextView = new TextView(mActivity);
+ mTextView.setTextIsSelectable(true);
+ String text = "HelloWorld";
+ PrecomputedText precomputed =
+ PrecomputedText.create(text, mTextView.getTextMetricsParams());
+
+ mTextView.setTextIsSelectable(false);
+ mTextView.setText(text, BufferType.SPANNABLE);
+ android.util.Log.e("TextViewTest", "Text:" + mTextView.getText().getClass().getName());
+ assertTrue(mTextView.useDynamicLayout());
+
+ mTextView.setTextIsSelectable(true);
+ mTextView.setText(text, BufferType.SPANNABLE);
+ assertTrue(mTextView.useDynamicLayout());
+
+ mTextView.setTextIsSelectable(false);
+ mTextView.setText(precomputed, BufferType.SPANNABLE);
+ assertFalse(mTextView.useDynamicLayout());
+
+ mTextView.setTextIsSelectable(true);
+ mTextView.setText(precomputed, BufferType.SPANNABLE);
+ assertTrue(mTextView.useDynamicLayout());
+ }
+
+ @Test
+ public void testUseDynamicLayout_EDITABLE() {
+ mTextView = new TextView(mActivity);
+ mTextView.setTextIsSelectable(true);
+ String text = "HelloWorld";
+ PrecomputedText precomputed =
+ PrecomputedText.create(text, mTextView.getTextMetricsParams());
+
+ mTextView.setTextIsSelectable(false);
+ mTextView.setText(text, BufferType.EDITABLE);
+ assertTrue(mTextView.useDynamicLayout());
+
+ mTextView.setTextIsSelectable(true);
+ mTextView.setText(text, BufferType.EDITABLE);
+ assertTrue(mTextView.useDynamicLayout());
+
+ mTextView.setTextIsSelectable(false);
+ mTextView.setText(precomputed, BufferType.EDITABLE);
+ assertTrue(mTextView.useDynamicLayout());
+
+ mTextView.setTextIsSelectable(true);
+ mTextView.setText(precomputed, BufferType.EDITABLE);
+ assertTrue(mTextView.useDynamicLayout());
+ }
+
private String createLongText() {
int size = 600 * 1000;
final StringBuilder builder = new StringBuilder(size);
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
index 2daef0cb0cf4..7949c77ab558 100644
--- a/data/keyboards/Android.mk
+++ b/data/keyboards/Android.mk
@@ -28,7 +28,7 @@ LOCAL_BUILT_MODULE := $(intermediates)/stamp
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 457e4aa426b8..898939edabf0 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -52,8 +52,20 @@ import java.util.ArrayList;
/**
* {@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>&lt;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 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
* <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 91754162180f..bc0e43b5e9c6 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -3226,9 +3226,18 @@ public class ExifInterface {
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 @@ public class ExifInterface {
}
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 17002aa43f28..591f33f53eeb 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -25,9 +25,7 @@ import android.app.PendingIntent;
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 @@ import java.util.concurrent.Executor;
* 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 @@ public class MediaController2 implements AutoCloseable {
* @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 @@ public class MediaController2 implements AutoCloseable {
* 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 @@ public class MediaController2 implements AutoCloseable {
* @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 @@ public class MediaController2 implements AutoCloseable {
* @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 class MediaController2 implements AutoCloseable {
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 @@ public class MediaController2 implements AutoCloseable {
@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 @@ public class MediaController2 implements AutoCloseable {
*
* @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 @@ public class MediaController2 implements AutoCloseable {
}
/**
- * 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 @@ public class MediaController2 implements AutoCloseable {
* @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 1967a1c43000..423a1fd4479c 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -19,7 +19,6 @@ package android.media;
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 @@ public class MediaItem2 {
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 @@ public class MediaItem2 {
/**
* 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 034d17e14154..f29d386c417d 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -20,7 +20,6 @@ import android.annotation.CallbackExecutor;
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 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
* 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 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 {
* @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 59dd8cb8ae58..7b03ae0ca424 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -19,7 +19,6 @@ package android.media;
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 @@ public final class MediaMetadata2 {
* 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 @@ public final class MediaMetadata2 {
* 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 @@ public final class MediaMetadata2 {
*
* @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 5c08f197d37e..a4265525fb6b 100644
--- a/media/java/android/media/MediaPlayerBase.java
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -130,33 +130,6 @@ public abstract class MediaPlayerBase implements AutoCloseable {
*/
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 @@ public abstract class MediaPlayerBase implements AutoCloseable {
/**
* 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 f3392291becd..88f37e72b621 100644
--- a/media/java/android/media/MediaPlaylistAgent.java
+++ b/media/java/android/media/MediaPlaylistAgent.java
@@ -20,7 +20,6 @@ import android.annotation.CallbackExecutor;
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 @@ public abstract class MediaPlaylistAgent {
@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 2033c5911b13..2b3c2b4c2cbf 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -33,8 +33,6 @@ import android.media.update.ApiLoader;
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 android.os.ResultReceiver;
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 @@ public class MediaSession2 implements AutoCloseable {
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 @@ public class MediaSession2 implements AutoCloseable {
}
/**
- * 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 @@ public class MediaSession2 implements AutoCloseable {
*/
// 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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
/**
* 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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
@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 @@ public class MediaSession2 implements AutoCloseable {
}
/**
- * 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 @@ public class MediaSession2 implements AutoCloseable {
*
* @return command or {@code null}
*/
- public @Nullable Command getCommand() {
+ public @Nullable
+ SessionCommand2 getCommand() {
return mProvider.getCommand_impl();
}
@@ -1221,12 +835,11 @@ public class MediaSession2 implements AutoCloseable {
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 @@ public class MediaSession2 implements AutoCloseable {
* 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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
* @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 @@ public class MediaSession2 implements AutoCloseable {
}
/**
- * 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 @@ public class MediaSession2 implements AutoCloseable {
* <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 @@ public class MediaSession2 implements AutoCloseable {
* @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 85ac9b223eff..6c3a4bfd73b7 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -21,7 +21,6 @@ import android.annotation.NonNull;
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 @@ public abstract class MediaSessionService2 extends Service {
/**
* 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 5f7a3340603a..9213190184ef 100644
--- a/media/java/android/media/Rating2.java
+++ b/media/java/android/media/Rating2.java
@@ -19,7 +19,6 @@ package android.media;
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.Retention;
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 @@ public final class Rating2 {
/**
* 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 @@ public final class Rating2 {
* 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 @@ public final class Rating2 {
* 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 @@ public final class Rating2 {
* @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 000000000000..fe86a3ae49ec
--- /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 000000000000..399765e68b34
--- /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 f088be352d6b..bf2d445942a9 100644
--- a/media/java/android/media/SessionToken2.java
+++ b/media/java/android/media/SessionToken2.java
@@ -150,8 +150,8 @@ public final class SessionToken2 {
* @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 2d96d096bf3a..1a4608f707b6 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -18,7 +18,6 @@ package android.media;
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 @@ public abstract class VolumeProvider2 {
* @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 b7f49988022e..3b12fca32ecb 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -407,7 +407,7 @@ public final class MediaSessionManager {
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 @@ public final class MediaSessionManager {
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 @@ public final class MediaSessionManager {
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 @@ public final class MediaSessionManager {
}
}
- 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 @@ public final class MediaSessionManager {
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 213897d3e183..7234f7b61656 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -21,7 +21,7 @@ import android.media.AudioAttributes;
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 @@ public interface MediaController2Provider extends TransportControlProvider {
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 5a1db3ef8824..475134861105 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -23,10 +23,10 @@ import android.media.MediaMetadata2;
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 @@ public interface MediaSession2Provider extends TransportControlProvider {
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 @@ public interface MediaSession2Provider extends TransportControlProvider {
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 @@ public interface MediaSession2Provider extends TransportControlProvider {
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 1c0e2556f12d..8687b802add8 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -35,6 +35,8 @@ import android.media.MediaSession2.SessionCallback;
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 @@ public interface StaticProvider {
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 @@ public interface StaticProvider {
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 @@ public interface StaticProvider {
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 03944d243dc8..d89a88ab6970 100644
--- a/media/java/android/media/update/TransportControlProvider.java
+++ b/media/java/android/media/update/TransportControlProvider.java
@@ -29,8 +29,6 @@ public interface TransportControlProvider {
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 32a00d597e62..1244d7f040b4 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -243,11 +243,11 @@ public class MtpDatabase implements AutoCloseable {
}
};
- 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 f2b110680af1..8af5ff7ac1fc 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -18,6 +18,8 @@ package android.mtp;
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 class MtpServer implements Runnable {
public MtpServer(
MtpDatabase database,
+ FileDescriptor controlFd,
boolean usePtp,
Runnable onTerminate,
String deviceInfoManufacturer,
@@ -44,6 +47,7 @@ public class MtpServer implements Runnable {
mOnTerminate = Preconditions.checkNotNull(onTerminate);
native_setup(
database,
+ controlFd,
usePtp,
deviceInfoManufacturer,
deviceInfoModel,
@@ -92,6 +96,7 @@ public class MtpServer implements Runnable {
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 c76cebebc730..8730e0622200 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -55,22 +55,17 @@ static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) {
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 @@ android_mtp_MtpServer_remove_storage(JNIEnv *env, jobject thiz, jint storageId)
// ----------------------------------------------------------------------------
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/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 131b35cb0109..d3a55e812ee8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1602,7 +1602,7 @@
<!-- Notification: Menu row: Content description for the snooze icon. [CHAR LIMIT=NONE] -->
<string name="notification_menu_snooze_description">notification snooze options</string>
- <!-- Notification: Menu row: Content description for the snooze action shown in local context menu. [CHAR LIMIT=NONE] -->
+ <!-- Notification: Menu row: Label for the snooze action shown in local context menu. [CHAR LIMIT=NONE] -->
<string name="notification_menu_snooze_action">Snooze</string>
<!-- Notification: Snooze panel: Snooze undo button label. [CHAR LIMIT=50]-->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index 62b50044132e..5b4d65290146 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -137,6 +137,10 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback {
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 d24675c67188..1bab36be8cd8 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;
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.io.PrintWriter;
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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
// 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
};
/**
- * @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/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index d609ae7b7374..fcd4e8f3669f 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 class KeyguardBouncer {
}
}
+ 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 8fb0620d8064..88bc6eaaea77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -842,10 +842,17 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
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/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 6047f8e89f3e..a51cd93794f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -303,16 +303,18 @@ public class QuickStepController implements GestureHelper {
@Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final int width = right - left;
- final int height = bottom - top;
+ final int width = (right - left) - mNavigationBarView.getPaddingEnd()
+ - mNavigationBarView.getPaddingStart();
+ final int height = (bottom - top) - mNavigationBarView.getPaddingBottom()
+ - mNavigationBarView.getPaddingTop();
final int x1, x2, y1, y2;
if (mIsVertical) {
- x1 = (width - mTrackThickness) / 2;
+ x1 = (width - mTrackThickness) / 2 + mNavigationBarView.getPaddingStart();
x2 = x1 + mTrackThickness;
y1 = mDragPositive ? height / 2 : mTrackPadding;
y2 = y1 + height / 2 - mTrackPadding;
} else {
- y1 = (height - mTrackThickness) / 2;
+ y1 = (height - mTrackThickness) / 2 + mNavigationBarView.getPaddingTop();
y2 = y1 + mTrackThickness;
x1 = mDragPositive ? width / 2 : mTrackPadding;
x2 = x1 + width / 2 - mTrackPadding;
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 cfc0cc6207c3..7a6e98da8b35 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
private float mNotificationDensity;
// Scrim blanking callbacks
- private Choreographer.FrameCallback mPendingFrameCallback;
+ private Runnable mPendingFrameCallback;
private Runnable mBlankingTransitionRunnable;
private final WakeLock mWakeLock;
@@ -240,7 +240,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
// 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
// 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
// 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
// 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
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 8cd6295f7ee1..82469742020e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4654,9 +4654,12 @@ public class StatusBar extends SystemUI implements DemoMode,
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) {
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 56a7b1ba37d1..1a64b0000647 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
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 class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
+ public boolean willDismissWithAction() {
+ return mBouncer.willDismissWithAction();
+ }
+
private static class DismissWithActionRequest {
final OnDismissAction dismissAction;
final Runnable cancelAction;
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 000000000000..3f85c9d169c7
--- /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/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index a37947df7094..67453d5da6c7 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.any;
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 @@ public class KeyguardBouncerTest extends SysuiTestCase {
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 45845fc147a6..7743c6b443a5 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.os.Looper;
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 @@ public class ScrimControllerTest extends SysuiTestCase {
* @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/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 9417f04bbf50..cfb5c6dd080b 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5547,6 +5547,17 @@ message MetricsEvent {
// 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;
+
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 7bb532ebc8d7..58f78ecdcefc 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 @@ public final class Helper {
*
* @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 @@ public final class Helper {
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 @@ public final class Helper {
+ 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 1e1de35a3a43..bcb0bc413b53 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/** 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
@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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
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 03c5850fbda1..97ab97bc8d82 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 @@ final class ViewState {
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/PruneInstantAppsJobService.java b/services/core/java/com/android/server/PruneInstantAppsJobService.java
index a6c36850dd81..48e3a432dba4 100644
--- a/services/core/java/com/android/server/PruneInstantAppsJobService.java
+++ b/services/core/java/com/android/server/PruneInstantAppsJobService.java
@@ -23,6 +23,7 @@ import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
+import android.os.AsyncTask;
import java.util.concurrent.TimeUnit;
@@ -47,10 +48,12 @@ public class PruneInstantAppsJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
- PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
- packageManagerInternal.pruneInstantApps();
- jobFinished(params, false);
+ AsyncTask.execute(() -> {
+ PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ packageManagerInternal.pruneInstantApps();
+ jobFinished(params, false);
+ });
return true;
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 3d7b21dbc3c2..379658f6302f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2668,6 +2668,9 @@ class StorageManagerService extends IStorageManager.Stub
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 4f0acf73b901..5131a93503f5 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.Intent;
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 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ boolean mSystemProvidersInstalled;
+
CoreSettingsObserver mCoreSettingsObserver;
FontScaleSettingObserver mFontScaleSettingObserver;
@@ -4183,12 +4186,14 @@ public class ActivityManagerService extends IActivityManager.Stub
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 @@ public class ActivityManagerService extends IActivityManager.Stub
"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 @@ public class ActivityManagerService extends IActivityManager.Stub
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/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 1335ced6182e..efd815383200 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -1243,15 +1243,13 @@ class RecentTasks {
*/
private int findRemoveIndexForAddTask(TaskRecord task) {
final int recentsCount = mTasks.size();
- final int taskActivityType = task.getActivityType();
final Intent intent = task.intent;
final boolean document = intent != null && intent.isDocument();
int maxRecents = task.maxRecents - 1;
for (int i = 0; i < recentsCount; i++) {
final TaskRecord tr = mTasks.get(i);
- final int trActivityType = tr.getActivityType();
if (task != tr) {
- if (taskActivityType != trActivityType || task.userId != tr.userId) {
+ if (!task.hasCompatibleActivityType(tr)) {
continue;
}
final Intent trIntent = tr.intent;
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 776e93dd053f..3f39f4504de9 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -368,6 +368,19 @@ public class ClipboardService extends SystemService {
return related;
}
+ /** Check if the user has the given restriction set. Default to true if error occured during
+ * calling UserManager, so it fails safe.
+ */
+ private boolean hasRestriction(String restriction, int userId) {
+ try {
+ return mUm.hasUserRestriction(restriction, userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote Exception calling UserManager.getUserRestrictions: ", e);
+ // Fails safe
+ return true;
+ }
+ }
+
void setPrimaryClipInternal(@Nullable ClipData clip, int callingUid) {
// Push clipboard to host, if any
if (mHostClipboardMonitor != null) {
@@ -391,13 +404,8 @@ public class ClipboardService extends SystemService {
if (related != null) {
int size = related.size();
if (size > 1) { // Related profiles list include the current profile.
- boolean canCopy = false;
- try {
- canCopy = !mUm.getUserRestrictions(userId).getBoolean(
- UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote Exception calling UserManager: " + e);
- }
+ final boolean canCopy = !hasRestriction(
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, userId);
// Copy clip data to related users if allowed. If disallowed, then remove
// primary clip in related users to prevent pasting stale content.
if (!canCopy) {
@@ -416,7 +424,11 @@ public class ClipboardService extends SystemService {
for (int i = 0; i < size; i++) {
int id = related.get(i).id;
if (id != userId) {
- setPrimaryClipInternal(getClipboard(id), clip, callingUid);
+ final boolean canCopyIntoProfile = !hasRestriction(
+ UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
+ if (canCopyIntoProfile) {
+ setPrimaryClipInternal(getClipboard(id), clip, callingUid);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 7c56f4d20864..f617964481bf 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.Manifest.permission.READ_CONTACTS;
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 class LockSettingsService extends ILockSettings.Stub {
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 f0bcb815ebe2..e0487ea28ddb 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 @@ public class RecoverableKeyStoreManager {
Preconditions.checkNotNull(secretTypes, "secretTypes is null");
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
+
+ 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 > 0) {
- mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ 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 7348b849a041..01f0d74f13f3 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 @@ public class MediaSessionService extends SystemService implements Monitor {
@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 @@ public class MediaSessionService extends SystemService implements Monitor {
@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 efca15984e57..8febecf0150d 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// 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/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 9e7ad47deab9..61c6be7c84e1 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 java.util.Map;
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 @@ public class PackageDexOptimizer {
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/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index dd88cd1a9266..2ffc4e77eec5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -64,6 +64,7 @@ import android.os.UserManager;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
import android.service.vr.IVrManager;
@@ -97,6 +98,7 @@ import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.power.batterysaver.BatterySaverController;
+import com.android.server.power.batterysaver.BatterySaverStateMachine;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -225,6 +227,7 @@ public final class PowerManagerService extends SystemService
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final BatterySaverPolicy mBatterySaverPolicy;
private final BatterySaverController mBatterySaverController;
+ private final BatterySaverStateMachine mBatterySaverStateMachine;
private LightsManager mLightsManager;
private BatteryManagerInternal mBatteryManagerInternal;
@@ -492,18 +495,6 @@ public final class PowerManagerService extends SystemService
// Time when we last logged a warning about calling userActivity() without permission.
private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE;
- // If true, the device is in low power mode.
- private boolean mLowPowerModeEnabled;
-
- // Current state of the low power mode setting.
- private boolean mLowPowerModeSetting;
-
- // Current state of whether the settings are allowing auto low power mode.
- private boolean mAutoLowPowerModeConfigured;
-
- // The user turned off low power mode below the trigger level
- private boolean mAutoLowPowerModeSnoozing;
-
// True if the battery level is currently considered low.
private boolean mBatteryLevelLow;
@@ -667,6 +658,7 @@ public final class PowerManagerService extends SystemService
mBatterySaverPolicy = new BatterySaverPolicy(mHandler);
mBatterySaverController = new BatterySaverController(mContext,
BackgroundThread.get().getLooper(), mBatterySaverPolicy);
+ mBatterySaverStateMachine = new BatterySaverStateMachine(mContext, mBatterySaverController);
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
@@ -704,6 +696,7 @@ public final class PowerManagerService extends SystemService
mBatterySaverPolicy = batterySaverPolicy;
mBatterySaverController = new BatterySaverController(context,
BackgroundThread.getHandler().getLooper(), batterySaverPolicy);
+ mBatterySaverStateMachine = new BatterySaverStateMachine(mContext, mBatterySaverController);
}
@Override
@@ -725,6 +718,8 @@ public final class PowerManagerService extends SystemService
final long now = SystemClock.uptimeMillis();
mBootCompleted = true;
mDirty |= DIRTY_BOOT_COMPLETED;
+
+ mBatterySaverStateMachine.onBootCompleted();
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
@@ -820,12 +815,6 @@ public final class PowerManagerService extends SystemService
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.LOW_POWER_MODE),
- false, mSettingsObserver, UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
- false, mSettingsObserver, UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.THEATER_MODE_ON),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
@@ -953,17 +942,6 @@ public final class PowerManagerService extends SystemService
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
- final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver,
- Settings.Global.LOW_POWER_MODE, 0) != 0;
- final boolean autoLowPowerModeConfigured = Settings.Global.getInt(resolver,
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) != 0;
- if (lowPowerModeEnabled != mLowPowerModeSetting
- || autoLowPowerModeConfigured != mAutoLowPowerModeConfigured) {
- mLowPowerModeSetting = lowPowerModeEnabled;
- mAutoLowPowerModeConfigured = autoLowPowerModeConfigured;
- updateLowPowerModeLocked();
- }
-
mDirty |= DIRTY_SETTINGS;
}
@@ -977,29 +955,6 @@ public final class PowerManagerService extends SystemService
}
}
- private void updateLowPowerModeLocked() {
- if ((mIsPowered || !mBatteryLevelLow && !mBootCompleted) && mLowPowerModeSetting) {
- if (DEBUG_SPEW) {
- Slog.d(TAG, "updateLowPowerModeLocked: powered or booting with sufficient battery,"
- + " turning setting off");
- }
- // Turn setting off if powered
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE, 0);
- mLowPowerModeSetting = false;
- }
- final boolean autoLowPowerModeEnabled = !mIsPowered && mAutoLowPowerModeConfigured
- && !mAutoLowPowerModeSnoozing && mBatteryLevelLow;
- final boolean lowPowerModeEnabled = mLowPowerModeSetting || autoLowPowerModeEnabled;
-
- if (mLowPowerModeEnabled != lowPowerModeEnabled) {
- mLowPowerModeEnabled = lowPowerModeEnabled;
-
- postAfterBootCompleted(() ->
- mBatterySaverController.enableBatterySaver(mLowPowerModeEnabled));
- }
- }
-
private void handleSettingsChangedLocked() {
updateSettingsLocked();
updatePowerStateLocked();
@@ -1751,15 +1706,7 @@ public final class PowerManagerService extends SystemService
}
}
- if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) {
- if (oldLevelLow != mBatteryLevelLow && !mBatteryLevelLow) {
- if (DEBUG_SPEW) {
- Slog.d(TAG, "updateIsPoweredLocked: resetting low power snooze");
- }
- mAutoLowPowerModeSnoozing = false;
- }
- updateLowPowerModeLocked();
- }
+ mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, mBatteryLevelLow);
}
}
@@ -2733,36 +2680,20 @@ public final class PowerManagerService extends SystemService
}
private boolean isLowPowerModeInternal() {
- synchronized (mLock) {
- return mLowPowerModeEnabled;
- }
+ return mBatterySaverController.isEnabled();
}
- private boolean setLowPowerModeInternal(boolean mode) {
+ private boolean setLowPowerModeInternal(boolean enabled) {
synchronized (mLock) {
- if (DEBUG) Slog.d(TAG, "setLowPowerModeInternal " + mode + " mIsPowered=" + mIsPowered);
+ if (DEBUG) {
+ Slog.d(TAG, "setLowPowerModeInternal " + enabled + " mIsPowered=" + mIsPowered);
+ }
if (mIsPowered) {
return false;
}
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE, mode ? 1 : 0);
- mLowPowerModeSetting = mode;
- if (mAutoLowPowerModeConfigured && mBatteryLevelLow) {
- if (mode && mAutoLowPowerModeSnoozing) {
- if (DEBUG_SPEW) {
- Slog.d(TAG, "setLowPowerModeInternal: clearing low power mode snooze");
- }
- mAutoLowPowerModeSnoozing = false;
- } else if (!mode && !mAutoLowPowerModeSnoozing) {
- if (DEBUG_SPEW) {
- Slog.d(TAG, "setLowPowerModeInternal: snoozing low power mode");
- }
- mAutoLowPowerModeSnoozing = true;
- }
- }
+ mBatterySaverStateMachine.setBatterySaverEnabledManually(enabled);
- updateLowPowerModeLocked();
return true;
}
}
@@ -2848,7 +2779,8 @@ public final class PowerManagerService extends SystemService
@VisibleForTesting
void updatePowerRequestFromBatterySaverPolicy(DisplayPowerRequest displayPowerRequest) {
PowerSaveState state = mBatterySaverPolicy.
- getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS, mLowPowerModeEnabled);
+ getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS,
+ mBatterySaverController.isEnabled());
displayPowerRequest.lowPowerMode = state.batterySaverEnabled;
displayPowerRequest.screenLowPowerBrightnessFactor = state.brightnessFactor;
}
@@ -3325,7 +3257,6 @@ public final class PowerManagerService extends SystemService
pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
pw.println(" mSandmanScheduled=" + mSandmanScheduled);
pw.println(" mSandmanSummoned=" + mSandmanSummoned);
- pw.println(" mLowPowerModeEnabled=" + mLowPowerModeEnabled);
pw.println(" mBatteryLevelLow=" + mBatteryLevelLow);
pw.println(" mLightDeviceIdleMode=" + mLightDeviceIdleMode);
pw.println(" mDeviceIdleMode=" + mDeviceIdleMode);
@@ -3378,9 +3309,6 @@ public final class PowerManagerService extends SystemService
pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
pw.println(" mDozeAfterScreenOff=" + mDozeAfterScreenOff);
- pw.println(" mLowPowerModeSetting=" + mLowPowerModeSetting);
- pw.println(" mAutoLowPowerModeConfigured=" + mAutoLowPowerModeConfigured);
- pw.println(" mAutoLowPowerModeSnoozing=" + mAutoLowPowerModeSnoozing);
pw.println(" mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig);
pw.println(" mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig);
pw.println(" mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig);
@@ -3456,6 +3384,7 @@ public final class PowerManagerService extends SystemService
pw.println("Display Power: " + mDisplayPowerCallbacks);
mBatterySaverPolicy.dump(pw);
+ mBatterySaverStateMachine.dump(pw);
pw.println();
final int numProfiles = mProfilePowerState.size();
@@ -3557,7 +3486,6 @@ public final class PowerManagerService extends SystemService
mRequestWaitForNegativeProximity);
proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled);
proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned);
- proto.write(PowerManagerServiceDumpProto.IS_LOW_POWER_MODE_ENABLED, mLowPowerModeEnabled);
proto.write(PowerManagerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow);
proto.write(PowerManagerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode);
proto.write(PowerManagerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode);
@@ -3663,15 +3591,6 @@ public final class PowerManagerService extends SystemService
PowerServiceSettingsAndConfigurationDumpProto.IS_DOZE_AFTER_SCREEN_OFF_CONFIG,
mDozeAfterScreenOff);
proto.write(
- PowerServiceSettingsAndConfigurationDumpProto.IS_LOW_POWER_MODE_SETTING,
- mLowPowerModeSetting);
- proto.write(
- PowerServiceSettingsAndConfigurationDumpProto.IS_AUTO_LOW_POWER_MODE_CONFIGURED,
- mAutoLowPowerModeConfigured);
- proto.write(
- PowerServiceSettingsAndConfigurationDumpProto.IS_AUTO_LOW_POWER_MODE_SNOOZING,
- mAutoLowPowerModeSnoozing);
- proto.write(
PowerServiceSettingsAndConfigurationDumpProto
.MINIMUM_SCREEN_OFF_TIMEOUT_CONFIG_MS,
mMinimumScreenOffTimeoutConfig);
@@ -3792,6 +3711,9 @@ public final class PowerManagerService extends SystemService
proto.end(uIDToken);
}
+ mBatterySaverStateMachine.dumpProto(proto,
+ PowerManagerServiceDumpProto.BATTERY_SAVER_STATE_MACHINE);
+
mHandler.getLooper().writeToProto(proto, PowerManagerServiceDumpProto.LOOPER);
for (WakeLock wl : mWakeLocks) {
@@ -4432,12 +4354,12 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
- public boolean setPowerSaveMode(boolean mode) {
+ public boolean setPowerSaveMode(boolean enabled) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
- return setLowPowerModeInternal(mode);
+ return setLowPowerModeInternal(enabled);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4752,7 +4674,8 @@ public final class PowerManagerService extends SystemService
@Override
public PowerSaveState getLowPowerState(@ServiceType int serviceType) {
synchronized (mLock) {
- return mBatterySaverPolicy.getBatterySaverPolicy(serviceType, mLowPowerModeEnabled);
+ return mBatterySaverPolicy.getBatterySaverPolicy(serviceType,
+ mBatterySaverController.isEnabled());
}
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
new file mode 100644
index 000000000000..5b3182ebd699
--- /dev/null
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -0,0 +1,413 @@
+/*
+ * 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.power.batterysaver;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.power.BatterySaverPolicy;
+import com.android.server.power.BatterySaverStateMachineProto;
+
+import java.io.PrintWriter;
+
+/**
+ * Decides when to enable / disable battery saver.
+ *
+ * (n.b. This isn't really implemented as a "state machine" though.)
+ *
+ * Test:
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+ */
+public class BatterySaverStateMachine {
+ private static final String TAG = "BatterySaverStateMachine";
+ private final Object mLock = new Object();
+
+ private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
+
+ private final Context mContext;
+ private final BatterySaverController mBatterySaverController;
+
+ /** Whether the system has booted. */
+ @GuardedBy("mLock")
+ private boolean mBootCompleted;
+
+ /** Whether global settings have been loaded already. */
+ @GuardedBy("mLock")
+ private boolean mSettingsLoaded;
+
+ /** Whether the first battery status has arrived. */
+ @GuardedBy("mLock")
+ private boolean mBatteryStatusSet;
+
+ /** Whether the device is connected to any power source. */
+ @GuardedBy("mLock")
+ private boolean mIsPowered;
+
+ /** Current battery level in %, 0-100. (Currently only used in dumpsys.) */
+ @GuardedBy("mLock")
+ private int mBatteryLevel;
+
+ /** Whether the battery level is considered to be "low" or not.*/
+ @GuardedBy("mLock")
+ private boolean mIsBatteryLevelLow;
+
+ /** Previously known value of Global.LOW_POWER_MODE. */
+ @GuardedBy("mLock")
+ private boolean mSettingBatterySaverEnabled;
+
+ /** Previously known value of Global.LOW_POWER_MODE_STICKY. */
+ @GuardedBy("mLock")
+ private boolean mSettingBatterySaverEnabledSticky;
+
+ /**
+ * Previously known value of Global.LOW_POWER_MODE_TRIGGER_LEVEL.
+ * (Currently only used in dumpsys.)
+ */
+ @GuardedBy("mLock")
+ private int mSettingBatterySaverTriggerThreshold;
+
+ /**
+ * Whether BS has been manually disabled while the battery level is low, in which case we
+ * shouldn't auto re-enable it until the battery level is not low.
+ */
+ @GuardedBy("mLock")
+ private boolean mBatterySaverSnoozing;
+
+ private final ContentObserver mSettingsObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ refreshSettingsLocked();
+ }
+ }
+ };
+
+ public BatterySaverStateMachine(
+ Context context, BatterySaverController batterySaverController) {
+ mContext = context;
+ mBatterySaverController = batterySaverController;
+ }
+
+ private boolean isBatterySaverEnabled() {
+ return mBatterySaverController.isEnabled();
+ }
+
+ private boolean isAutoBatterySaverConfigured() {
+ return mSettingBatterySaverTriggerThreshold > 0;
+ }
+
+ /**
+ * {@link com.android.server.power.PowerManagerService} calls it when the system is booted.
+ */
+ public void onBootCompleted() {
+ if (DEBUG) {
+ Slog.d(TAG, "onBootCompleted");
+ }
+ synchronized (mLock) {
+
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE),
+ false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE_STICKY),
+ false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+ false, mSettingsObserver, UserHandle.USER_SYSTEM);
+
+ mBootCompleted = true;
+
+ refreshSettingsLocked();
+
+ doAutoBatterySaverLocked();
+ }
+ }
+
+ void refreshSettingsLocked() {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ final boolean lowPowerModeEnabled = getGlobalSetting(
+ Settings.Global.LOW_POWER_MODE, 0) != 0;
+ final boolean lowPowerModeEnabledSticky = getGlobalSetting(
+ Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0;
+ final int lowPowerModeTriggerLevel = getGlobalSetting(
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+
+ setSettingsLocked(lowPowerModeEnabled, lowPowerModeEnabledSticky,
+ lowPowerModeTriggerLevel);
+ }
+
+ /**
+ * {@link com.android.server.power.PowerManagerService} calls it when relevant global settings
+ * have changed.
+ *
+ * Note this will be called before {@link #onBootCompleted} too.
+ */
+ @VisibleForTesting
+ void setSettingsLocked(boolean batterySaverEnabled, boolean batterySaverEnabledSticky,
+ int batterySaverTriggerThreshold) {
+ if (DEBUG) {
+ Slog.d(TAG, "setSettings: enabled=" + batterySaverEnabled
+ + " sticky=" + batterySaverEnabledSticky
+ + " threshold=" + batterySaverTriggerThreshold);
+ }
+
+ mSettingsLoaded = true;
+
+ final boolean enabledChanged = mSettingBatterySaverEnabled != batterySaverEnabled;
+ final boolean stickyChanged =
+ mSettingBatterySaverEnabledSticky != batterySaverEnabledSticky;
+ final boolean thresholdChanged
+ = mSettingBatterySaverTriggerThreshold != batterySaverTriggerThreshold;
+
+ if (!(enabledChanged || stickyChanged || thresholdChanged)) {
+ return;
+ }
+
+ mSettingBatterySaverEnabled = batterySaverEnabled;
+ mSettingBatterySaverEnabledSticky = batterySaverEnabledSticky;
+ mSettingBatterySaverTriggerThreshold = batterySaverTriggerThreshold;
+
+ if (enabledChanged) {
+ final String reason = batterySaverEnabled
+ ? "Global.low_power changed to 1" : "Global.low_power changed to 0";
+ enableBatterySaverLocked(/*enable=*/ batterySaverEnabled, /*manual=*/ true,
+ reason);
+ }
+ }
+
+ /**
+ * {@link com.android.server.power.PowerManagerService} calls it when battery state changes.
+ *
+ * Note this may be called before {@link #onBootCompleted} too.
+ */
+ public void setBatteryStatus(boolean newPowered, int newLevel, boolean newBatteryLevelLow) {
+ if (DEBUG) {
+ Slog.d(TAG, "setBatteryStatus: powered=" + newPowered + " level=" + newLevel
+ + " low=" + newBatteryLevelLow);
+ }
+ synchronized (mLock) {
+ mBatteryStatusSet = true;
+
+ final boolean poweredChanged = mIsPowered != newPowered;
+ final boolean levelChanged = mBatteryLevel != newLevel;
+ final boolean lowChanged = mIsBatteryLevelLow != newBatteryLevelLow;
+
+ if (!(poweredChanged || levelChanged || lowChanged)) {
+ return;
+ }
+
+ mIsPowered = newPowered;
+ mBatteryLevel = newLevel;
+ mIsBatteryLevelLow = newBatteryLevelLow;
+
+ doAutoBatterySaverLocked();
+ }
+ }
+
+ /**
+ * Decide whether to auto-start / stop battery saver.
+ */
+ private void doAutoBatterySaverLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "doAutoBatterySaverLocked: mBootCompleted=" + mBootCompleted
+ + " mSettingsLoaded=" + mSettingsLoaded
+ + " mBatteryStatusSet=" + mBatteryStatusSet
+ + " mIsBatteryLevelLow=" + mIsBatteryLevelLow
+ + " mBatterySaverSnoozing=" + mBatterySaverSnoozing
+ + " mIsPowered=" + mIsPowered
+ + " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky);
+ }
+ if (!(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
+ return; // Not fully initialized yet.
+ }
+ if (!mIsBatteryLevelLow) {
+ updateSnoozingLocked(false, "Battery not low");
+ }
+ if (mIsPowered) {
+ updateSnoozingLocked(false, "Plugged in");
+ enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, "Plugged in");
+
+ } else if (mSettingBatterySaverEnabledSticky) {
+ // Re-enable BS.
+ enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true, "Sticky restore");
+
+ } else if (mIsBatteryLevelLow) {
+ if (!mBatterySaverSnoozing && isAutoBatterySaverConfigured()) {
+ enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false, "Auto ON");
+ }
+ } else { // Battery not low
+ enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, "Auto OFF");
+ }
+ }
+
+ /**
+ * {@link com.android.server.power.PowerManagerService} calls it when
+ * {@link android.os.PowerManager#setPowerSaveMode} is called.
+ *
+ * Note this could? be called before {@link #onBootCompleted} too.
+ */
+ public void setBatterySaverEnabledManually(boolean enabled) {
+ if (DEBUG) {
+ Slog.d(TAG, "setBatterySaverEnabledManually: enabled=" + enabled);
+ }
+ synchronized (mLock) {
+ enableBatterySaverLocked(/*enable=*/ enabled, /*manual=*/ true,
+ (enabled ? "Manual ON" : "Manual OFF"));
+ }
+ }
+
+ /**
+ * Actually enable / disable battery saver. Write the new state to the global settings
+ * and propagate it to {@link #mBatterySaverController}.
+ */
+ private void enableBatterySaverLocked(boolean enable, boolean manual, String reason) {
+ if (DEBUG) {
+ Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual
+ + " reason=" + reason);
+ }
+ final boolean wasEnabled = mBatterySaverController.isEnabled();
+
+ if (wasEnabled == enable) {
+ if (DEBUG) {
+ Slog.d(TAG, "Already " + (enable ? "enabled" : "disabled"));
+ }
+ return;
+ }
+ if (enable && mIsPowered) {
+ if (DEBUG) Slog.d(TAG, "Can't enable: isPowered");
+ return;
+ }
+
+ if (manual) {
+ if (enable) {
+ updateSnoozingLocked(false, "Manual snooze OFF");
+ } else {
+ // When battery saver is disabled manually (while battery saver is enabled)
+ // when the battery level is low, we "snooze" BS -- i.e. disable auto battery saver.
+ // We resume auto-BS once the battery level is not low, or the device is plugged in.
+ if (isBatterySaverEnabled() && mIsBatteryLevelLow) {
+ updateSnoozingLocked(true, "Manual snooze");
+ }
+ }
+ }
+
+ mSettingBatterySaverEnabled = enable;
+ putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);
+
+ if (manual) {
+ mSettingBatterySaverEnabledSticky = enable;
+ putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
+ }
+ mBatterySaverController.enableBatterySaver(enable);
+
+ if (DEBUG) {
+ Slog.d(TAG, "Battery saver: Enabled=" + enable
+ + " manual=" + manual
+ + " reason=" + reason);
+ }
+ }
+
+ private void updateSnoozingLocked(boolean snoozing, String reason) {
+ if (mBatterySaverSnoozing == snoozing) {
+ return;
+ }
+ if (DEBUG) Slog.d(TAG, "Snooze: " + (snoozing ? "start" : "stop") + " reason=" + reason);
+ mBatterySaverSnoozing = snoozing;
+ }
+
+ @VisibleForTesting
+ protected void putGlobalSetting(String key, int value) {
+ Global.putInt(mContext.getContentResolver(), key, value);
+ }
+
+ @VisibleForTesting
+ protected int getGlobalSetting(String key, int defValue) {
+ return Global.getInt(mContext.getContentResolver(), key, defValue);
+ }
+
+ public void dump(PrintWriter pw) {
+ synchronized (mLock) {
+ pw.println();
+ pw.println("Battery saver state machine:");
+
+ pw.print(" Enabled=");
+ pw.println(mBatterySaverController.isEnabled());
+
+ pw.print(" mBootCompleted=");
+ pw.println(mBootCompleted);
+ pw.print(" mSettingsLoaded=");
+ pw.println(mSettingsLoaded);
+ pw.print(" mBatteryStatusSet=");
+ pw.println(mBatteryStatusSet);
+
+ pw.print(" mBatterySaverSnoozing=");
+ pw.println(mBatterySaverSnoozing);
+
+ pw.print(" mIsPowered=");
+ pw.println(mIsPowered);
+ pw.print(" mBatteryLevel=");
+ pw.println(mBatteryLevel);
+ pw.print(" mIsBatteryLevelLow=");
+ pw.println(mIsBatteryLevelLow);
+
+ pw.print(" mSettingBatterySaverEnabled=");
+ pw.println(mSettingBatterySaverEnabled);
+ pw.print(" mSettingBatterySaverEnabledSticky=");
+ pw.println(mSettingBatterySaverEnabledSticky);
+ pw.print(" mSettingBatterySaverTriggerThreshold=");
+ pw.println(mSettingBatterySaverTriggerThreshold);
+ }
+ }
+
+ public void dumpProto(ProtoOutputStream proto, long tag) {
+ synchronized (mLock) {
+ final long token = proto.start(tag);
+
+ proto.write(BatterySaverStateMachineProto.ENABLED,
+ mBatterySaverController.isEnabled());
+
+ proto.write(BatterySaverStateMachineProto.BOOT_COMPLETED, mBootCompleted);
+ proto.write(BatterySaverStateMachineProto.SETTINGS_LOADED, mSettingsLoaded);
+ proto.write(BatterySaverStateMachineProto.BATTERY_STATUS_SET, mBatteryStatusSet);
+
+ proto.write(BatterySaverStateMachineProto.BATTERY_SAVER_SNOOZING,
+ mBatterySaverSnoozing);
+
+ proto.write(BatterySaverStateMachineProto.IS_POWERED, mIsPowered);
+ proto.write(BatterySaverStateMachineProto.BATTERY_LEVEL, mBatteryLevel);
+ proto.write(BatterySaverStateMachineProto.IS_BATTERY_LEVEL_LOW, mIsBatteryLevelLow);
+
+ proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_ENABLED,
+ mSettingBatterySaverEnabled);
+ proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_ENABLED_STICKY,
+ mSettingBatterySaverEnabledSticky);
+ proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_TRIGGER_THRESHOLD,
+ mSettingBatterySaverTriggerThreshold);
+
+ proto.end(token);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 74c5ee9c3520..b3d28fcbc4f1 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -972,10 +972,13 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
public void triggerUidSnapshot() {
enforceCallingPermission();
synchronized (sStatsdLock) {
+ final long token = Binder.clearCallingIdentity();
try {
informAllUidsLocked(mContext);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to trigger uid snapshot.", e);
+ } finally {
+ restoreCallingIdentity(token);
}
}
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 427b9d2da948..89efe12927ba 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -86,6 +86,7 @@ cc_defaults {
"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 f37f87060fa6..ff1ec04cb23e 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 @@ static jint android_server_UsbDeviceManager_getAudioMode(JNIEnv* /* env */, jobj
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 @@ static const JNINativeMethod method_table[] = {
(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 2e07703020e8..1e216a3cff86 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 class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
public static class DevicePolicyData {
- @NonNull PasswordMetrics mActivePasswordMetrics = new PasswordMetrics();
int mFailedPasswordAttempts = 0;
- boolean mPasswordStateHasBeenSetSinceBoot = false;
boolean mPasswordValidAtLastCheckpoint = true;
int mUserHandle;
@@ -628,6 +626,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
/**
+ * 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
validateQualityConstant(metrics.quality);
- DevicePolicyData policy = getUserData(userHandle);
synchronized (this) {
- policy.mActivePasswordMetrics = metrics;
- policy.mPasswordStateHasBeenSetSinceBoot = true;
+ mUserPasswordMetrics.put(userHandle, metrics);
}
}
@@ -7640,6 +7667,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return DevicePolicyManager.STATE_USER_UNMANAGED;
}
+ enforceManageUsers();
int userHandle = mInjector.userHandleGetCallingUserId();
return getUserProvisioningState(userHandle);
}
@@ -7833,8 +7861,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@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 0268519795ba..836611489200 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 @@ class Owners {
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/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 376f5b193716..77653872e5ee 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -42,11 +42,13 @@ import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Debug;
@@ -101,8 +103,8 @@ public class RecentTasksTest extends ActivityTestsBase {
private TestRecentTasks mRecentTasks;
private TestRunningTasks mRunningTasks;
- private static ArrayList<TaskRecord> mTasks = new ArrayList<>();
- private static ArrayList<TaskRecord> mSameDocumentTasks = new ArrayList<>();
+ private ArrayList<TaskRecord> mTasks;
+ private ArrayList<TaskRecord> mSameDocumentTasks;
private CallbacksRecorder mCallbacksRecorder;
@@ -155,12 +157,14 @@ public class RecentTasksTest extends ActivityTestsBase {
mRecentTasks.registerCallback(mCallbacksRecorder);
QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
+ mTasks = new ArrayList<>();
mTasks.add(createTaskBuilder(".Task1").build());
mTasks.add(createTaskBuilder(".Task2").build());
mTasks.add(createTaskBuilder(".Task3").build());
mTasks.add(createTaskBuilder(".Task4").build());
mTasks.add(createTaskBuilder(".Task5").build());
+ mSameDocumentTasks = new ArrayList<>();
mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
}
@@ -294,7 +298,32 @@ public class RecentTasksTest extends ActivityTestsBase {
assertTrue(mCallbacksRecorder.added.contains(task2));
assertTrue(mCallbacksRecorder.trimmed.isEmpty());
assertTrue(mCallbacksRecorder.removed.isEmpty());
+ }
+ @Test
+ public void testAddTaskCompatibleActivityType_expectRemove() throws Exception {
+ Configuration config1 = new Configuration();
+ config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ TaskRecord task1 = createTaskBuilder(".Task1")
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ .setStack(mStack)
+ .build();
+ task1.onConfigurationChanged(config1);
+ assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
+ mRecentTasks.add(task1);
+ mCallbacksRecorder.clear();
+
+ TaskRecord task2 = createTaskBuilder(".Task1")
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ .setStack(mStack)
+ .build();
+ assertTrue(task2.getActivityType() == ACTIVITY_TYPE_STANDARD);
+ mRecentTasks.add(task2);
+ assertTrue(mCallbacksRecorder.added.size() == 1);
+ assertTrue(mCallbacksRecorder.added.contains(task2));
+ assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+ assertTrue(mCallbacksRecorder.removed.size() == 1);
+ assertTrue(mCallbacksRecorder.removed.contains(task1));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index ea207f1f1910..9e6055d55e0f 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -29,6 +29,9 @@ import com.android.server.am.TaskPersister;
import java.io.File;
import java.util.Random;
+/**
+ * atest FrameworksServicesTests:TaskPersisterTest
+ */
public class TaskPersisterTest extends AndroidTestCase {
private static final String TEST_USER_NAME = "AM-Test-User";
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 43490d312dbc..a64efb793e5b 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 class DevicePolicyManagerTest extends DpmTestBase {
}
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 class DevicePolicyManagerTest extends DpmTestBase {
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 class DevicePolicyManagerTest extends DpmTestBase {
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 @@ public class DevicePolicyManagerTest extends DpmTestBase {
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 class DevicePolicyManagerTest extends DpmTestBase {
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 class DevicePolicyManagerTest extends DpmTestBase {
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 class DevicePolicyManagerTest extends DpmTestBase {
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 class DevicePolicyManagerTest extends DpmTestBase {
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 class DevicePolicyManagerTest extends DpmTestBase {
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 class DevicePolicyManagerTest extends DpmTestBase {
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 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
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/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 445e50a4f819..06b94cb4928b 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
@@ -144,6 +144,13 @@ public class RecoverableKeyStoreManagerTest {
private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
private static final String TEST_ROOT_CERT_ALIAS = "";
+ private static final KeyChainProtectionParams TEST_PROTECTION_PARAMS =
+ new KeyChainProtectionParams.Builder()
+ .setUserSecretType(TYPE_LOCKSCREEN)
+ .setLockScreenUiFormat(UI_FORMAT_PASSWORD)
+ .setKeyDerivationParams(KeyDerivationParams.createSha256Params(TEST_SALT))
+ .setSecret(TEST_SECRET)
+ .build();
@Mock private Context mMockContext;
@Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
@@ -459,12 +466,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
verify(mMockContext, times(1))
.enforceCallingOrSelfPermission(
@@ -481,12 +483,7 @@ public class RecoverableKeyStoreManagerTest {
RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
assertEquals(1, mRecoverySessionStorage.size());
RecoverySessionStorage.Entry entry =
@@ -503,12 +500,7 @@ public class RecoverableKeyStoreManagerTest {
RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
verify(mMockContext, times(2))
.enforceCallingOrSelfPermission(
@@ -522,12 +514,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
assertEquals(1, mRecoverySessionStorage.size());
RecoverySessionStorage.Entry entry =
@@ -543,12 +530,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
mRecoverableKeyStoreManager.closeSession(TEST_SESSION_ID);
@@ -562,12 +544,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
mRecoverableKeyStoreManager.closeSession("some random session");
@@ -604,18 +581,14 @@ public class RecoverableKeyStoreManagerTest {
public void startRecoverySession_throwsIfPublicKeysMismatch() throws Exception {
byte[] vaultParams = TEST_VAULT_PARAMS.clone();
vaultParams[1] ^= (byte) 1; // Flip 1 bit
+
try {
mRecoverableKeyStoreManager.startRecoverySession(
TEST_SESSION_ID,
TEST_PUBLIC_KEY,
vaultParams,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
fail("should have thrown");
} catch (ServiceSpecificException e) {
assertThat(e.getMessage()).contains("do not match");
@@ -650,12 +623,7 @@ public class RecoverableKeyStoreManagerTest {
RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
vaultParams,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
fail("should have thrown");
} catch (ServiceSpecificException e) {
assertThat(e.getMessage()).contains("do not match");
@@ -673,12 +641,7 @@ public class RecoverableKeyStoreManagerTest {
RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
fail("should have thrown");
} catch (ServiceSpecificException e) {
assertThat(e.getMessage()).contains("empty");
@@ -698,12 +661,7 @@ public class RecoverableKeyStoreManagerTest {
RecoveryCertPath.createRecoveryCertPath(shortCertPath),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
fail("should have thrown");
} catch (ServiceSpecificException e) {
// expected
@@ -734,11 +692,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
try {
mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
@@ -758,11 +712,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
.getKeyClaimant();
SecretKey recoveryKey = randomRecoveryKey();
@@ -792,11 +742,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
.getKeyClaimant();
SecretKey recoveryKey = randomRecoveryKey();
@@ -816,11 +762,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
.getKeyClaimant();
SecretKey recoveryKey = randomRecoveryKey();
@@ -850,11 +792,7 @@ public class RecoverableKeyStoreManagerTest {
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
+ ImmutableList.of(TEST_PROTECTION_PARAMS));
byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
.getKeyClaimant();
SecretKey recoveryKey = randomRecoveryKey();
@@ -941,7 +879,7 @@ public class RecoverableKeyStoreManagerTest {
}
@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[]{};
@@ -960,6 +898,41 @@ public class RecoverableKeyStoreManagerTest {
}
@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 d61a294b1a39..ead817a21028 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
@@ -15,6 +15,15 @@ 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 final RecoverySnapshotStorage mRecoverySnapshotStorage = new RecoverySnapshotStorage();
@@ -26,26 +35,17 @@ public class RecoverySnapshotStorageTest {
@Test
public void get_returnsSetSnapshot() {
int userId = 1000;
- KeyChainSnapshot keyChainSnapshot = new KeyChainSnapshot(
- /*snapshotVersion=*/ 1,
- new ArrayList<>(),
- new ArrayList<>(),
- new byte[0]);
- mRecoverySnapshotStorage.put(userId, keyChainSnapshot);
-
- assertEquals(keyChainSnapshot, mRecoverySnapshotStorage.get(userId));
+
+ mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+ assertEquals(MINIMAL_KEYCHAIN_SNAPSHOT, mRecoverySnapshotStorage.get(userId));
}
@Test
public void remove_removesSnapshots() {
int userId = 1000;
- KeyChainSnapshot keyChainSnapshot = new KeyChainSnapshot(
- /*snapshotVersion=*/ 1,
- new ArrayList<>(),
- new ArrayList<>(),
- new byte[0]);
- mRecoverySnapshotStorage.put(userId, keyChainSnapshot);
+ mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
mRecoverySnapshotStorage.remove(userId);
assertNull(mRecoverySnapshotStorage.get(1000));
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
new file mode 100644
index 000000000000..ab640d66b222
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -0,0 +1,532 @@
+/*
+ * 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.power.batterysaver;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.provider.Settings.Global;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContext;
+
+import com.google.common.base.Objects;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+
+/**
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BatterySaverStateMachineTest {
+
+ private MyMockContext mMockContext;
+ private ContentResolver mMockContextResolver;
+ private BatterySaverController mMockBatterySaverController;
+ private Device mDevice;
+ private TestableBatterySaverStateMachine mTarget;
+
+ private class MyMockContext extends MockContext {
+ @Override
+ public ContentResolver getContentResolver() {
+ return mMockContextResolver;
+ }
+ }
+
+ private DevicePersistedState mPersistedState;
+
+ private class DevicePersistedState {
+ // Current battery level.
+ public int batteryLevel = 100;
+
+ // Whether battery level is currently low or not.
+ public boolean batteryLow = false;
+
+ // Whether the device is plugged in or not.
+ public boolean powered = false;
+
+ // Global settings.
+ public final HashMap<String, Integer> global = new HashMap<>();
+ }
+
+ /**
+ * This class simulates a device's volatile status that will be reset by {@link #initDevice()}.
+ */
+ private class Device {
+ public boolean batterySaverEnabled = false;
+
+ public int getLowPowerModeTriggerLevel() {
+ return mPersistedState.global.getOrDefault(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ }
+
+ public void setBatteryLevel(int level) {
+ mPersistedState.batteryLevel = level;
+ if (mPersistedState.batteryLevel <= Math.max(15, getLowPowerModeTriggerLevel())) {
+ mPersistedState.batteryLow = true;
+ } else if (mPersistedState.batteryLow
+ && (mPersistedState.batteryLevel >= (getLowPowerModeTriggerLevel() + 5))) {
+ mPersistedState.batteryLow = false;
+ }
+ pushBatteryStatus();
+ }
+
+ public void setPowered(boolean newPowered) {
+ mPersistedState.powered = newPowered;
+ pushBatteryStatus();
+ }
+
+ public void pushBatteryStatus() {
+ mTarget.setBatteryStatus(mPersistedState.powered, mPersistedState.batteryLevel,
+ mPersistedState.batteryLow);
+ }
+
+ public void pushGlobalSettings() {
+ mTarget.setSettingsLocked(
+ mPersistedState.global.getOrDefault(Global.LOW_POWER_MODE, 0) != 0,
+ mPersistedState.global.getOrDefault(Global.LOW_POWER_MODE_STICKY, 0) != 0,
+ mDevice.getLowPowerModeTriggerLevel());
+ }
+
+ public void putGlobalSetting(String key, int value) {
+ mPersistedState.global.put(key, value);
+ pushGlobalSettings();
+ }
+
+ public int getGlobalSetting(String key, int defValue) {
+ return mPersistedState.global.getOrDefault(key, defValue);
+ }
+ }
+
+ /**
+ * Test target class.
+ */
+ private class TestableBatterySaverStateMachine extends BatterySaverStateMachine {
+ public TestableBatterySaverStateMachine() {
+ super(mMockContext, mMockBatterySaverController);
+ }
+
+ @Override
+ protected void putGlobalSetting(String key, int value) {
+ if (Objects.equal(mPersistedState.global.get(key), value)) {
+ return;
+ }
+ mDevice.putGlobalSetting(key, value);
+ }
+
+ @Override
+ protected int getGlobalSetting(String key, int defValue) {
+ return mDevice.getGlobalSetting(key, defValue);
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mMockContext = new MyMockContext();
+ mMockContextResolver = mock(ContentResolver.class);
+ mMockBatterySaverController = mock(BatterySaverController.class);
+
+ doAnswer((inv) -> mDevice.batterySaverEnabled = inv.getArgument(0))
+ .when(mMockBatterySaverController).enableBatterySaver(anyBoolean());
+ when(mMockBatterySaverController.isEnabled())
+ .thenAnswer((inv) -> mDevice.batterySaverEnabled);
+
+ mPersistedState = new DevicePersistedState();
+ initDevice();
+ }
+
+ private void initDevice() {
+ mDevice = new Device();
+
+ mTarget = new TestableBatterySaverStateMachine();
+
+ mDevice.pushBatteryStatus();
+ mDevice.pushGlobalSettings();
+ mTarget.onBootCompleted();
+ }
+
+ @Test
+ public void testNoAutoBatterySaver() {
+ assertEquals(0, mDevice.getLowPowerModeTriggerLevel());
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(100, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(90);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(50);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(50, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(16);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(16, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // When LOW_POWER_MODE_TRIGGER_LEVEL is 0, 15% will still trigger low-battery, but
+ // BS wont be enabled.
+ mDevice.setBatteryLevel(15);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(15, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(10);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(10, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ // Manually enable BS.
+ mTarget.setBatterySaverEnabledManually(true);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(10, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(50);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(50, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Start charging. It'll disable BS.
+ mDevice.setPowered(true);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(50, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(60);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(60, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Unplug.
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(60, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(10);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(10, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(80);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Reboot the device.
+ initDevice();
+
+ assertEquals(true, mDevice.batterySaverEnabled); // Sticky.
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(30);
+ initDevice();
+
+ assertEquals(true, mDevice.batterySaverEnabled); // Still sticky.
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mTarget.setBatterySaverEnabledManually(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ initDevice(); // reboot.
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+ }
+
+ @Test
+ public void testAutoBatterySaver() {
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(100, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(90);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(51);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(51, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Hit the threshold. BS should be enabled.
+ mDevice.setBatteryLevel(50);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(50, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ // Battery goes up, but until it hits 55%, we still keep BS on.
+ mDevice.setBatteryLevel(54);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(54, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ // 50% + 5%, now BS will be off.
+ mDevice.setBatteryLevel(55);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(55, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(40);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(40, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(true);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(40, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(40, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze.
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(40, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(30);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ // Plug in and out, snooze will reset.
+ mDevice.setPowered(true);
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(true);
+ mDevice.setBatteryLevel(60);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(60, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(60, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(50);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(50, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(70);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(70, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Bump ump the threshold.
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 70);
+ mDevice.setBatteryLevel(mPersistedState.batteryLevel);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(70, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ // Then down.
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 60);
+ mDevice.setBatteryLevel(mPersistedState.batteryLevel);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(70, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Reboot in low state -> automatically enable BS.
+ mDevice.setPowered(false);
+ mDevice.setBatteryLevel(30);
+ mTarget.setBatterySaverEnabledManually(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ initDevice();
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+ }
+
+ @Test
+ public void testAutoBatterySaver_withSticky() {
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
+
+ mTarget.setBatterySaverEnabledManually(true);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(100, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(30);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(80);
+
+ assertEquals(true, mDevice.batterySaverEnabled); // Still enabled.
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setPowered(true);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(30);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled); // Restores BS.
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(true);
+ mDevice.setBatteryLevel(90);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ initDevice();
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mTarget.setBatterySaverEnabledManually(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ initDevice();
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+ }
+
+ @Test
+ public void testNoAutoBatterySaver_fromAdb() {
+
+ assertEquals(0, mDevice.getLowPowerModeTriggerLevel());
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(100, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(90);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Enable
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE, 1);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Disable
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE, 0);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Enable again
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE, 1);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ // Reboot -- setting BS from adb is also sticky.
+ initDevice();
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index ef0780a0eeff..9f8042c844de 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.FgThread;
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 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
private final UEventObserver mUEventObserver;
private static Set<Integer> sBlackListedInterfaces;
+ private HashMap<Long, FileDescriptor> mControlFds;
static {
sBlackListedInterfaces = new HashSet<>();
@@ -270,6 +272,18 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
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 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
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 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
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 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
// 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 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
updateUsbNotification(false);
updateAdbNotification(false);
if (mBootCompleted) {
- updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
- false);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
if ((mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) != 0) {
updateCurrentAccessory();
@@ -812,7 +823,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
&& mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
setScreenUnlockedFunctions();
} else {
- setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled);
+ setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
}
}
updateUsbFunctions();
@@ -847,8 +858,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
updateUsbNotification(false);
if (mBootCompleted) {
if (mHostConnected || prevHostConnected) {
- updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
- false);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
} else {
mPendingBootBroadcast = true;
@@ -994,7 +1004,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
protected void finishBoot() {
if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
if (mPendingBootBroadcast) {
- updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), false);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
mPendingBootBroadcast = false;
}
if (!mScreenLocked
@@ -1597,7 +1607,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
/**
* Start up dependent services.
*/
- updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), true);
+ updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
}
if (!waitForState(oemFunctions)) {
@@ -1943,7 +1953,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
if (mBootCompleted && isUsbDataTransferActive(functions)) {
// Start up dependent services.
- updateUsbStateBroadcastIfNeeded(functions, true);
+ updateUsbStateBroadcastIfNeeded(functions);
}
}
}
@@ -1979,6 +1989,22 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
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 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
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/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 0fcd075e8200..67ad0907181b 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -54,6 +54,7 @@ import java.util.LinkedList;
public class UsbHostManager {
private static final String TAG = UsbHostManager.class.getSimpleName();
private static final boolean DEBUG = false;
+ private static final int LINUX_FOUNDATION_VID = 0x1d6b;
private final Context mContext;
@@ -267,7 +268,6 @@ public class UsbHostManager {
}
private boolean isBlackListed(String deviceAddress) {
- Slog.i(TAG, "isBlackListed(" + deviceAddress + ")");
int count = mHostBlacklist.length;
for (int i = 0; i < count; i++) {
if (deviceAddress.startsWith(mHostBlacklist[i])) {
@@ -279,7 +279,6 @@ public class UsbHostManager {
/* returns true if the USB device should not be accessible by applications */
private boolean isBlackListed(int clazz, int subClass) {
- Slog.i(TAG, "isBlackListed(" + clazz + ", " + subClass + ")");
// blacklist hubs
if (clazz == UsbConstants.USB_CLASS_HUB) return true;
@@ -302,6 +301,40 @@ public class UsbHostManager {
}
}
+ private void logUsbDevice(UsbDescriptorParser descriptorParser) {
+ int vid = 0;
+ int pid = 0;
+ String mfg = "<unknown>";
+ String product = "<unknown>";
+ String version = "<unknown>";
+ String serial = "<unknown>";
+
+ UsbDeviceDescriptor deviceDescriptor = descriptorParser.getDeviceDescriptor();
+ if (deviceDescriptor != null) {
+ vid = deviceDescriptor.getVendorID();
+ pid = deviceDescriptor.getProductID();
+ mfg = deviceDescriptor.getMfgString(descriptorParser);
+ product = deviceDescriptor.getProductString(descriptorParser);
+ version = deviceDescriptor.getDeviceReleaseString();
+ serial = deviceDescriptor.getSerialString(descriptorParser);
+ }
+
+ if (vid == LINUX_FOUNDATION_VID) {
+ return; // don't care about OS-constructed virtual USB devices.
+ }
+ boolean hasAudio = descriptorParser.hasAudioInterface();
+ boolean hasHid = descriptorParser.hasHIDInterface();
+ boolean hasStorage = descriptorParser.hasStorageInterface();
+
+ String attachedString = "USB device attached: ";
+ attachedString += String.format("vidpid %04x:%04x", vid, pid);
+ attachedString += String.format(" mfg/product/ver/serial %s/%s/%s/%s",
+ mfg, product, version, serial);
+ attachedString += String.format(" hasAudio/HID/Storage: %b/%b/%b",
+ hasAudio, hasHid, hasStorage);
+ Slog.d(TAG, attachedString);
+ }
+
/* Called from JNI in monitorUsbHostBus() to report new USB devices
Returns true if successful, i.e. the USB Audio device descriptors are
correctly parsed and the unique device is added to the audio device list.
@@ -313,10 +346,18 @@ public class UsbHostManager {
Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
}
- // check class/subclass first as it is more likely to be blacklisted
- if (isBlackListed(deviceClass, deviceSubclass) || isBlackListed(deviceAddress)) {
+ if (isBlackListed(deviceAddress)) {
if (DEBUG) {
- Slog.d(TAG, "device is black listed");
+ Slog.d(TAG, "device address is black listed");
+ }
+ return false;
+ }
+ UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
+ logUsbDevice(parser);
+
+ if (isBlackListed(deviceClass, deviceSubclass)) {
+ if (DEBUG) {
+ Slog.d(TAG, "device class is black listed");
}
return false;
}
@@ -329,40 +370,31 @@ public class UsbHostManager {
return false;
}
- UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress);
- if (parser.parseDescriptors(descriptors)) {
-
- UsbDevice newDevice = parser.toAndroidUsbDevice();
- if (newDevice == null) {
- Slog.e(TAG, "Couldn't create UsbDevice object.");
- // Tracking
- addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
- parser.getRawDescriptors());
+ UsbDevice newDevice = parser.toAndroidUsbDevice();
+ if (newDevice == null) {
+ Slog.e(TAG, "Couldn't create UsbDevice object.");
+ // Tracking
+ addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
+ parser.getRawDescriptors());
+ } else {
+ mDevices.put(deviceAddress, newDevice);
+ Slog.d(TAG, "Added device " + newDevice);
+
+ // It is fine to call this only for the current user as all broadcasts are
+ // sent to all profiles of the user and the dialogs should only show once.
+ ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
+ if (usbDeviceConnectionHandler == null) {
+ getCurrentUserSettings().deviceAttached(newDevice);
} else {
- mDevices.put(deviceAddress, newDevice);
-
- // It is fine to call this only for the current user as all broadcasts are
- // sent to all profiles of the user and the dialogs should only show once.
- ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
- if (usbDeviceConnectionHandler == null) {
- getCurrentUserSettings().deviceAttached(newDevice);
- } else {
- getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
- usbDeviceConnectionHandler);
- }
+ getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
+ usbDeviceConnectionHandler);
+ }
- mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
+ mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
- // Tracking
- addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
- parser.getRawDescriptors());
- }
- } else {
- Slog.e(TAG, "Error parsing USB device descriptors for " + deviceAddress);
// Tracking
- addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADPARSE,
+ addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
parser.getRawDescriptors());
- return false;
}
}
@@ -376,18 +408,18 @@ public class UsbHostManager {
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
private void usbDeviceRemoved(String deviceAddress) {
- if (DEBUG) {
- Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") - start");
- }
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceAddress);
if (device != null) {
+ Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
mUsbAlsaManager.usbDeviceRemoved(deviceAddress/*device*/);
mSettingsManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null);
+ } else {
+ Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
}
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index c1a75912425a..e92bd74fadf2 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 @@ public class UsbService extends IUsbManager.Stub {
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/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 956efc075d0a..e61542824083 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -43,11 +43,6 @@ public final class UsbDescriptorParser {
// Obtained from the first AudioClass Header descriptor.
private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
- public UsbDescriptorParser(String deviceAddr) {
- mDeviceAddr = deviceAddr;
- mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
- }
-
/**
* Connect this parser to an existing set of already parsed descriptors.
* This is useful for reporting.
@@ -214,7 +209,7 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
- public boolean parseDescriptors(byte[] descriptors) {
+ public void parseDescriptors(byte[] descriptors) {
if (DEBUG) {
Log.d(TAG, "parseDescriptors() - start");
}
@@ -248,17 +243,6 @@ public final class UsbDescriptorParser {
if (DEBUG) {
Log.d(TAG, "parseDescriptors() - end " + mDescriptors.size() + " descriptors.");
}
- return true;
- }
-
- /**
- * @hide
- */
- public boolean parseDevice() {
- byte[] rawDescriptors = getRawDescriptors();
-
- return rawDescriptors != null
- ? parseDescriptors(rawDescriptors) : false;
}
public byte[] getRawDescriptors() {
@@ -491,9 +475,18 @@ public final class UsbDescriptorParser {
}
/**
+ *@ hide
+ */
+ public boolean hasAudioInterface() {
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
+ return !descriptors.isEmpty();
+ }
+
+ /**
* @hide
*/
- public boolean hasHIDDescriptor() {
+ public boolean hasHIDInterface() {
ArrayList<UsbDescriptor> descriptors =
getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID);
return !descriptors.isEmpty();
@@ -502,6 +495,15 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
+ public boolean hasStorageInterface() {
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_STORAGE);
+ return !descriptors.isEmpty();
+ }
+
+ /**
+ * @hide
+ */
public boolean hasMIDIInterface() {
ArrayList<UsbDescriptor> descriptors =
getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
@@ -540,7 +542,7 @@ public final class UsbDescriptorParser {
probability += 0.75f;
}
- if (hasMic && hasHIDDescriptor()) {
+ if (hasMic && hasHIDInterface()) {
probability += 0.25f;
}
@@ -593,7 +595,7 @@ public final class UsbDescriptorParser {
probability += 0.75f;
}
- if (hasSpeaker && hasHIDDescriptor()) {
+ if (hasSpeaker && hasHIDInterface()) {
probability += 0.25f;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index e31e3a312648..fae594ac4986 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -48,7 +48,7 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
private int mDeviceRelease; // 12:2 Device Release number - BCD
private byte mMfgIndex; // 14:1 Index of Manufacturer String Descriptor
private byte mProductIndex; // 15:1 Index of Product String Descriptor
- private byte mSerialNum; // 16:1 Index of Serial Number String Descriptor
+ private byte mSerialIndex; // 16:1 Index of Serial Number String Descriptor
private byte mNumConfigs; // 17:1 Number of Possible Configurations
private ArrayList<UsbConfigDescriptor> mConfigDescriptors =
@@ -91,16 +91,37 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
return mDeviceRelease;
}
+ // mDeviceRelease is binary-coded decimal, format DD.DD
+ public String getDeviceReleaseString() {
+ int hundredths = mDeviceRelease & 0xF;
+ int tenths = (mDeviceRelease & 0xF0) >> 4;
+ int ones = (mDeviceRelease & 0xF00) >> 8;
+ int tens = (mDeviceRelease & 0xF000) >> 12;
+ return String.format("%d.%d%d", tens * 10 + ones, tenths, hundredths);
+ }
+
public byte getMfgIndex() {
return mMfgIndex;
}
+ public String getMfgString(UsbDescriptorParser p) {
+ return p.getDescriptorString(mMfgIndex);
+ }
+
public byte getProductIndex() {
return mProductIndex;
}
- public byte getSerialNum() {
- return mSerialNum;
+ public String getProductString(UsbDescriptorParser p) {
+ return p.getDescriptorString(mProductIndex);
+ }
+
+ public byte getSerialIndex() {
+ return mSerialIndex;
+ }
+
+ public String getSerialString(UsbDescriptorParser p) {
+ return p.getDescriptorString(mSerialIndex);
}
public byte getNumConfigs() {
@@ -119,16 +140,14 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
Log.d(TAG, "toAndroid()");
}
- String mfgName = parser.getDescriptorString(mMfgIndex);
- String prodName = parser.getDescriptorString(mProductIndex);
+ String mfgName = getMfgString(parser);
+ String prodName = getProductString(parser);
if (DEBUG) {
Log.d(TAG, " mfgName:" + mfgName + " prodName:" + prodName);
}
- // Create version string in "%.%" format
- String versionString =
- Integer.toString(mDeviceRelease >> 8) + "." + (mDeviceRelease & 0xFF);
- String serialStr = parser.getDescriptorString(mSerialNum);
+ String versionString = getDeviceReleaseString();
+ String serialStr = getSerialString(parser);
if (DEBUG) {
Log.d(TAG, " versionString:" + versionString + " serialStr:" + serialStr);
}
@@ -159,7 +178,7 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
mDeviceRelease = stream.unpackUsbShort();
mMfgIndex = stream.getByte();
mProductIndex = stream.getByte();
- mSerialNum = stream.getByte();
+ mSerialIndex = stream.getByte();
mNumConfigs = stream.getByte();
return mLength;
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 08f8bb6494a0..890a6ea7c88e 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -18,11 +18,14 @@ package android.telephony;
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 @@ public abstract class CellIdentity implements Parcelable {
/** @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 @@ public abstract class CellIdentity implements Parcelable {
mMncStr = null;
log("invalid MNC format: " + mnc);
}
+ mAlphaLong = alphal;
+ mAlphaShort = alphas;
}
/** Implement the Parcelable interface */
@@ -138,6 +151,40 @@ public abstract class CellIdentity implements Parcelable {
}
/**
+ * @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 @@ public abstract class CellIdentity implements Parcelable {
dest.writeInt(type);
dest.writeString(mMccStr);
dest.writeString(mMncStr);
+ dest.writeString(mAlphaLong);
+ dest.writeString(mAlphaShort);
}
/**
@@ -154,7 +203,8 @@ public abstract class CellIdentity implements Parcelable {
* @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 713ac00a2d3a..58a2c455636b 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -49,23 +49,17 @@ public final class CellIdentityCdma extends CellIdentity {
* 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 final class CellIdentityCdma extends CellIdentity {
*/
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 @@ public final class CellIdentityCdma extends CellIdentity {
} else {
mLongitude = mLatitude = Integer.MAX_VALUE;
}
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityCdma(CellIdentityCdma cid) {
@@ -178,28 +170,10 @@ public final class CellIdentityCdma extends CellIdentity {
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 @@ public final class CellIdentityCdma extends CellIdentity {
&& 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 @@ public final class CellIdentityCdma extends CellIdentity {
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 @@ public final class CellIdentityCdma extends CellIdentity {
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 aae7929b6799..c697880faced 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -37,22 +37,16 @@ public final class CellIdentityGsm extends CellIdentity {
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 final class CellIdentityGsm extends CellIdentity {
*/
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 @@ public final class CellIdentityGsm extends CellIdentity {
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 @@ public final class CellIdentityGsm extends CellIdentity {
@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 @@ public final class CellIdentityGsm extends CellIdentity {
&& 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 @@ public final class CellIdentityGsm extends CellIdentity {
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 @@ public final class CellIdentityGsm extends CellIdentity {
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 9b3ef568751b..177fcedf8c15 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -37,10 +37,6 @@ public final class CellIdentityLte extends CellIdentity {
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 @@ public final class CellIdentityLte extends CellIdentity {
* @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 final class CellIdentityLte extends CellIdentity {
*/
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 @@ public final class CellIdentityLte extends CellIdentity {
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 @@ public final class CellIdentityLte extends CellIdentity {
@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 @@ public final class CellIdentityLte extends CellIdentity {
&& 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 @@ public final class CellIdentityLte extends CellIdentity {
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 @@ public final class CellIdentityLte extends CellIdentity {
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 7475c74ea70c..18ab6d4d2bd6 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -35,21 +35,15 @@ public final class CellIdentityTdscdma extends CellIdentity {
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 @@ public final class CellIdentityTdscdma extends CellIdentity {
* @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 final class CellIdentityTdscdma extends CellIdentity {
*/
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 @@ public final class CellIdentityTdscdma extends CellIdentity {
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 @@ public final class CellIdentityTdscdma extends CellIdentity {
&& mLac == o.mLac
&& mCid == o.mCid
&& mCpid == o.mCpid
- && mAlphaLong == o.mAlphaLong
- && mAlphaShort == o.mAlphaShort;
+ && super.equals(other);
}
@Override
@@ -219,8 +186,6 @@ public final class CellIdentityTdscdma extends CellIdentity {
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 @@ public final class CellIdentityTdscdma extends CellIdentity {
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 52fa54f373d4..984483edd8da 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -37,22 +37,16 @@ public final class CellIdentityWcdma extends CellIdentity {
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 final class CellIdentityWcdma extends CellIdentity {
*/
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 @@ public final class CellIdentityWcdma extends CellIdentity {
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 @@ public final class CellIdentityWcdma extends CellIdentity {
&& 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 @@ public final class CellIdentityWcdma extends CellIdentity {
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 @@ public final class CellIdentityWcdma extends CellIdentity {
mCid = in.readInt();
mPsc = in.readInt();
mUarfcn = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 26ffe3216bdc..6480aab06feb 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -21,31 +21,25 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.location.LocationManager;
import android.os.Binder;
-import android.os.Build;
import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
-import android.util.SparseBooleanArray;
+import android.util.Log;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
/**
* Helper for performing location access checks.
* @hide
*/
public final class LocationAccessPolicy {
+ private static final String LOG_TAG = LocationAccessPolicy.class.getSimpleName();
/**
* API to determine if the caller has permissions to get cell location.
*
@@ -94,10 +88,12 @@ public final class LocationAccessPolicy {
}
private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
- int locationMode = Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
- return locationMode != Settings.Secure.LOCATION_MODE_OFF
- && locationMode != Settings.Secure.LOCATION_MODE_SENSORS_ONLY;
+ LocationManager locationManager = context.getSystemService(LocationManager.class);
+ if (locationManager == null) {
+ Log.w(LOG_TAG, "Couldn't get location manager, denying location access");
+ return false;
+ }
+ return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
}
private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c5386eff612e..a12a7a0c7b48 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -40,6 +40,7 @@ import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -214,6 +215,10 @@ public class TelephonyManager {
return ActivityThread.currentOpPackageName();
}
+ private boolean isSystemProcess() {
+ return Process.myUid() == Process.SYSTEM_UID;
+ }
+
/**
* Returns the multi SIM variant
* Returns DSDS for Dual SIM Dual Standby
@@ -2866,15 +2871,18 @@ public class TelephonyManager {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null) {
Rlog.e(TAG, "IMSI error: Subscriber Info is null");
+ if (!isSystemProcess()) {
+ throw new RuntimeException("IMSI error: Subscriber Info is null");
+ }
return;
}
int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName());
} catch (RemoteException ex) {
Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing
- Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
}
}
@@ -3863,11 +3871,18 @@ public class TelephonyManager {
public void sendDialerSpecialCode(String inputCode) {
try {
final ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ if (!isSystemProcess()) {
+ throw new RuntimeException("Telephony service unavailable");
+ }
+ return;
+ }
telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode);
} catch (RemoteException ex) {
// This could happen if binder process crashes.
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
}
}
@@ -7836,6 +7851,9 @@ public class TelephonyManager {
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
}
}
}
diff --git a/tests/UsbTests/res/raw/usbdescriptors_massstorage.bin b/tests/UsbTests/res/raw/usbdescriptors_massstorage.bin
new file mode 100644
index 000000000000..1790369c5026
--- /dev/null
+++ b/tests/UsbTests/res/raw/usbdescriptors_massstorage.bin
Binary files differ
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java b/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
index f32395226f4a..ea027d7ae049 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
@@ -18,6 +18,7 @@ package com.android.server.usb;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import android.content.Context;
@@ -28,6 +29,7 @@ import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.server.usb.descriptors.UsbDescriptorParser;
+import com.android.server.usb.descriptors.UsbDeviceDescriptor;
import com.google.common.io.ByteStreams;
import java.io.InputStream;
@@ -61,14 +63,24 @@ public class UsbDescriptorParserTests {
}
// Testing same codepath as UsbHostManager.java:usbDeviceAdded
- UsbDescriptorParser parser = new UsbDescriptorParser("test-usb-addr");
- if (!parser.parseDescriptors(descriptors)) {
- fail("failed to parse descriptors.");
- }
+ UsbDescriptorParser parser = new UsbDescriptorParser("test-usb-addr", descriptors);
return parser;
}
- // A Headset has a microphone and a speaker and is a headset.
+ /** A Headset has a microphone and a speaker and is a headset.
+ * Descriptors for this example show up on lsusb -v with:
+ * bcdDevice 22.80
+ * and a UAC1 audio device with the following control interface:
+ * bInterfaceClass 1 Audio
+ * ...
+ * bDescriptorSubtype 2 (INPUT_TERMINAL)
+ * bTerminalID 1
+ * wTerminalType 0x0201 Microphone
+ * ...
+ * bDescriptorSubtype 3 (OUTPUT_TERMINAL)
+ * bTerminalID 15
+ * wTerminalType 0x0302 Headphones
+ */
@Test
@SmallTest
public void testHeadsetDescriptorParser() {
@@ -77,9 +89,24 @@ public class UsbDescriptorParserTests {
assertTrue(parser.hasOutput());
assertTrue(parser.isInputHeadset());
assertTrue(parser.isOutputHeadset());
+
+ assertTrue(parser.hasAudioInterface());
+ assertTrue(parser.hasHIDInterface());
+ assertFalse(parser.hasStorageInterface());
+
+ assertEquals(parser.getDeviceDescriptor().getDeviceReleaseString(), "22.80");
}
- // Headphones have no microphones but are considered a headset.
+ /** Headphones have no microphones but are considered a headset.
+ * Descriptors for this example show up on lsusb -v with:
+ * bcdDevice 22.80
+ * and a UAC1 audio device with the following control interface:
+ * bInterfaceClass 1 Audio
+ * ...
+ * bDescriptorSubtype 3 (OUTPUT_TERMINAL)
+ * bTerminalID 15
+ * wTerminalType 0x0302 Headphones
+ */
@Test
@SmallTest
public void testHeadphoneDescriptorParser() {
@@ -88,9 +115,24 @@ public class UsbDescriptorParserTests {
assertTrue(parser.hasOutput());
assertFalse(parser.isInputHeadset());
assertTrue(parser.isOutputHeadset());
+
+ assertTrue(parser.hasAudioInterface());
+ assertTrue(parser.hasHIDInterface());
+ assertFalse(parser.hasStorageInterface());
+
+ assertEquals(parser.getDeviceDescriptor().getDeviceReleaseString(), "22.80");
}
- // Line out has no microphones and aren't considered a headset.
+ /** Line out with no microphones aren't considered a headset.
+ * Descriptors for this example show up on lsusb -v with:
+ * bcdDevice 22.80
+ * and the following UAC1 audio control interface
+ * bInterfaceClass 1 Audio
+ * ...
+ * bDescriptorSubtype 3 (OUTPUT_TERMINAL)
+ * bTerminalID 15
+ * wTerminalType 0x0603 Line Connector
+ */
@Test
@SmallTest
public void testLineoutDescriptorParser() {
@@ -99,9 +141,20 @@ public class UsbDescriptorParserTests {
assertTrue(parser.hasOutput());
assertFalse(parser.isInputHeadset());
assertFalse(parser.isOutputHeadset());
+
+ assertTrue(parser.hasAudioInterface());
+ assertTrue(parser.hasHIDInterface());
+ assertFalse(parser.hasStorageInterface());
+
+ assertEquals(parser.getDeviceDescriptor().getDeviceReleaseString(), "22.80");
}
- // An HID-only device shouldn't be considered anything at all.
+ /** An HID-only device shouldn't be considered anything at all.
+ /* Descriptors show up on lsusb -v with:
+ * bcdDevice 22.80
+ * and a single HID interface,
+ * bInterfaceClass 3 Human Interface Device
+ */
@Test
@SmallTest
public void testNothingDescriptorParser() {
@@ -110,6 +163,34 @@ public class UsbDescriptorParserTests {
assertFalse(parser.hasOutput());
assertFalse(parser.isInputHeadset());
assertFalse(parser.isOutputHeadset());
+
+ assertFalse(parser.hasAudioInterface());
+ assertTrue(parser.hasHIDInterface());
+ assertFalse(parser.hasStorageInterface());
+
+ assertEquals(parser.getDeviceDescriptor().getDeviceReleaseString(), "22.80");
+ }
+
+ /** A USB mass-storage device.
+ * Shows up on lsusb -v with:
+ * bcdDevice 2.08
+ * and a single interface descriptor,
+ * bInterfaceClass 8 Mass Storage
+ */
+ @Test
+ @SmallTest
+ public void testMassStorageDescriptorParser() {
+ UsbDescriptorParser parser = loadParser(R.raw.usbdescriptors_massstorage);
+ assertFalse(parser.hasInput());
+ assertFalse(parser.hasOutput());
+ assertFalse(parser.isInputHeadset());
+ assertFalse(parser.isOutputHeadset());
+
+ assertFalse(parser.hasAudioInterface());
+ assertFalse(parser.hasHIDInterface());
+ assertTrue(parser.hasStorageInterface());
+
+ assertEquals(parser.getDeviceDescriptor().getDeviceReleaseString(), "2.08");
}
}
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
new file mode 100644
index 000000000000..d637ff346c82
--- /dev/null
+++ b/tools/stringslint/stringslint.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+
+# 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.
+
+"""
+Enforces common Android string best-practices. It ignores lint messages from
+a previous strings file, if provided.
+
+Usage: stringslint.py strings.xml
+Usage: stringslint.py strings.xml old_strings.xml
+"""
+
+import re, sys
+import lxml.etree as ET
+
+BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
+
+def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
+ # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
+ codes = []
+ if reset: codes.append("0")
+ else:
+ if not fg is None: codes.append("3%d" % (fg))
+ if not bg is None:
+ if not bright: codes.append("4%d" % (bg))
+ else: codes.append("10%d" % (bg))
+ if bold: codes.append("1")
+ elif dim: codes.append("2")
+ else: codes.append("22")
+ return "\033[%sm" % (";".join(codes))
+
+warnings = None
+
+def warn(tag, msg, actual, expected):
+ global warnings
+ key = "%s:%d" % (tag.attrib["name"], hash(msg))
+ value = "%sLine %d: '%s':%s %s" % (format(fg=YELLOW, bold=True),
+ tag.sourceline,
+ tag.attrib["name"],
+ format(reset=True),
+ msg)
+ if not actual is None: value += "\n\tActual: %s%s%s" % (format(dim=True),
+ actual,
+ format(reset=True))
+ if not expected is None: value += "\n\tExample: %s%s%s" % (format(dim=True),
+ expected,
+ format(reset=True))
+ warnings[key] = value
+
+def lint(path):
+ global warnings
+ warnings = {}
+
+ with open(path) as f:
+ raw = f.read()
+ if len(raw.strip()) == 0:
+ return warnings
+ tree = ET.fromstring(raw)
+ root = tree #tree.getroot()
+
+ last_comment = None
+ for child in root:
+ # TODO: handle plurals
+ if isinstance(child, ET._Comment):
+ last_comment = child
+ elif child.tag == "string":
+ # We always consume comment
+ comment = last_comment
+ last_comment = None
+
+ # Validate comment
+ if comment is None:
+ warn(child, "Missing string comment to aid translation",
+ None, None)
+ continue
+ if "do not translate" in comment.text.lower():
+ continue
+ if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false":
+ continue
+ if re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text) is None:
+ warn(child, "Missing CHAR LIMIT to aid translation",
+ repr(comment), "<!-- Description of string [CHAR LIMIT=32] -->")
+
+ # Look for common mistakes/substitutions
+ text = "".join(child.itertext()).strip()
+ if "'" in text:
+ warn(child, "Turned quotation mark glyphs are more polished",
+ text, "This doesn\u2019t need to \u2018happen\u2019 today")
+ if '"' in text and not text.startswith('"') and text.endswith('"'):
+ warn(child, "Turned quotation mark glyphs are more polished",
+ text, "This needs to \u201chappen\u201d today")
+ if "..." in text:
+ warn(child, "Ellipsis glyph is more polished",
+ text, "Loading\u2026")
+ if "wi-fi" in text.lower():
+ warn(child, "Non-breaking glyph is more polished",
+ text, "Wi\u2011Fi")
+ if "wifi" in text.lower():
+ warn(child, "Using non-standard spelling",
+ text, "Wi\u2011Fi")
+ if re.search("\d-\d", text):
+ warn(child, "Ranges should use en dash glyph",
+ text, "You will find this material in chapters 8\u201312")
+ if "--" in text:
+ warn(child, "Phrases should use em dash glyph",
+ text, "Upon discovering errors\u2014all 124 of them\u2014they recalled.")
+ if ". " in text:
+ warn(child, "Only use single space between sentences",
+ text, "First idea. Second idea.")
+
+ # When more than one substitution, require indexes
+ if len(re.findall("%[^%]", text)) > 1:
+ if len(re.findall("%[^\d]", text)) > 0:
+ warn(child, "Substitutions must be indexed",
+ text, "Add %1$s to %2$s")
+
+ # Require xliff substitutions
+ for gc in child.iter():
+ badsub = False
+ if gc.tail and re.search("%[^%]", gc.tail): badsub = True
+ if re.match("{.*xliff.*}g", gc.tag):
+ if "id" not in gc.attrib:
+ warn(child, "Substitutions must define id attribute",
+ None, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
+ if "example" not in gc.attrib:
+ warn(child, "Substitutions must define example attribute",
+ None, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
+ else:
+ if gc.text and re.search("%[^%]", gc.text): badsub = True
+ if badsub:
+ warn(child, "Substitutions must be inside xliff tags",
+ text, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
+
+ return warnings
+
+if len(sys.argv) > 2:
+ before = lint(sys.argv[2])
+else:
+ before = {}
+after = lint(sys.argv[1])
+
+for b in before:
+ if b in after:
+ del after[b]
+
+if len(after) > 0:
+ for a in sorted(after.keys()):
+ print after[a]
+ print
+ sys.exit(1)
diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh
new file mode 100755
index 000000000000..c79ba04d5e10
--- /dev/null
+++ b/tools/stringslint/stringslint_sha.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do
+ python $ANDROID_BUILD_TOP/frameworks/base/tools/stringslint/stringslint.py <(git show $1:$file) <(git show $1^:$file)
+done