diff options
132 files changed, 4397 insertions, 2146 deletions
diff --git a/Android.bp b/Android.bp index aeb3cebe8a13..cb7f7a2317d3 100644 --- a/Android.bp +++ b/Android.bp @@ -1221,7 +1221,7 @@ metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.x "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " + "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo" -doc_defaults { +stubs_defaults { name: "metalava-api-stubs-default", srcs: [ ":opt-telephony-srcs", @@ -1246,10 +1246,9 @@ doc_defaults { ], local_sourcepaths: frameworks_base_subdirs, installable: false, - metalava_enabled: true, - metalava_annotations_enabled: true, - metalava_previous_api: ":last-released-public-api", - metalava_merge_annotations_dirs: [ + annotations_enabled: true, + previous_api: ":last-released-public-api", + merge_annotations_dirs: [ "metalava-manual", "ojluni-annotated-stubs", ], @@ -1481,7 +1480,7 @@ java_library_static { ], } -droiddoc { +droidstubs { name: "hiddenapi-lists-docs", defaults: ["metalava-api-stubs-default"], arg_files: [ @@ -1540,7 +1539,7 @@ filegroup { ], } -droiddoc { +droidstubs { name: "api-stubs-docs", defaults: ["metalava-api-stubs-default"], api_filename: "public_api.txt", @@ -1562,7 +1561,7 @@ droiddoc { }, } -droiddoc { +droidstubs { name: "system-api-stubs-docs", defaults: ["metalava-api-stubs-default"], api_tag_name: "SYSTEM", @@ -1586,7 +1585,7 @@ droiddoc { }, } -droiddoc { +droidstubs { name: "test-api-stubs-docs", defaults: ["metalava-api-stubs-default"], api_tag_name: "TEST", diff --git a/api/current.txt b/api/current.txt index e31f8a05611d..2c2bc5f4d21c 100755 --- a/api/current.txt +++ b/api/current.txt @@ -934,6 +934,7 @@ package android { field public static final int minSdkVersion = 16843276; // 0x101020c field public static final int minWidth = 16843071; // 0x101013f field public static final int minimumHorizontalAngle = 16843901; // 0x101047d + field public static final int minimumUiTimeout = 16844174; // 0x101058e field public static final int minimumVerticalAngle = 16843902; // 0x101047e field public static final int mipMap = 16843725; // 0x10103cd field public static final int mirrorForRtl = 16843726; // 0x10103ce @@ -2874,10 +2875,12 @@ package android.accessibilityservice { method public int getCapabilities(); method public deprecated java.lang.String getDescription(); method public java.lang.String getId(); + method public int getMinimumUiTimeoutMillis(); method public android.content.pm.ResolveInfo getResolveInfo(); method public java.lang.String getSettingsActivityName(); method public java.lang.String loadDescription(android.content.pm.PackageManager); method public java.lang.CharSequence loadSummary(android.content.pm.PackageManager); + method public void setMinimumUiTimeoutMillis(int); method public void writeToParcel(android.os.Parcel, int); field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10 field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20 @@ -42654,6 +42657,7 @@ package android.telephony { method public static int[] getSubscriptionIds(int); method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int); method public boolean isNetworkRoaming(int); + method public static boolean isValidSubscriptionId(int); method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method public void setSubscriptionOverrideCongested(int, boolean, long); @@ -49546,6 +49550,7 @@ package android.view.accessibility { method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList(); method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int); method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(); + method public int getMinimumUiTimeoutMillis(); method public void interrupt(); method public static boolean isAccessibilityButtonSupported(); method public boolean isEnabled(); diff --git a/api/system-current.txt b/api/system-current.txt index 1e1c621a561d..e2c59cb10dd4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6785,7 +6785,6 @@ package android.webkit { method public static android.content.pm.PackageInfo getLoadedPackageInfo(); method public static int loadWebViewNativeLibraryFromPackage(java.lang.String, java.lang.ClassLoader); method public static void prepareWebViewInZygote(); - field public static final java.lang.String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = "persist.sys.webview.vmsize"; field public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; // 0x2 field public static final int LIBLOAD_FAILED_JNI_CALL = 7; // 0x7 field public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; // 0x4 diff --git a/api/test-current.txt b/api/test-current.txt index 0f89dfd12c19..28e97347db48 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1336,30 +1336,47 @@ package android.util.proto { method public void writeRawZigZag64(long); } - public final class ProtoOutputStream { + public final class ProtoInputStream extends android.util.proto.ProtoStream { + ctor public ProtoInputStream(java.io.InputStream, int); + ctor public ProtoInputStream(java.io.InputStream); + ctor public ProtoInputStream(byte[]); + method public int decodeZigZag32(int); + method public long decodeZigZag64(long); + method public java.lang.String dumpDebugData(); + method public void end(long); + method public int getFieldNumber(); + method public int getOffset(); + method public int getWireType(); + method public boolean isNextField(long) throws java.io.IOException; + method public int nextField() throws java.io.IOException; + method public boolean readBoolean(long) throws java.io.IOException; + method public byte[] readBytes(long) throws java.io.IOException; + method public double readDouble(long) throws java.io.IOException; + method public float readFloat(long) throws java.io.IOException; + method public int readInt(long) throws java.io.IOException; + method public long readLong(long) throws java.io.IOException; + method public java.lang.String readString(long) throws java.io.IOException; + method public void skip() throws java.io.IOException; + method public long start(long) throws java.io.IOException; + field public static final int NO_MORE_FIELDS = -1; // 0xffffffff + } + + public final class ProtoOutputStream extends android.util.proto.ProtoStream { ctor public ProtoOutputStream(); ctor public ProtoOutputStream(int); ctor public ProtoOutputStream(java.io.OutputStream); ctor public ProtoOutputStream(java.io.FileDescriptor); method public static int checkFieldId(long, long); - method public static int convertObjectIdToOrdinal(int); method public void dump(java.lang.String); method public void end(long); method public deprecated void endObject(long); method public deprecated void endRepeatedObject(long); method public void flush(); method public byte[] getBytes(); - method public static int getDepthFromToken(long); - method public static int getObjectIdFromToken(long); - method public static boolean getRepeatedFromToken(long); - method public static int getSizePosFromToken(long); - method public static int getTagSizeFromToken(long); method public static long makeFieldId(int, long); - method public static long makeToken(int, boolean, int, int, int); method public long start(long); method public deprecated long startObject(long); method public deprecated long startRepeatedObject(long); - method public static java.lang.String token2String(long); method public void write(long, double); method public void write(long, float); method public void write(long, int); @@ -1416,6 +1433,27 @@ package android.util.proto { method public void writeTag(int, int); method public deprecated void writeUInt32(long, int); method public deprecated void writeUInt64(long, long); + } + + public class ProtoParseException extends java.lang.RuntimeException { + ctor public ProtoParseException(java.lang.String); + } + + public abstract class ProtoStream { + ctor public ProtoStream(); + method public static int convertObjectIdToOrdinal(int); + method public static int getDepthFromToken(long); + method public static java.lang.String getFieldCountString(long); + method public static java.lang.String getFieldIdString(long); + method public static java.lang.String getFieldTypeString(long); + method public static int getObjectIdFromToken(long); + method public static int getOffsetFromToken(long); + method public static boolean getRepeatedFromToken(long); + method public static int getTagSizeFromToken(long); + method public static java.lang.String getWireTypeString(int); + method public static long makeFieldId(int, long); + method public static long makeToken(int, boolean, int, int, int); + method public static java.lang.String token2String(long); field public static final long FIELD_COUNT_MASK = 16492674416640L; // 0xf0000000000L field public static final long FIELD_COUNT_PACKED = 5497558138880L; // 0x50000000000L field public static final long FIELD_COUNT_REPEATED = 2199023255552L; // 0x20000000000L @@ -1435,6 +1473,7 @@ package android.util.proto { field public static final long FIELD_TYPE_INT64 = 12884901888L; // 0x300000000L field public static final long FIELD_TYPE_MASK = 1095216660480L; // 0xff00000000L field public static final long FIELD_TYPE_MESSAGE = 47244640256L; // 0xb00000000L + field protected static final java.lang.String[] FIELD_TYPE_NAMES; field public static final long FIELD_TYPE_SFIXED32 = 64424509440L; // 0xf00000000L field public static final long FIELD_TYPE_SFIXED64 = 68719476736L; // 0x1000000000L field public static final int FIELD_TYPE_SHIFT = 32; // 0x20 @@ -1444,7 +1483,6 @@ package android.util.proto { field public static final long FIELD_TYPE_UINT32 = 55834574848L; // 0xd00000000L field public static final long FIELD_TYPE_UINT64 = 17179869184L; // 0x400000000L field public static final long FIELD_TYPE_UNKNOWN = 0L; // 0x0L - field public static final java.lang.String TAG = "ProtoOutputStream"; field public static final int WIRE_TYPE_END_GROUP = 4; // 0x4 field public static final int WIRE_TYPE_FIXED32 = 5; // 0x5 field public static final int WIRE_TYPE_FIXED64 = 1; // 0x1 @@ -1454,8 +1492,8 @@ package android.util.proto { field public static final int WIRE_TYPE_VARINT = 0; // 0x0 } - public class ProtoParseException extends java.lang.RuntimeException { - ctor public ProtoParseException(java.lang.String); + public class WireTypeMismatchException extends android.util.proto.ProtoParseException { + ctor public WireTypeMismatchException(java.lang.String); } } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index d117f390705d..2c07431ee38a 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -91,7 +91,7 @@ message Atom { ActivityForegroundStateChanged activity_foreground_state_changed = 42; IsolatedUidChanged isolated_uid_changed = 43; PacketWakeupOccurred packet_wakeup_occurred = 44; - // 45 is available + WallClockTimeShifted wall_clock_time_shifted = 45; AnomalyDetected anomaly_detected = 46; AppBreadcrumbReported app_breadcrumb_reported = 47; AppStartOccurred app_start_occurred = 48; @@ -1479,6 +1479,18 @@ message AppBreadcrumbReported { } /** + * Logs the wall-clock time when a significant wall-clock time shift occurs. + * For example, this could be due to the user manually changing the time. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/AlarmManagerService.java + */ +message WallClockTimeShifted { + // New wall-clock time in milliseconds, according to System.currentTimeMillis(). + optional int64 wall_clock_timestamp_millis = 1; +} + +/** * Logs when statsd detects an anomaly. * * Logged from: diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index eec90fc8d053..afd8ec2212fe 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -49,7 +49,6 @@ const int FIELD_ID_DATA = 1; // for EventMetricData const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1; const int FIELD_ID_ATOMS = 2; -const int FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS = 3; EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric, const int conditionIndex, @@ -146,13 +145,9 @@ void EventMetricProducer::onMatchedLogEventInternalLocked( if (truncateTimestamp) { mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long)truncateTimestampNsToFiveMinutes(event.GetElapsedTimestampNs())); - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS, - (long long)truncateTimestampNsToFiveMinutes(getWallClockNs())); } else { mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long)event.GetElapsedTimestampNs()); - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS, - (long long)getWallClockNs()); } uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS); diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index bcfcd7a4f06e..02b97734d3ff 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -63,7 +63,6 @@ const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5; // for GaugeBucketInfo const int FIELD_ID_ATOM = 3; const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4; -const int FIELD_ID_WALL_CLOCK_ATOM_TIMESTAMP = 5; const int FIELD_ID_BUCKET_NUM = 6; const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7; const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8; @@ -286,16 +285,9 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, const int64_t elapsedTimestampNs = truncateTimestamp ? truncateTimestampNsToFiveMinutes(atom.mElapsedTimestamps) : atom.mElapsedTimestamps; - const int64_t wallClockNs = truncateTimestamp ? - truncateTimestampNsToFiveMinutes(atom.mWallClockTimestampNs) : - atom.mWallClockTimestampNs; protoOutput->write( FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP, (long long)elapsedTimestampNs); - protoOutput->write( - FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | - FIELD_ID_WALL_CLOCK_ATOM_TIMESTAMP, - (long long)wallClockNs); } } protoOutput->end(bucketInfoToken); @@ -450,7 +442,7 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) { return; } - GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs, getWallClockNs()); + GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs); (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom); // Anomaly detection on gauge metric only works when there is one numeric // field specified. diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index e3da5db4b90e..6379389218fd 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -33,12 +33,11 @@ namespace os { namespace statsd { struct GaugeAtom { - GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int64_t wallClockNs) - : mFields(fields), mElapsedTimestamps(elapsedTimeNs), mWallClockTimestampNs(wallClockNs) { + GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs) + : mFields(fields), mElapsedTimestamps(elapsedTimeNs) { } std::shared_ptr<vector<FieldValue>> mFields; int64_t mElapsedTimestamps; - int64_t mWallClockTimestampNs; }; struct GaugeBucket { diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index db7e680f1d37..ab0b23cc009d 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -46,7 +46,7 @@ message EventMetricData { optional Atom atom = 2; - optional int64 wall_clock_timestamp_nanos = 3; + optional int64 wall_clock_timestamp_nanos = 3 [deprecated = true]; } message CountBucketInfo { @@ -142,7 +142,7 @@ message GaugeBucketInfo { repeated int64 elapsed_timestamp_nanos = 4; - repeated int64 wall_clock_timestamp_nanos = 5; + repeated int64 wall_clock_timestamp_nanos = 5 [deprecated = true]; optional int64 bucket_num = 6; diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp index ea6eb3f3d712..5b6f167984dc 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp @@ -148,7 +148,7 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { EXPECT_EQ(1, data.bucket_info(0).atom_size()); EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size()); + EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty()); @@ -271,7 +271,7 @@ TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) { EXPECT_EQ(1, data.bucket_info(0).atom_size()); EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size()); + EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty()); @@ -375,7 +375,6 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { EXPECT_EQ(1, data.bucket_info(0).atom_size()); EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size()); EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty()); diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp index 3de8d0da2c3b..5c1ef01dd576 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp @@ -173,7 +173,7 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { if (sampling_type == GaugeMetric::ALL_CONDITION_CHANGES) { EXPECT_EQ(2, data.bucket_info(0).atom_size()); EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(2, data.bucket_info(0).wall_clock_timestamp_nanos_size()); + EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); @@ -192,7 +192,6 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(1, data.bucket_info(1).atom_size()); EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); - EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, @@ -206,7 +205,6 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(2, data.bucket_info(2).atom_size()); EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); - EXPECT_EQ(2, data.bucket_info(2).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, @@ -226,7 +224,6 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { } else { EXPECT_EQ(1, data.bucket_info(0).atom_size()); EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); @@ -239,7 +236,6 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(1, data.bucket_info(1).atom_size()); EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); - EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, @@ -253,7 +249,6 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(1, data.bucket_info(2).atom_size()); EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); - EXPECT_EQ(1, data.bucket_info(2).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, @@ -276,7 +271,6 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(1, data.bucket_info_size()); EXPECT_EQ(1, data.bucket_info(0).atom_size()); EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index f0a0e88310f9..759443dcf476 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -76,6 +76,7 @@ import java.util.List; * @attr ref android.R.styleable#AccessibilityService_notificationTimeout * @attr ref android.R.styleable#AccessibilityService_packageNames * @attr ref android.R.styleable#AccessibilityService_settingsActivity + * @attr ref android.R.styleable#AccessibilityService_minimumUiTimeout * @see AccessibilityService * @see android.view.accessibility.AccessibilityEvent * @see android.view.accessibility.AccessibilityManager @@ -426,6 +427,13 @@ public class AccessibilityServiceInfo implements Parcelable { public boolean crashed; /** + * The minimum timeout in milliseconds that UI controls need to remain on the screen. + * + * @see #setMinimumUiTimeoutMillis + */ + private int mMinimumUiTimeout; + + /** * The component name the accessibility service. */ private ComponentName mComponentName; @@ -529,6 +537,9 @@ public class AccessibilityServiceInfo implements Parcelable { notificationTimeout = asAttributes.getInt( com.android.internal.R.styleable.AccessibilityService_notificationTimeout, 0); + mMinimumUiTimeout = asAttributes.getInt( + com.android.internal.R.styleable.AccessibilityService_minimumUiTimeout, + 0); flags = asAttributes.getInt( com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0); mSettingsActivityName = asAttributes.getString( @@ -598,6 +609,7 @@ public class AccessibilityServiceInfo implements Parcelable { packageNames = other.packageNames; feedbackType = other.feedbackType; notificationTimeout = other.notificationTimeout; + mMinimumUiTimeout = other.mMinimumUiTimeout; flags = other.flags; } @@ -755,6 +767,29 @@ public class AccessibilityServiceInfo implements Parcelable { return null; } + /** + * Set the minimum time that controls need to remain on the screen to support the user. + * <p> + * <strong>This value can be dynamically set at runtime by + * {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}.</strong> + * </p> + * + * @param timeout The timeout in milliseconds. + */ + public void setMinimumUiTimeoutMillis(int timeout) { + mMinimumUiTimeout = timeout; + } + + /** + * Get the minimum ui timeout. + * + * @see #setMinimumUiTimeoutMillis + * @return The timeout in milliseconds. + */ + public int getMinimumUiTimeoutMillis() { + return mMinimumUiTimeout; + } + /** {@hide} */ public boolean isDirectBootAware() { return ((flags & FLAG_FORCE_DIRECT_BOOT_AWARE) != 0) @@ -773,6 +808,7 @@ public class AccessibilityServiceInfo implements Parcelable { parcel.writeStringArray(packageNames); parcel.writeInt(feedbackType); parcel.writeLong(notificationTimeout); + parcel.writeInt(mMinimumUiTimeout); parcel.writeInt(flags); parcel.writeInt(crashed ? 1 : 0); parcel.writeParcelable(mComponentName, flagz); @@ -790,6 +826,7 @@ public class AccessibilityServiceInfo implements Parcelable { packageNames = parcel.readStringArray(); feedbackType = parcel.readInt(); notificationTimeout = parcel.readLong(); + mMinimumUiTimeout = parcel.readInt(); flags = parcel.readInt(); crashed = parcel.readInt() != 0; mComponentName = parcel.readParcelable(this.getClass().getClassLoader()); @@ -840,6 +877,8 @@ public class AccessibilityServiceInfo implements Parcelable { stringBuilder.append(", "); stringBuilder.append("notificationTimeout: ").append(notificationTimeout); stringBuilder.append(", "); + stringBuilder.append("minimumUiTimeout: ").append(mMinimumUiTimeout); + stringBuilder.append(", "); appendFlags(stringBuilder, flags); stringBuilder.append(", "); stringBuilder.append("id: ").append(getId()); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index bf2d86096773..7b8664658422 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2310,7 +2310,7 @@ public class Activity extends ContextThemeWrapper * * @param newConfig The new device configuration. */ - public void onConfigurationChanged(Configuration newConfig) { + public void onConfigurationChanged(@NonNull Configuration newConfig) { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onConfigurationChanged " + this + ": " + newConfig); mCalled = true; @@ -6350,7 +6350,8 @@ public class Activity extends ContextThemeWrapper * @see android.view.Window#getLayoutInflater */ @Nullable - public View onCreateView(String name, Context context, AttributeSet attrs) { + public View onCreateView(@NonNull String name, @NonNull Context context, + @NonNull AttributeSet attrs) { return null; } @@ -6364,7 +6365,9 @@ public class Activity extends ContextThemeWrapper * @see android.view.LayoutInflater#createView * @see android.view.Window#getLayoutInflater */ - public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { + @Nullable + public View onCreateView(@Nullable View parent, @NonNull String name, + @NonNull Context context, @NonNull AttributeSet attrs) { if (!"fragment".equals(name)) { return onCreateView(name, context, attrs); } diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 636366d53af2..68b745d54bc0 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -127,7 +127,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } @CallSuper - public void onConfigurationChanged(Configuration newConfig) { + public void onConfigurationChanged(@NonNull Configuration newConfig) { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { for (int i=0; i<callbacks.length; i++) { diff --git a/core/java/android/content/ComponentCallbacks.java b/core/java/android/content/ComponentCallbacks.java index b96c8d38f99d..428545dbcd5e 100644 --- a/core/java/android/content/ComponentCallbacks.java +++ b/core/java/android/content/ComponentCallbacks.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.NonNull; import android.content.res.Configuration; /** @@ -44,7 +45,7 @@ public interface ComponentCallbacks { * * @param newConfig The new device configuration. */ - void onConfigurationChanged(Configuration newConfig); + void onConfigurationChanged(@NonNull Configuration newConfig); /** * This is called when the overall system is running low on memory, and diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 1ee3c933b326..cb336597cb8f 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -58,7 +58,7 @@ import java.util.List; * execute in parallel with any {@link Surface} initialization, such as waiting for a * {@link android.view.SurfaceView} to be ready as part of the UI initialization.</li> * - * <li>The third and most complex usage pattern inlvolves surface sharing. Once instantiated an + * <li>The third and most complex usage pattern involves surface sharing. Once instantiated an * OutputConfiguration can be enabled for surface sharing via {@link #enableSurfaceSharing}. This * must be done before creating a new capture session and enables calls to * {@link CameraCaptureSession#updateOutputConfiguration}. An OutputConfiguration with enabled diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java index 6d9ba778d09a..7e52ca331f9f 100644 --- a/core/java/android/hardware/display/BrightnessConfiguration.java +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -141,13 +141,6 @@ public final class BrightnessConfiguration implements Parcelable { private String mDescription; /** - * STOPSHIP remove when app has stopped using this. - * @hide - */ - public Builder() { - } - - /** * Constructs the builder with the control points for the brightness curve. * * Brightness curves must have strictly increasing ambient brightness values in lux and @@ -159,24 +152,6 @@ public final class BrightnessConfiguration implements Parcelable { * @throws IllegalArgumentException if the nit levels are not monotonically increasing. */ public Builder(float[] lux, float[] nits) { - setCurve(lux, nits); - } - - /** - * Sets the control points for the brightness curve. - * - * Brightness curves must have strictly increasing ambient brightness values in lux and - * monotonically increasing display brightness values in nits. In addition, the initial - * control point must be 0 lux. - * - * @throws IllegalArgumentException if the initial control point is not at 0 lux. - * @throws IllegalArgumentException if the lux levels are not strictly increasing. - * @throws IllegalArgumentException if the nit levels are not monotonically increasing. - * - * STOPSHIP remove when app has stopped using this. - * @hide - */ - public Builder setCurve(float[] lux, float[] nits) { Preconditions.checkNotNull(lux); Preconditions.checkNotNull(nits); if (lux.length == 0 || nits.length == 0) { @@ -190,11 +165,10 @@ public final class BrightnessConfiguration implements Parcelable { } Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux"); Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); - checkMonotonic(lux, true/*strictly increasing*/, "lux"); + checkMonotonic(lux, true /*strictly increasing*/, "lux"); checkMonotonic(nits, false /*strictly increasing*/, "nits"); mCurveLux = lux; mCurveNits = nits; - return this; } /** diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 9bcc6001615f..40465ceafcb8 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -197,7 +197,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * * <p>Example: "//www.google.com/search?q=android" * - * @return the decoded scheme-specific-part + * @return the encoded scheme-specific-part */ public abstract String getEncodedSchemeSpecificPart(); diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index 0fef78d1f4ba..3d4c00c3b480 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -1601,12 +1601,13 @@ public class BaseBundle { private void readFromParcelInner(Parcel parcel, int length) { if (length < 0) { throw new RuntimeException("Bad length in parcel: " + length); - } else if (length == 0) { // Empty Bundle or end of data. mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL; mParcelledByNative = false; return; + } else if (length % 4 != 0) { + throw new IllegalStateException("Bundle length is not aligned by 4: " + length); } final int magic = parcel.readInt(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e0073985ce78..026195e64a97 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6508,6 +6508,25 @@ public final class Settings { public static final String MULTI_PRESS_TIMEOUT = "multi_press_timeout"; /** + * Whether the user specifies a minimum ui timeout to override minimum ui timeout of + * accessibility service + * + * Type: int (0 for false, 1 for true) + * @hide + */ + public static final String ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED = + "accessibility_minimum_ui_timeout_enabled"; + + /** + * Setting that specifies ui minimum timeout in milliseconds. + * + * @see #ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED + * @hide + */ + public static final String ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS = + "accessibility_minimum_ui_timeout_ms"; + + /** * List of the enabled print services. * * N and beyond uses {@link #DISABLED_PRINT_SERVICES}. But this might be used in an upgrade @@ -8195,6 +8214,8 @@ public final class Settings { ZEN_SETTINGS_SUGGESTION_VIEWED, CHARGING_SOUNDS_ENABLED, CHARGING_VIBRATION_ENABLED, + ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, + ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, }; /** @@ -8349,6 +8370,8 @@ public final class Settings { VALIDATORS.put(ZEN_SETTINGS_SUGGESTION_VIEWED, BOOLEAN_VALIDATOR); VALIDATORS.put(CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(CHARGING_VIBRATION_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, NON_NEGATIVE_INTEGER_VALIDATOR); } /** diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index b5ade2a3b654..0476cf8961a5 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -39,7 +39,6 @@ public class FeatureFlagUtils { private static final Map<String, String> DEFAULT_FLAGS; static { DEFAULT_FLAGS = new HashMap<>(); - DEFAULT_FLAGS.put("settings_battery_display_app_list", "false"); DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false"); DEFAULT_FLAGS.put("settings_audio_switcher", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java new file mode 100644 index 000000000000..209451bcfe6a --- /dev/null +++ b/core/java/android/util/proto/ProtoInputStream.java @@ -0,0 +1,978 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util.proto; + +import android.annotation.TestApi; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; + +/** + * Class to read to a protobuf stream. + * + * Each read method takes an ID code from the protoc generated classes + * and return a value of the field. To read a nested object, call #start + * and then #end when you are done. + * + * The ID codes have type information embedded into them, so if you call + * the incorrect function you will get an IllegalArgumentException. + * + * nextField will return the field number of the next field, which can be + * matched to the protoc generated ID code and used to determine how to + * read the next field. + * + * It is STRONGLY RECOMMENDED to read from the ProtoInputStream with a switch + * statement wrapped in a while loop. Additionally, it is worth logging or + * storing unexpected fields or ones that do not match the expected wire type + * + * ex: + * void parseFromProto(ProtoInputStream stream) { + * while(stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + * try { + * switch (stream.getFieldNumber()) { + * case (int) DummyProto.NAME: + * mName = stream.readString(DummyProto.NAME); + * break; + * case (int) DummyProto.VALUE: + * mValue = stream.readInt(DummyProto.VALUE); + * break; + * default: + * LOG(TAG, "Unhandled field in proto!\n" + * + ProtoUtils.currentFieldToString(stream)); + * } + * } catch (WireTypeMismatchException wtme) { + * LOG(TAG, "Wire Type mismatch in proto!\n" + ProtoUtils.currentFieldToString(stream)); + * } + * } + * } + * + * @hide + */ +@TestApi +public final class ProtoInputStream extends ProtoStream { + + public static final int NO_MORE_FIELDS = -1; + + /** + * Our stream. If there is one. + */ + private InputStream mStream; + + /** + * The field number of the current field. Will be equal to NO_MORE_FIELDS if end of message is + * reached + */ + private int mFieldNumber; + + /** + * The wire type of the current field + */ + private int mWireType; + + private static final byte STATE_STARTED_FIELD_READ = 1 << 0; + private static final byte STATE_READING_PACKED = 1 << 1; + private static final byte STATE_FIELD_MISS = 2 << 1; + + /** + * Tracks some boolean states for the proto input stream + * bit 0: Started Field Read, true - tag has been read, ready to read field data. + * false - field data has been read, reading to start next field. + * bit 1: Reading Packed Field, true - currently reading values from a packed field + * false - not reading from packed field. + */ + private byte mState = 0; + + /** + * Keeps track of the currently read nested Objects, for end object sanity checking and debug + */ + private ArrayList<Long> mExpectedObjectTokenStack = null; + + /** + * Current nesting depth of start calls. + */ + private int mDepth = -1; + + /** + * Buffer for the to be read data. If mStream is not null, it will be constantly refilled from + * the stream. + */ + private byte[] mBuffer; + + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * Size of the buffer if reading from a stream. + */ + private final int mBufferSize; + + /** + * The number of bytes that have been skipped or dropped from the buffer. + */ + private int mDiscardedBytes = 0; + + /** + * Current offset in the buffer + * mOffset + mDiscardedBytes = current offset in proto binary + */ + private int mOffset = 0; + + /** + * Note the offset of the last byte in the buffer. Usually will equal the size of the buffer. + * mEnd + mDiscardedBytes = the last known byte offset + 1 + */ + private int mEnd = 0; + + /** + * Packed repeated fields are not read in one go. mPackedEnd keeps track of where the packed + * field ends in the proto binary if current field is packed. + */ + private int mPackedEnd = 0; + + /** + * Construct a ProtoInputStream on top of an InputStream to read a proto. Also specify the + * number of bytes the ProtoInputStream will buffer from the input stream + * + * @param stream from which the proto is read + */ + public ProtoInputStream(InputStream stream, int bufferSize) { + mStream = stream; + if (bufferSize > 0) { + mBufferSize = bufferSize; + } else { + mBufferSize = DEFAULT_BUFFER_SIZE; + } + mBuffer = new byte[mBufferSize]; + } + + /** + * Construct a ProtoInputStream on top of an InputStream to read a proto + * + * @param stream from which the proto is read + */ + public ProtoInputStream(InputStream stream) { + this(stream, DEFAULT_BUFFER_SIZE); + } + + /** + * Construct a ProtoInputStream to read a proto directly from a byte array + * + * @param buffer - the byte array to be parsed + */ + public ProtoInputStream(byte[] buffer) { + mBufferSize = buffer.length; + mEnd = buffer.length; + mBuffer = buffer; + mStream = null; + } + + /** + * Get the field number of the current field. + */ + public int getFieldNumber() { + return mFieldNumber; + } + + /** + * Get the wire type of the current field. + * + * @return an int that matches one of the ProtoStream WIRE_TYPE_ constants + */ + public int getWireType() { + if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) { + // mWireType got overwritten when STATE_READING_PACKED was set. Send length delimited + // constant instead + return WIRE_TYPE_LENGTH_DELIMITED; + } + return mWireType; + } + + /** + * Get the current offset in the proto binary. + */ + public int getOffset() { + return mOffset + mDiscardedBytes; + } + + /** + * Reads the tag of the next field from the stream. If previous field value was not read, its + * data will be skipped over. + * + * @return the field number of the next field + * @throws IOException if an I/O error occurs + */ + public int nextField() throws IOException { + + if ((mState & STATE_FIELD_MISS) == STATE_FIELD_MISS) { + // Data from the last nextField was not used, reuse the info + mState &= ~STATE_FIELD_MISS; + return mFieldNumber; + } + if ((mState & STATE_STARTED_FIELD_READ) == STATE_STARTED_FIELD_READ) { + // Field data was not read, skip to the next field + skip(); + mState &= ~STATE_STARTED_FIELD_READ; + } + if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) { + if (getOffset() < mPackedEnd) { + // In the middle of a packed field, return the same tag until last packed value + // has been read + mState |= STATE_STARTED_FIELD_READ; + return mFieldNumber; + } else if (getOffset() == mPackedEnd) { + // Reached the end of the packed field + mState &= ~STATE_READING_PACKED; + } else { + throw new ProtoParseException( + "Unexpectedly reached end of packed field at offset 0x" + + Integer.toHexString(mPackedEnd) + + dumpDebugData()); + } + } + + if ((mDepth >= 0) && (getOffset() == getOffsetFromToken( + mExpectedObjectTokenStack.get(mDepth)))) { + // reached end of a embedded message + mFieldNumber = NO_MORE_FIELDS; + } else { + readTag(); + } + return mFieldNumber; + } + + /** + * Attempt to guess the next field. If there is a match, the field data will be ready to read. + * If there is no match, nextField will need to be called to get the field number + * + * @return true if fieldId matches the next field, false if not + */ + public boolean isNextField(long fieldId) throws IOException { + if (nextField() == (int) fieldId) { + return true; + } + // Note to reuse the info from the nextField call in the next call. + mState |= STATE_FIELD_MISS; + return false; + } + + /** + * Read a single double. + * Will throw if the current wire type is not fixed64 + * + * @param fieldId - must match the current field number and field type + */ + public double readDouble(long fieldId) throws IOException { + assertFreshData(); + assertFieldNumber(fieldId); + checkPacked(fieldId); + + double value; + switch ((int) ((fieldId & FIELD_TYPE_MASK) + >>> FIELD_TYPE_SHIFT)) { + case (int) (FIELD_TYPE_DOUBLE >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_FIXED64); + value = Double.longBitsToDouble(readFixed64()); + break; + default: + throw new IllegalArgumentException( + "Requested field id (" + getFieldIdString(fieldId) + + ") cannot be read as a double" + + dumpDebugData()); + } + // Successfully read the field + mState &= ~STATE_STARTED_FIELD_READ; + return value; + } + + /** + * Read a single float. + * Will throw if the current wire type is not fixed32 + * + * @param fieldId - must match the current field number and field type + */ + public float readFloat(long fieldId) throws IOException { + assertFreshData(); + assertFieldNumber(fieldId); + checkPacked(fieldId); + + float value; + switch ((int) ((fieldId & FIELD_TYPE_MASK) + >>> FIELD_TYPE_SHIFT)) { + case (int) (FIELD_TYPE_FLOAT >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_FIXED32); + value = Float.intBitsToFloat(readFixed32()); + break; + default: + throw new IllegalArgumentException( + "Requested field id (" + getFieldIdString(fieldId) + ") is not a float" + + dumpDebugData()); + } + // Successfully read the field + mState &= ~STATE_STARTED_FIELD_READ; + return value; + } + + /** + * Read a single 32bit or varint proto type field as an int. + * Will throw if the current wire type is not varint or fixed32 + * + * @param fieldId - must match the current field number and field type + */ + public int readInt(long fieldId) throws IOException { + assertFreshData(); + assertFieldNumber(fieldId); + checkPacked(fieldId); + + int value; + switch ((int) ((fieldId & FIELD_TYPE_MASK) + >>> FIELD_TYPE_SHIFT)) { + case (int) (FIELD_TYPE_FIXED32 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_SFIXED32 >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_FIXED32); + value = readFixed32(); + break; + case (int) (FIELD_TYPE_SINT32 >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_VARINT); + value = decodeZigZag32((int) readVarint()); + break; + case (int) (FIELD_TYPE_INT32 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_UINT32 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_ENUM >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_VARINT); + value = (int) readVarint(); + break; + default: + throw new IllegalArgumentException( + "Requested field id (" + getFieldIdString(fieldId) + ") is not an int" + + dumpDebugData()); + } + // Successfully read the field + mState &= ~STATE_STARTED_FIELD_READ; + return value; + } + + /** + * Read a single 64bit or varint proto type field as an long. + * + * @param fieldId - must match the current field number + */ + public long readLong(long fieldId) throws IOException { + assertFreshData(); + assertFieldNumber(fieldId); + checkPacked(fieldId); + + long value; + switch ((int) ((fieldId & FIELD_TYPE_MASK) + >>> FIELD_TYPE_SHIFT)) { + case (int) (FIELD_TYPE_FIXED64 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_SFIXED64 >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_FIXED64); + value = readFixed64(); + break; + case (int) (FIELD_TYPE_SINT64 >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_VARINT); + value = decodeZigZag64(readVarint()); + break; + case (int) (FIELD_TYPE_INT64 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_UINT64 >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_VARINT); + value = readVarint(); + break; + default: + throw new IllegalArgumentException( + "Requested field id (" + getFieldIdString(fieldId) + ") is not an long" + + dumpDebugData()); + } + // Successfully read the field + mState &= ~STATE_STARTED_FIELD_READ; + return value; + } + + /** + * Read a single 32bit or varint proto type field as an boolean. + * + * @param fieldId - must match the current field number + */ + public boolean readBoolean(long fieldId) throws IOException { + assertFreshData(); + assertFieldNumber(fieldId); + checkPacked(fieldId); + + boolean value; + switch ((int) ((fieldId & FIELD_TYPE_MASK) + >>> FIELD_TYPE_SHIFT)) { + case (int) (FIELD_TYPE_BOOL >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_VARINT); + value = readVarint() != 0; + break; + default: + throw new IllegalArgumentException( + "Requested field id (" + getFieldIdString(fieldId) + ") is not an boolean" + + dumpDebugData()); + } + // Successfully read the field + mState &= ~STATE_STARTED_FIELD_READ; + return value; + } + + /** + * Read a string field + * + * @param fieldId - must match the current field number + */ + public String readString(long fieldId) throws IOException { + assertFreshData(); + assertFieldNumber(fieldId); + + String value; + switch ((int) ((fieldId & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) { + case (int) (FIELD_TYPE_STRING >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_LENGTH_DELIMITED); + int len = (int) readVarint(); + value = readRawString(len); + break; + default: + throw new IllegalArgumentException( + "Requested field id(" + getFieldIdString(fieldId) + + ") is not an string" + + dumpDebugData()); + } + // Successfully read the field + mState &= ~STATE_STARTED_FIELD_READ; + return value; + } + + /** + * Read a bytes field + * + * @param fieldId - must match the current field number + */ + public byte[] readBytes(long fieldId) throws IOException { + assertFreshData(); + assertFieldNumber(fieldId); + + byte[] value; + switch ((int) ((fieldId & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) { + case (int) (FIELD_TYPE_MESSAGE >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_BYTES >>> FIELD_TYPE_SHIFT): + assertWireType(WIRE_TYPE_LENGTH_DELIMITED); + int len = (int) readVarint(); + value = readRawBytes(len); + break; + default: + throw new IllegalArgumentException( + "Requested field type (" + getFieldIdString(fieldId) + + ") cannot be read as raw bytes" + + dumpDebugData()); + } + // Successfully read the field + mState &= ~STATE_STARTED_FIELD_READ; + return value; + } + + /** + * Start the read of an embedded Object + * + * @param fieldId - must match the current field number + * @return a token. The token must be handed back when finished reading embedded Object + */ + public long start(long fieldId) throws IOException { + assertFreshData(); + assertFieldNumber(fieldId); + assertWireType(WIRE_TYPE_LENGTH_DELIMITED); + + int messageSize = (int) readVarint(); + + if (mExpectedObjectTokenStack == null) { + mExpectedObjectTokenStack = new ArrayList<>(); + } + if (++mDepth == mExpectedObjectTokenStack.size()) { + // Create a token to keep track of nested Object and extend the object stack + mExpectedObjectTokenStack.add(makeToken(0, + (fieldId & FIELD_COUNT_REPEATED) == FIELD_COUNT_REPEATED, mDepth, + (int) fieldId, getOffset() + messageSize)); + + } else { + // Create a token to keep track of nested Object + mExpectedObjectTokenStack.set(mDepth, makeToken(0, + (fieldId & FIELD_COUNT_REPEATED) == FIELD_COUNT_REPEATED, mDepth, + (int) fieldId, getOffset() + messageSize)); + } + + // Sanity check + if (mDepth > 0 + && getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) + > getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth - 1))) { + throw new ProtoParseException("Embedded Object (" + + token2String(mExpectedObjectTokenStack.get(mDepth)) + + ") ends after of parent Objects's (" + + token2String(mExpectedObjectTokenStack.get(mDepth - 1)) + + ") end" + + dumpDebugData()); + } + mState &= ~STATE_STARTED_FIELD_READ; + return mExpectedObjectTokenStack.get(mDepth); + } + + /** + * Note the end of a nested object. Must be called to continue streaming the rest of the proto. + * end can be called mid object parse. The offset will be moved to the next field outside the + * object. + * + * @param token - token + */ + public void end(long token) { + // Sanity check to make sure user is keeping track of their embedded messages + if (mExpectedObjectTokenStack.get(mDepth) != token) { + throw new ProtoParseException( + "end token " + token + " does not match current message token " + + mExpectedObjectTokenStack.get(mDepth) + + dumpDebugData()); + } + if (getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) > getOffset()) { + // Did not read all of the message, skip to the end + incOffset(getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) - getOffset()); + } + mDepth--; + mState &= ~STATE_STARTED_FIELD_READ; + } + + /** + * Read the tag at the start of the next field and collect field number and wire type. + * Will set mFieldNumber to NO_MORE_FIELDS if end of buffer/stream reached. + */ + private void readTag() throws IOException { + fillBuffer(); + if (mOffset >= mEnd) { + // reached end of the stream + mFieldNumber = NO_MORE_FIELDS; + return; + } + int tag = (int) readVarint(); + mFieldNumber = tag >>> FIELD_ID_SHIFT; + mWireType = tag & WIRE_TYPE_MASK; + mState |= STATE_STARTED_FIELD_READ; + } + + /** + * Decode a 32 bit ZigZag encoded signed int. + * + * @param n - int to decode + * @return the decoded signed int + */ + public int decodeZigZag32(final int n) { + return (n >>> 1) ^ -(n & 1); + } + + /** + * Decode a 64 bit ZigZag encoded signed long. + * + * @param n - long to decode + * @return the decoded signed long + */ + public long decodeZigZag64(final long n) { + return (n >>> 1) ^ -(n & 1); + } + + /** + * Read a varint from the buffer + * + * @return the varint as a long + */ + private long readVarint() throws IOException { + long value = 0; + int shift = 0; + while (true) { + fillBuffer(); + // Limit how much bookkeeping is done by checking how far away the end of the buffer is + // and directly accessing buffer up until the end. + final int fragment = mEnd - mOffset; + for (int i = 0; i < fragment; i++) { + byte b = mBuffer[(mOffset + i)]; + value |= (b & 0x7FL) << shift; + if ((b & 0x80) == 0) { + incOffset(i + 1); + return value; + } + shift += 7; + if (shift > 63) { + throw new ProtoParseException( + "Varint is too large at offset 0x" + + Integer.toHexString(getOffset() + i) + + dumpDebugData()); + } + } + // Hit the end of the buffer, do some incrementing and checking, then continue + incOffset(fragment); + } + } + + /** + * Read a fixed 32 bit int from the buffer + * + * @return the fixed32 as a int + */ + private int readFixed32() throws IOException { + // check for fast path, which is likely with a reasonable buffer size + if (mOffset + 4 <= mEnd) { + // don't bother filling buffer since we know the end is plenty far away + incOffset(4); + return (mBuffer[mOffset - 4] & 0xFF) + | ((mBuffer[mOffset - 3] & 0xFF) << 8) + | ((mBuffer[mOffset - 2] & 0xFF) << 16) + | ((mBuffer[mOffset - 1] & 0xFF) << 24); + } + + // the Fixed32 crosses the edge of a chunk, read the Fixed32 in multiple fragments. + // There will be two fragment reads except when the chunk size is 2 or less. + int value = 0; + int shift = 0; + int bytesLeft = 4; + while (bytesLeft > 0) { + fillBuffer(); + // Find the number of bytes available until the end of the chunk or Fixed32 + int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft; + incOffset(fragment); + bytesLeft -= fragment; + while (fragment > 0) { + value |= ((mBuffer[mOffset - fragment] & 0xFF) << shift); + fragment--; + shift += 8; + } + } + return value; + } + + /** + * Read a fixed 64 bit long from the buffer + * + * @return the fixed64 as a long + */ + private long readFixed64() throws IOException { + // check for fast path, which is likely with a reasonable buffer size + if (mOffset + 8 <= mEnd) { + // don't bother filling buffer since we know the end is plenty far away + incOffset(8); + return (mBuffer[mOffset - 8] & 0xFFL) + | ((mBuffer[mOffset - 7] & 0xFFL) << 8) + | ((mBuffer[mOffset - 6] & 0xFFL) << 16) + | ((mBuffer[mOffset - 5] & 0xFFL) << 24) + | ((mBuffer[mOffset - 4] & 0xFFL) << 32) + | ((mBuffer[mOffset - 3] & 0xFFL) << 40) + | ((mBuffer[mOffset - 2] & 0xFFL) << 48) + | ((mBuffer[mOffset - 1] & 0xFFL) << 56); + } + + // the Fixed64 crosses the edge of a chunk, read the Fixed64 in multiple fragments. + // There will be two fragment reads except when the chunk size is 6 or less. + long value = 0; + int shift = 0; + int bytesLeft = 8; + while (bytesLeft > 0) { + fillBuffer(); + // Find the number of bytes available until the end of the chunk or Fixed64 + int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft; + incOffset(fragment); + bytesLeft -= fragment; + while (fragment > 0) { + value |= ((mBuffer[(mOffset - fragment)] & 0xFFL) << shift); + fragment--; + shift += 8; + } + } + return value; + } + + /** + * Read raw bytes from the buffer + * + * @param n - number of bytes to read + * @return a byte array with raw bytes + */ + private byte[] readRawBytes(int n) throws IOException { + byte[] buffer = new byte[n]; + int pos = 0; + while (mOffset + n - pos > mEnd) { + int fragment = mEnd - mOffset; + if (fragment > 0) { + System.arraycopy(mBuffer, mOffset, buffer, pos, fragment); + incOffset(fragment); + pos += fragment; + } + fillBuffer(); + if (mOffset >= mEnd) { + throw new ProtoParseException( + "Unexpectedly reached end of the InputStream at offset 0x" + + Integer.toHexString(mEnd) + + dumpDebugData()); + } + } + System.arraycopy(mBuffer, mOffset, buffer, pos, n - pos); + incOffset(n - pos); + return buffer; + } + + /** + * Read raw string from the buffer + * + * @param n - number of bytes to read + * @return a string + */ + private String readRawString(int n) throws IOException { + fillBuffer(); + if (mOffset + n <= mEnd) { + // fast path read. String is well within the current buffer + String value = StringFactory.newStringFromBytes(mBuffer, mOffset, n, + StandardCharsets.UTF_8); + incOffset(n); + return value; + } else if (n <= mBufferSize) { + // String extends past buffer, but can be encapsulated in a buffer. Copy the first chunk + // of the string to the start of the buffer and then fill the rest of the buffer from + // the stream. + final int stringHead = mEnd - mOffset; + System.arraycopy(mBuffer, mOffset, mBuffer, 0, stringHead); + mEnd = stringHead + mStream.read(mBuffer, stringHead, n - stringHead); + + mDiscardedBytes += mOffset; + mOffset = 0; + + String value = StringFactory.newStringFromBytes(mBuffer, mOffset, n, + StandardCharsets.UTF_8); + incOffset(n); + return value; + } + // Otherwise, the string is too large to use the buffer. Create the string from a + // separate byte array. + return StringFactory.newStringFromBytes(readRawBytes(n), 0, n, StandardCharsets.UTF_8); + } + + /** + * Fill the buffer with a chunk from the stream if need be. + * Will skip chunks until mOffset is reached + */ + private void fillBuffer() throws IOException { + if (mOffset >= mEnd && mStream != null) { + mOffset -= mEnd; + mDiscardedBytes += mEnd; + if (mOffset >= mBufferSize) { + int skipped = (int) mStream.skip((mOffset / mBufferSize) * mBufferSize); + mDiscardedBytes += skipped; + mOffset -= skipped; + } + mEnd = mStream.read(mBuffer); + } + } + + /** + * Skips the rest of current field and moves to the start of the next field. This should only be + * called while state is STATE_STARTED_FIELD_READ + */ + public void skip() throws IOException { + if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) { + incOffset(mPackedEnd - getOffset()); + } else { + switch (mWireType) { + case WIRE_TYPE_VARINT: + byte b; + do { + fillBuffer(); + b = mBuffer[mOffset]; + incOffset(1); + } while ((b & 0x80) != 0); + break; + case WIRE_TYPE_FIXED64: + incOffset(8); + break; + case WIRE_TYPE_LENGTH_DELIMITED: + fillBuffer(); + int length = (int) readVarint(); + incOffset(length); + break; + /* + case WIRE_TYPE_START_GROUP: + // Not implemented + break; + case WIRE_TYPE_END_GROUP: + // Not implemented + break; + */ + case WIRE_TYPE_FIXED32: + incOffset(4); + break; + default: + throw new ProtoParseException( + "Unexpected wire type: " + mWireType + " at offset 0x" + + Integer.toHexString(mOffset) + + dumpDebugData()); + } + } + mState &= ~STATE_STARTED_FIELD_READ; + } + + /** + * Increment the offset and handle all the relevant bookkeeping + * Refilling the buffer when its end is reached will be handled elsewhere (ideally just before + * a read, to avoid unnecessary reads from stream) + * + * @param n - number of bytes to increment + */ + private void incOffset(int n) { + mOffset += n; + + if (mDepth >= 0 && getOffset() > getOffsetFromToken( + mExpectedObjectTokenStack.get(mDepth))) { + throw new ProtoParseException("Unexpectedly reached end of embedded object. " + + token2String(mExpectedObjectTokenStack.get(mDepth)) + + dumpDebugData()); + } + } + + /** + * Check the current wire type to determine if current numeric field is packed. If it is packed, + * set up to deal with the field + * This should only be called for primitive numeric field types. + * + * @param fieldId - used to determine what the packed wire type is. + */ + private void checkPacked(long fieldId) throws IOException { + if (mWireType == WIRE_TYPE_LENGTH_DELIMITED) { + // Primitive Field is length delimited, must be a packed field. + final int length = (int) readVarint(); + mPackedEnd = getOffset() + length; + mState |= STATE_READING_PACKED; + + // Fake the wire type, based on the field type + switch ((int) ((fieldId & FIELD_TYPE_MASK) + >>> FIELD_TYPE_SHIFT)) { + case (int) (FIELD_TYPE_FLOAT >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_FIXED32 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_SFIXED32 >>> FIELD_TYPE_SHIFT): + if (length % 4 != 0) { + throw new IllegalArgumentException( + "Requested field id (" + getFieldIdString(fieldId) + + ") packed length " + length + + " is not aligned for fixed32" + + dumpDebugData()); + } + mWireType = WIRE_TYPE_FIXED32; + break; + case (int) (FIELD_TYPE_DOUBLE >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_FIXED64 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_SFIXED64 >>> FIELD_TYPE_SHIFT): + if (length % 8 != 0) { + throw new IllegalArgumentException( + "Requested field id (" + getFieldIdString(fieldId) + + ") packed length " + length + + " is not aligned for fixed64" + + dumpDebugData()); + } + mWireType = WIRE_TYPE_FIXED64; + break; + case (int) (FIELD_TYPE_SINT32 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_INT32 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_UINT32 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_SINT64 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_INT64 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_UINT64 >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_ENUM >>> FIELD_TYPE_SHIFT): + case (int) (FIELD_TYPE_BOOL >>> FIELD_TYPE_SHIFT): + mWireType = WIRE_TYPE_VARINT; + break; + default: + throw new IllegalArgumentException( + "Requested field id (" + getFieldIdString(fieldId) + + ") is not a packable field" + + dumpDebugData()); + } + } + } + + + /** + * Check a field id constant against current field number + * + * @param fieldId - throws if fieldId does not match mFieldNumber + */ + private void assertFieldNumber(long fieldId) { + if ((int) fieldId != mFieldNumber) { + throw new IllegalArgumentException("Requested field id (" + getFieldIdString(fieldId) + + ") does not match current field number (0x" + Integer.toHexString( + mFieldNumber) + + ") at offset 0x" + Integer.toHexString(getOffset()) + + dumpDebugData()); + } + } + + + /** + * Check a wire type against current wire type. + * + * @param wireType - throws if wireType does not match mWireType. + */ + private void assertWireType(int wireType) { + if (wireType != mWireType) { + throw new WireTypeMismatchException( + "Current wire type " + getWireTypeString(mWireType) + + " does not match expected wire type " + getWireTypeString(wireType) + + " at offset 0x" + Integer.toHexString(getOffset()) + + dumpDebugData()); + } + } + + /** + * Check if there is data ready to be read. + */ + private void assertFreshData() { + if ((mState & STATE_STARTED_FIELD_READ) != STATE_STARTED_FIELD_READ) { + throw new ProtoParseException( + "Attempting to read already read field at offset 0x" + Integer.toHexString( + getOffset()) + dumpDebugData()); + } + } + + /** + * Dump debugging data about the buffer. + */ + public String dumpDebugData() { + StringBuilder sb = new StringBuilder(); + + sb.append("\nmFieldNumber : 0x" + Integer.toHexString(mFieldNumber)); + sb.append("\nmWireType : 0x" + Integer.toHexString(mWireType)); + sb.append("\nmState : 0x" + Integer.toHexString(mState)); + sb.append("\nmDiscardedBytes : 0x" + Integer.toHexString(mDiscardedBytes)); + sb.append("\nmOffset : 0x" + Integer.toHexString(mOffset)); + sb.append("\nmExpectedObjectTokenStack : "); + if (mExpectedObjectTokenStack == null) { + sb.append("null"); + } else { + sb.append(mExpectedObjectTokenStack); + } + sb.append("\nmDepth : 0x" + Integer.toHexString(mDepth)); + sb.append("\nmBuffer : "); + if (mBuffer == null) { + sb.append("null"); + } else { + sb.append(mBuffer); + } + sb.append("\nmBufferSize : 0x" + Integer.toHexString(mBufferSize)); + sb.append("\nmEnd : 0x" + Integer.toHexString(mEnd)); + + return sb.toString(); + } +} diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java index a94806a02fbb..a1ee61c15f24 100644 --- a/core/java/android/util/proto/ProtoOutputStream.java +++ b/core/java/android/util/proto/ProtoOutputStream.java @@ -100,88 +100,12 @@ import java.io.UnsupportedEncodingException; * errors if they are not matched. */ @TestApi -public final class ProtoOutputStream { +public final class ProtoOutputStream extends ProtoStream { + /** + * @hide + */ public static final String TAG = "ProtoOutputStream"; - public static final int FIELD_ID_SHIFT = 3; - public static final int WIRE_TYPE_MASK = (1<<FIELD_ID_SHIFT)-1; - public static final int FIELD_ID_MASK = ~WIRE_TYPE_MASK; - - public static final int WIRE_TYPE_VARINT = 0; - public static final int WIRE_TYPE_FIXED64 = 1; - public static final int WIRE_TYPE_LENGTH_DELIMITED = 2; - public static final int WIRE_TYPE_START_GROUP = 3; - public static final int WIRE_TYPE_END_GROUP = 4; - public static final int WIRE_TYPE_FIXED32 = 5; - - /** - * Position of the field type in a (long) fieldId. - */ - public static final int FIELD_TYPE_SHIFT = 32; - - /** - * Mask for the field types stored in a fieldId. Leaves a whole - * byte for future expansion, even though there are currently only 17 types. - */ - public static final long FIELD_TYPE_MASK = 0x0ffL << FIELD_TYPE_SHIFT; - - public static final long FIELD_TYPE_UNKNOWN = 0; - - /** - * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly, - * so no extra mapping needs to be maintained in this case. - */ - public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT; -// public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated. - public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT; - - private static final String[] FIELD_TYPE_NAMES = new String[] { - "Double", - "Float", - "Int64", - "UInt64", - "Int32", - "Fixed64", - "Fixed32", - "Bool", - "String", - "Group", // This field is deprecated but reserved here for indexing. - "Message", - "Bytes", - "UInt32", - "Enum", - "SFixed32", - "SFixed64", - "SInt32", - "SInt64", - }; - - // - // FieldId flags for whether the field is single, repeated or packed. - // - public static final int FIELD_COUNT_SHIFT = 40; - public static final long FIELD_COUNT_MASK = 0x0fL << FIELD_COUNT_SHIFT; - - public static final long FIELD_COUNT_UNKNOWN = 0; - public static final long FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT; - public static final long FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT; - public static final long FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT; - /** * Our buffer. */ @@ -1997,94 +1921,6 @@ public final class ProtoOutputStream { } } - // - // Child objects - // - - /** - * Make a token. - * Bits 61-63 - tag size (So we can go backwards later if the object had not data) - * - 3 bits, max value 7, max value needed 5 - * Bit 60 - true if the object is repeated (lets us require endObject or endRepeatedObject) - * Bits 59-51 - depth (For error checking) - * - 9 bits, max value 512, when checking, value is masked (if we really - * are more than 512 levels deep) - * Bits 32-50 - objectId (For error checking) - * - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap - * because of the overflow, and only the tokens are compared. - * Bits 0-31 - offset of the first size field in the buffer. - */ - // VisibleForTesting - public static long makeToken(int tagSize, boolean repeated, int depth, int objectId, - int sizePos) { - return ((0x07L & (long)tagSize) << 61) - | (repeated ? (1L << 60) : 0) - | (0x01ffL & (long)depth) << 51 - | (0x07ffffL & (long)objectId) << 32 - | (0x0ffffffffL & (long)sizePos); - } - - /** - * Get the encoded tag size from the token. - */ - public static int getTagSizeFromToken(long token) { - return (int)(0x7 & (token >> 61)); - } - - /** - * Get whether this is a call to startObject (false) or startRepeatedObject (true). - */ - public static boolean getRepeatedFromToken(long token) { - return (0x1 & (token >> 60)) != 0; - } - - /** - * Get the nesting depth of startObject calls from the token. - */ - public static int getDepthFromToken(long token) { - return (int)(0x01ff & (token >> 51)); - } - - /** - * Get the object ID from the token. The object ID is a serial number for the - * startObject calls that have happened on this object. The values are truncated - * to 9 bits, but that is sufficient for error checking. - */ - public static int getObjectIdFromToken(long token) { - return (int)(0x07ffff & (token >> 32)); - } - - /** - * Get the location of the childRawSize (the first 32 bit size field) in this object. - */ - public static int getSizePosFromToken(long token) { - return (int)token; - } - - /** - * Convert the object ID to the ordinal value -- the n-th call to startObject. - * The object IDs start at -1 and count backwards, so that the value is unlikely - * to alias with an actual size field that had been written. - */ - public static int convertObjectIdToOrdinal(int objectId) { - return (-1 & 0x07ffff) - objectId; - } - - /** - * Return a debugging string of a token. - */ - public static String token2String(long token) { - if (token == 0L) { - return "Token(0)"; - } else { - return "Token(val=0x" + Long.toHexString(token) - + " depth=" + getDepthFromToken(token) - + " object=" + convertObjectIdToOrdinal(getObjectIdFromToken(token)) - + " tagSize=" + getTagSizeFromToken(token) - + " sizePos=" + getSizePosFromToken(token) - + ')'; - } - } /** * Start a child object. @@ -2175,7 +2011,7 @@ public final class ProtoOutputStream { // at which to write the size. final int depth = getDepthFromToken(token); final boolean expectedRepeated = getRepeatedFromToken(token); - final int sizePos = getSizePosFromToken(token); + final int sizePos = getOffsetFromToken(token); final int childRawSize = mBuffer.getWritePos() - sizePos - 8; if (repeated != expectedRepeated) { @@ -2346,56 +2182,6 @@ public final class ProtoOutputStream { } /** - * Get the developer-usable name of a field type. - */ - private static String getFieldTypeString(long fieldType) { - int index = ((int)((fieldType & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) - 1; - if (index >= 0 && index < FIELD_TYPE_NAMES.length) { - return FIELD_TYPE_NAMES[index]; - } else { - return null; - } - } - - /** - * Get the developer-usable name of a field count. - */ - private static String getFieldCountString(long fieldCount) { - if (fieldCount == FIELD_COUNT_SINGLE) { - return ""; - } else if (fieldCount == FIELD_COUNT_REPEATED) { - return "Repeated"; - } else if (fieldCount == FIELD_COUNT_PACKED) { - return "Packed"; - } else { - return null; - } - } - - /** - * Get a debug string for a fieldId. - */ - private String getFieldIdString(long fieldId) { - final long fieldCount = fieldId & FIELD_COUNT_MASK; - String countString = getFieldCountString(fieldCount); - if (countString == null) { - countString = "fieldCount=" + fieldCount; - } - if (countString.length() > 0) { - countString += " "; - } - - final long fieldType = fieldId & FIELD_TYPE_MASK; - String typeString = getFieldTypeString(fieldType); - if (typeString == null) { - typeString = "fieldType=" + fieldType; - } - - return countString + typeString + " tag=" + ((int) fieldId) - + " fieldId=0x" + Long.toHexString(fieldId); - } - - /** * Return how many bytes an encoded field tag will require. */ private static int getTagSize(int id) { diff --git a/core/java/android/util/proto/ProtoStream.java b/core/java/android/util/proto/ProtoStream.java new file mode 100644 index 000000000000..9e2e95a5923d --- /dev/null +++ b/core/java/android/util/proto/ProtoStream.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util.proto; + +import android.annotation.TestApi; + +/** + * Abstract base class for both protobuf streams. + * + * Contains a set of useful constants and methods used by both + * ProtoOutputStream and ProtoInputStream + * + * @hide + */ +@TestApi +public abstract class ProtoStream { + + public static final int FIELD_ID_SHIFT = 3; + public static final int WIRE_TYPE_MASK = (1 << FIELD_ID_SHIFT) - 1; + public static final int FIELD_ID_MASK = ~WIRE_TYPE_MASK; + + public static final int WIRE_TYPE_VARINT = 0; + public static final int WIRE_TYPE_FIXED64 = 1; + public static final int WIRE_TYPE_LENGTH_DELIMITED = 2; + public static final int WIRE_TYPE_START_GROUP = 3; + public static final int WIRE_TYPE_END_GROUP = 4; + public static final int WIRE_TYPE_FIXED32 = 5; + + /** + * Position of the field type in a (long) fieldId. + */ + public static final int FIELD_TYPE_SHIFT = 32; + + /** + * Mask for the field types stored in a fieldId. Leaves a whole + * byte for future expansion, even though there are currently only 17 types. + */ + public static final long FIELD_TYPE_MASK = 0x0ffL << FIELD_TYPE_SHIFT; + + public static final long FIELD_TYPE_UNKNOWN = 0; + + /** + * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly, + * so no extra mapping needs to be maintained in this case. + */ + public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT; + // public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated. + public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT; + + protected static final String[] FIELD_TYPE_NAMES = new String[]{ + "Double", + "Float", + "Int64", + "UInt64", + "Int32", + "Fixed64", + "Fixed32", + "Bool", + "String", + "Group", // This field is deprecated but reserved here for indexing. + "Message", + "Bytes", + "UInt32", + "Enum", + "SFixed32", + "SFixed64", + "SInt32", + "SInt64", + }; + + // + // FieldId flags for whether the field is single, repeated or packed. + // + public static final int FIELD_COUNT_SHIFT = 40; + public static final long FIELD_COUNT_MASK = 0x0fL << FIELD_COUNT_SHIFT; + + public static final long FIELD_COUNT_UNKNOWN = 0; + public static final long FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT; + public static final long FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT; + public static final long FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT; + + + /** + * Get the developer-usable name of a field type. + */ + public static String getFieldTypeString(long fieldType) { + int index = ((int) ((fieldType & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) - 1; + if (index >= 0 && index < FIELD_TYPE_NAMES.length) { + return FIELD_TYPE_NAMES[index]; + } else { + return null; + } + } + + /** + * Get the developer-usable name of a field count. + */ + public static String getFieldCountString(long fieldCount) { + if (fieldCount == FIELD_COUNT_SINGLE) { + return ""; + } else if (fieldCount == FIELD_COUNT_REPEATED) { + return "Repeated"; + } else if (fieldCount == FIELD_COUNT_PACKED) { + return "Packed"; + } else { + return null; + } + } + + /** + * Get the developer-usable name of a wire type. + */ + public static String getWireTypeString(int wireType) { + switch (wireType) { + case WIRE_TYPE_VARINT: + return "Varint"; + case WIRE_TYPE_FIXED64: + return "Fixed64"; + case WIRE_TYPE_LENGTH_DELIMITED: + return "Length Delimited"; + case WIRE_TYPE_START_GROUP: + return "Start Group"; + case WIRE_TYPE_END_GROUP: + return "End Group"; + case WIRE_TYPE_FIXED32: + return "Fixed32"; + default: + return null; + } + } + + /** + * Get a debug string for a fieldId. + */ + public static String getFieldIdString(long fieldId) { + final long fieldCount = fieldId & FIELD_COUNT_MASK; + String countString = getFieldCountString(fieldCount); + if (countString == null) { + countString = "fieldCount=" + fieldCount; + } + if (countString.length() > 0) { + countString += " "; + } + + final long fieldType = fieldId & FIELD_TYPE_MASK; + String typeString = getFieldTypeString(fieldType); + if (typeString == null) { + typeString = "fieldType=" + fieldType; + } + + return countString + typeString + " tag=" + ((int) fieldId) + + " fieldId=0x" + Long.toHexString(fieldId); + } + + /** + * Combine a fieldId (the field keys in the proto file) and the field flags. + * Mostly useful for testing because the generated code contains the fieldId + * constants. + */ + public static long makeFieldId(int id, long fieldFlags) { + return fieldFlags | (((long) id) & 0x0ffffffffL); + } + + // + // Child objects + // + + /** + * Make a token. + * Bits 61-63 - tag size (So we can go backwards later if the object had not data) + * - 3 bits, max value 7, max value needed 5 + * Bit 60 - true if the object is repeated (lets us require endObject or endRepeatedObject) + * Bits 59-51 - depth (For error checking) + * - 9 bits, max value 512, when checking, value is masked (if we really + * are more than 512 levels deep) + * Bits 32-50 - objectId (For error checking) + * - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap + * because of the overflow, and only the tokens are compared. + * Bits 0-31 - offset of interest for the object. + */ + public static long makeToken(int tagSize, boolean repeated, int depth, int objectId, + int offset) { + return ((0x07L & (long) tagSize) << 61) + | (repeated ? (1L << 60) : 0) + | (0x01ffL & (long) depth) << 51 + | (0x07ffffL & (long) objectId) << 32 + | (0x0ffffffffL & (long) offset); + } + + /** + * Get the encoded tag size from the token. + */ + public static int getTagSizeFromToken(long token) { + return (int) (0x7 & (token >> 61)); + } + + /** + * Get whether this is a call to startObject (false) or startRepeatedObject (true). + */ + public static boolean getRepeatedFromToken(long token) { + return (0x1 & (token >> 60)) != 0; + } + + /** + * Get the nesting depth of startObject calls from the token. + */ + public static int getDepthFromToken(long token) { + return (int) (0x01ff & (token >> 51)); + } + + /** + * Get the object ID from the token. The object ID is a serial number for the + * startObject calls that have happened on this object. The values are truncated + * to 9 bits, but that is sufficient for error checking. + */ + public static int getObjectIdFromToken(long token) { + return (int) (0x07ffff & (token >> 32)); + } + + /** + * Get the location of the offset recorded in the token. + */ + public static int getOffsetFromToken(long token) { + return (int) token; + } + + /** + * Convert the object ID to the ordinal value -- the n-th call to startObject. + * The object IDs start at -1 and count backwards, so that the value is unlikely + * to alias with an actual size field that had been written. + */ + public static int convertObjectIdToOrdinal(int objectId) { + return (-1 & 0x07ffff) - objectId; + } + + /** + * Return a debugging string of a token. + */ + public static String token2String(long token) { + if (token == 0L) { + return "Token(0)"; + } else { + return "Token(val=0x" + Long.toHexString(token) + + " depth=" + getDepthFromToken(token) + + " object=" + convertObjectIdToOrdinal(getObjectIdFromToken(token)) + + " tagSize=" + getTagSizeFromToken(token) + + " offset=" + getOffsetFromToken(token) + + ')'; + } + } +} diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java index c7bbb9f2a044..03bf590902c0 100644 --- a/core/java/android/util/proto/ProtoUtils.java +++ b/core/java/android/util/proto/ProtoUtils.java @@ -19,6 +19,8 @@ package android.util.proto; import android.util.AggStats; import android.util.Duration; +import java.io.IOException; + /** * This class contains a list of helper functions to write common proto in * //frameworks/base/core/proto/android/base directory @@ -70,4 +72,54 @@ public class ProtoUtils { } } } + + /** + * Provide debug data about the current field as a string + */ + public static String currentFieldToString(ProtoInputStream proto) throws IOException { + StringBuilder sb = new StringBuilder(); + + final int fieldNumber = proto.getFieldNumber(); + final int wireType = proto.getWireType(); + long fieldConstant; + + sb.append("Offset : 0x" + Integer.toHexString(proto.getOffset())); + sb.append("\nField Number : 0x" + Integer.toHexString(proto.getFieldNumber())); + sb.append("\nWire Type : "); + switch (wireType) { + case ProtoStream.WIRE_TYPE_VARINT: + sb.append("varint"); + fieldConstant = ProtoStream.makeFieldId(fieldNumber, + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64); + sb.append("\nField Value : 0x" + Long.toHexString(proto.readLong(fieldConstant))); + break; + case ProtoStream.WIRE_TYPE_FIXED64: + sb.append("fixed64"); + fieldConstant = ProtoStream.makeFieldId(fieldNumber, + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64); + sb.append("\nField Value : 0x" + Long.toHexString(proto.readLong(fieldConstant))); + break; + case ProtoStream.WIRE_TYPE_LENGTH_DELIMITED: + sb.append("length delimited"); + fieldConstant = ProtoStream.makeFieldId(fieldNumber, + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES); + sb.append("\nField Bytes : " + proto.readBytes(fieldConstant)); + break; + case ProtoStream.WIRE_TYPE_START_GROUP: + sb.append("start group"); + break; + case ProtoStream.WIRE_TYPE_END_GROUP: + sb.append("end group"); + break; + case ProtoStream.WIRE_TYPE_FIXED32: + sb.append("fixed32"); + fieldConstant = ProtoStream.makeFieldId(fieldNumber, + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32); + sb.append("\nField Value : 0x" + Integer.toHexString(proto.readInt(fieldConstant))); + break; + default: + sb.append("unknown(" + proto.getWireType() + ")"); + } + return sb.toString(); + } } diff --git a/core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java b/core/java/android/util/proto/WireTypeMismatchException.java index 0efa4b4ac694..d90b4f8df8ae 100644 --- a/core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java +++ b/core/java/android/util/proto/WireTypeMismatchException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * 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. @@ -14,11 +14,25 @@ * limitations under the License. */ +package android.util.proto; -package com.android.webview.chromium; +import android.annotation.TestApi; /** - * An empty class for testing purposes. + * Thrown when there is an error parsing protobuf data. + * + * @hide */ -public class WebViewLoadingTestClass { +@TestApi +public class WireTypeMismatchException extends ProtoParseException { + + /** + * Construct a WireTypeMismatchException. + * + * @param msg The message. + */ + public WireTypeMismatchException(String msg) { + super(msg); + } } + diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 45fa5614abb7..eb41e078e59f 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -32,6 +32,7 @@ import android.os.Message; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; +import android.os.SystemClock; import android.text.style.AccessibilityClickableSpan; import android.text.style.ClickableSpan; import android.util.LongSparseArray; @@ -702,6 +703,14 @@ final class AccessibilityInteractionController { // Handle this hidden action separately succeeded = handleClickableSpanActionUiThread( target, virtualDescendantId, arguments); + } else if (action == R.id.accessibilityActionOutsideTouch) { + // trigger ACTION_OUTSIDE to notify windows + final long now = SystemClock.uptimeMillis(); + MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE, + 0, 0, 0); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + mViewRootImpl.dispatchInputEvent(event); + succeeded = true; } else { AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); if (provider != null) { diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index c520a9958687..9aab41985695 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.LayoutRes; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; @@ -154,7 +155,9 @@ public abstract class LayoutInflater { * @return View Newly created view. Return null for the default * behavior. */ - public View onCreateView(String name, Context context, AttributeSet attrs); + @Nullable + View onCreateView(@NonNull String name, @NonNull Context context, + @NonNull AttributeSet attrs); } public interface Factory2 extends Factory { @@ -172,7 +175,9 @@ public abstract class LayoutInflater { * @return View Newly created view. Return null for the default * behavior. */ - public View onCreateView(View parent, String name, Context context, AttributeSet attrs); + @Nullable + View onCreateView(@Nullable View parent, @NonNull String name, + @NonNull Context context, @NonNull AttributeSet attrs); } private static class FactoryMerger implements Factory2 { @@ -186,13 +191,17 @@ public abstract class LayoutInflater { mF22 = f22; } - public View onCreateView(String name, Context context, AttributeSet attrs) { + @Nullable + public View onCreateView(@NonNull String name, @NonNull Context context, + @NonNull AttributeSet attrs) { View v = mF1.onCreateView(name, context, attrs); if (v != null) return v; return mF2.onCreateView(name, context, attrs); } - public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { + @Nullable + public View onCreateView(@Nullable View parent, @NonNull String name, + @NonNull Context context, @NonNull AttributeSet attrs) { View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs) : mF1.onCreateView(name, context, attrs); if (v != null) return v; diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java index 7bae28a4e817..82e9a5c71eae 100644 --- a/core/java/android/view/WindowInfo.java +++ b/core/java/android/view/WindowInfo.java @@ -49,6 +49,7 @@ public class WindowInfo implements Parcelable { public CharSequence title; public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID; public boolean inPictureInPicture; + public boolean hasFlagWatchOutsideTouch; private WindowInfo() { /* do nothing - hide constructor */ @@ -74,6 +75,7 @@ public class WindowInfo implements Parcelable { window.title = other.title; window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor; window.inPictureInPicture = other.inPictureInPicture; + window.hasFlagWatchOutsideTouch = other.hasFlagWatchOutsideTouch; if (other.childTokens != null && !other.childTokens.isEmpty()) { if (window.childTokens == null) { @@ -108,6 +110,7 @@ public class WindowInfo implements Parcelable { parcel.writeCharSequence(title); parcel.writeLong(accessibilityIdOfAnchor); parcel.writeInt(inPictureInPicture ? 1 : 0); + parcel.writeInt(hasFlagWatchOutsideTouch ? 1 : 0); if (childTokens != null && !childTokens.isEmpty()) { parcel.writeInt(1); @@ -130,6 +133,8 @@ public class WindowInfo implements Parcelable { builder.append(", focused=").append(focused); builder.append(", children=").append(childTokens); builder.append(", accessibility anchor=").append(accessibilityIdOfAnchor); + builder.append(", pictureInPicture=").append(inPictureInPicture); + builder.append(", watchOutsideTouch=").append(hasFlagWatchOutsideTouch); builder.append(']'); return builder.toString(); } @@ -145,6 +150,7 @@ public class WindowInfo implements Parcelable { title = parcel.readCharSequence(); accessibilityIdOfAnchor = parcel.readLong(); inPictureInPicture = (parcel.readInt() == 1); + hasFlagWatchOutsideTouch = (parcel.readInt() == 1); final boolean hasChildren = (parcel.readInt() == 1); if (hasChildren) { @@ -167,6 +173,7 @@ public class WindowInfo implements Parcelable { childTokens.clear(); } inPictureInPicture = false; + hasFlagWatchOutsideTouch = false; } public static final Parcelable.Creator<WindowInfo> CREATOR = diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index c59c491be878..e88682ea6287 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -139,6 +139,8 @@ public final class AccessibilityManager { int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; + int mMinimumUiTimeout; + boolean mIsTouchExplorationEnabled; @UnsupportedAppUsage @@ -320,6 +322,11 @@ public final class AccessibilityManager { public void setRelevantEventTypes(int eventTypes) { mRelevantEventTypes = eventTypes; } + + @Override + public void setMinimumUiTimeout(int uiTimeout) { + mMinimumUiTimeout = uiTimeout; + } }; /** @@ -827,6 +834,19 @@ public final class AccessibilityManager { } /** + * Get the minimum timeout for changes to the UI needed by this user. Controls should remain + * on the screen for at least this long to give users time to react. Some users may need + * extra time to review the controls, or to reach them, or to activate assistive technology + * to activate the controls automatically. + * + * @return The minimum ui timeout for the current user in milliseconds. + * {@link Integer#MAX_VALUE} if timeout is infinite. + */ + public int getMinimumUiTimeoutMillis() { + return mMinimumUiTimeout; + } + + /** * Get the preparers that are registered for an accessibility ID * * @param id The ID of interest @@ -1139,6 +1159,7 @@ public final class AccessibilityManager { final long userStateAndRelevantEvents = service.addClient(mClient, mUserId); setStateLocked(IntPair.first(userStateAndRelevantEvents)); mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents); + mMinimumUiTimeout = service.getMinimumUiTimeout(); mService = service; } catch (RemoteException re) { Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index c93e2c15407b..3e2ef186f793 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -72,4 +72,6 @@ interface IAccessibilityManager { // System process only boolean sendFingerprintGesture(int gestureKeyCode); + + int getMinimumUiTimeout(); } diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl index 9cc0315280c7..d2ddca3acae7 100644 --- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl @@ -29,4 +29,6 @@ oneway interface IAccessibilityManagerClient { void notifyServicesStateChanged(); void setRelevantEventTypes(int eventTypes); + + void setMinimumUiTimeout(int uiTimeout); } diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index 61a697282dcb..a7d7a8d67f64 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -28,7 +28,7 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Slog; -import com.android.internal.inputmethod.LocaleUtils; +import com.android.internal.inputmethod.InputMethodUtils; import java.util.ArrayList; import java.util.Arrays; @@ -384,7 +384,7 @@ public final class InputMethodSubtype implements Parcelable { if (!TextUtils.isEmpty(mSubtypeLanguageTag)) { mCachedLocaleObj = Locale.forLanguageTag(mSubtypeLanguageTag); } else { - mCachedLocaleObj = LocaleUtils.constructLocaleFromString(mSubtypeLocale); + mCachedLocaleObj = InputMethodUtils.constructLocaleFromString(mSubtypeLocale); } return mCachedLocaleObj; } diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java index 41b70b61fd4a..026610ecb6f9 100644 --- a/core/java/android/view/textservice/SpellCheckerSubtype.java +++ b/core/java/android/view/textservice/SpellCheckerSubtype.java @@ -25,7 +25,7 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Slog; -import com.android.internal.inputmethod.LocaleUtils; +import com.android.internal.inputmethod.InputMethodUtils; import java.util.ArrayList; import java.util.Arrays; @@ -228,7 +228,7 @@ public final class SpellCheckerSubtype implements Parcelable { if (!TextUtils.isEmpty(mSubtypeLanguageTag)) { return Locale.forLanguageTag(mSubtypeLanguageTag); } - return LocaleUtils.constructLocaleFromString(mSubtypeLocale); + return InputMethodUtils.constructLocaleFromString(mSubtypeLocale); } /** diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index e9a9e8f80662..46b1f6e6189e 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -51,9 +51,6 @@ public final class WebViewFactory { private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create"; - public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = - "persist.sys.webview.vmsize"; - private static final String LOGTAG = "WebViewFactory"; private static final boolean DEBUG = false; diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java index cabba06bdff5..5a6aebaaad51 100644 --- a/core/java/android/webkit/WebViewLibraryLoader.java +++ b/core/java/android/webkit/WebViewLibraryLoader.java @@ -17,15 +17,14 @@ package android.webkit; import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.ActivityManagerInternal; -import android.content.pm.ApplicationInfo; +import android.app.ActivityThread; +import android.app.LoadedApk; +import android.content.Context; import android.content.pm.PackageInfo; import android.os.Build; import android.os.Process; import android.os.RemoteException; -import android.os.SystemProperties; -import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -33,11 +32,7 @@ import com.android.server.LocalServices; import dalvik.system.VMRuntime; -import java.io.File; -import java.io.IOException; import java.util.Arrays; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; /** * @hide @@ -50,7 +45,6 @@ public class WebViewLibraryLoader { "/data/misc/shared_relro/libwebviewchromium32.relro"; private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 = "/data/misc/shared_relro/libwebviewchromium64.relro"; - private static final long CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES = 100 * 1024 * 1024; private static final boolean DEBUG = false; @@ -69,18 +63,26 @@ public class WebViewLibraryLoader { boolean result = false; boolean is64Bit = VMRuntime.getRuntime().is64Bit(); try { - if (args.length != 1 || args[0] == null) { + if (args.length != 2 || args[0] == null || args[1] == null) { Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args)); return; } - Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), lib: " + args[0]); + String packageName = args[0]; + String libraryFileName = args[1]; + Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), package: " + + packageName + " library: " + libraryFileName); if (!sAddressSpaceReserved) { Log.e(LOGTAG, "can't create relro file; address space not reserved"); return; } - result = nativeCreateRelroFile(args[0] /* path */, + LoadedApk apk = ActivityThread.currentActivityThread().getPackageInfo( + packageName, + null, + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + result = nativeCreateRelroFile(libraryFileName, is64Bit ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 : - CHROMIUM_WEBVIEW_NATIVE_RELRO_32); + CHROMIUM_WEBVIEW_NATIVE_RELRO_32, + apk.getClassLoader()); if (result && DEBUG) Log.v(LOGTAG, "created relro file"); } finally { // We must do our best to always notify the update service, even if something fails. @@ -101,7 +103,8 @@ public class WebViewLibraryLoader { /** * Create a single relro file by invoking an isolated process that to do the actual work. */ - static void createRelroFile(final boolean is64Bit, @NonNull WebViewNativeLibrary nativeLib) { + static void createRelroFile(final boolean is64Bit, @NonNull String packageName, + @NonNull String libraryFileName) { final String abi = is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]; @@ -119,13 +122,10 @@ public class WebViewLibraryLoader { }; try { - if (nativeLib == null || nativeLib.path == null) { - throw new IllegalArgumentException( - "Native library paths to the WebView RelRo process must not be null!"); - } boolean success = LocalServices.getService(ActivityManagerInternal.class) .startIsolatedProcess( - RelroFileCreator.class.getName(), new String[] { nativeLib.path }, + RelroFileCreator.class.getName(), + new String[] { packageName, libraryFileName }, "WebViewLoader-" + abi, abi, Process.SHARED_RELRO_UID, crashHandler); if (!success) throw new Exception("Failed to start the relro file creator process"); } catch (Throwable t) { @@ -140,83 +140,50 @@ public class WebViewLibraryLoader { * be called whenever we change WebView provider. * @return the number of relro processes started. */ - static int prepareNativeLibraries(PackageInfo webviewPackageInfo) - throws WebViewFactory.MissingWebViewPackageException { - WebViewNativeLibrary nativeLib32bit = - getWebViewNativeLibrary(webviewPackageInfo, false /* is64bit */); - WebViewNativeLibrary nativeLib64bit = - getWebViewNativeLibrary(webviewPackageInfo, true /* is64bit */); - updateWebViewZygoteVmSize(nativeLib32bit, nativeLib64bit); - - return createRelros(nativeLib32bit, nativeLib64bit); + static int prepareNativeLibraries(@NonNull PackageInfo webViewPackageInfo) { + // TODO(torne): new way of calculating VM size + // updateWebViewZygoteVmSize(nativeLib32bit, nativeLib64bit); + String libraryFileName = WebViewFactory.getWebViewLibrary( + webViewPackageInfo.applicationInfo); + if (libraryFileName == null) { + // Can't do anything with no filename, don't spawn any processes. + return 0; + } + return createRelros(webViewPackageInfo.packageName, libraryFileName); } /** * @return the number of relro processes started. */ - private static int createRelros(@Nullable WebViewNativeLibrary nativeLib32bit, - @Nullable WebViewNativeLibrary nativeLib64bit) { + private static int createRelros(@NonNull String packageName, @NonNull String libraryFileName) { if (DEBUG) Log.v(LOGTAG, "creating relro files"); int numRelros = 0; if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { - if (nativeLib32bit == null) { - Log.e(LOGTAG, "No 32-bit WebView library path, skipping relro creation."); - } else { - if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro"); - createRelroFile(false /* is64Bit */, nativeLib32bit); - numRelros++; - } + if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro"); + createRelroFile(false /* is64Bit */, packageName, libraryFileName); + numRelros++; } if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { - if (nativeLib64bit == null) { - Log.e(LOGTAG, "No 64-bit WebView library path, skipping relro creation."); - } else { - if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro"); - createRelroFile(true /* is64Bit */, nativeLib64bit); - numRelros++; - } + if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro"); + createRelroFile(true /* is64Bit */, packageName, libraryFileName); + numRelros++; } return numRelros; } /** - * - * @return the native WebView libraries in the new WebView APK. - */ - private static void updateWebViewZygoteVmSize( - @Nullable WebViewNativeLibrary nativeLib32bit, - @Nullable WebViewNativeLibrary nativeLib64bit) - throws WebViewFactory.MissingWebViewPackageException { - // Find the native libraries of the new WebView package, to change the size of the - // memory region in the Zygote reserved for the library. - long newVmSize = 0L; - - if (nativeLib32bit != null) newVmSize = Math.max(newVmSize, nativeLib32bit.size); - if (nativeLib64bit != null) newVmSize = Math.max(newVmSize, nativeLib64bit.size); - - if (DEBUG) { - Log.v(LOGTAG, "Based on library size, need " + newVmSize - + " bytes of address space."); - } - // The required memory can be larger than the file on disk (due to .bss), and an - // upgraded version of the library will likely be larger, so always attempt to - // reserve twice as much as we think to allow for the library to grow during this - // boot cycle. - newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES); - Log.d(LOGTAG, "Setting new address space to " + newVmSize); - setWebViewZygoteVmSize(newVmSize); - } - - /** * Reserve space for the native library to be loaded into. */ static void reserveAddressSpaceInZygote() { System.loadLibrary("webviewchromium_loader"); - long addressSpaceToReserve = - SystemProperties.getLong(WebViewFactory.CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY, - CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES); + boolean is64Bit = VMRuntime.getRuntime().is64Bit(); + // On 64-bit address space is really cheap and we can reserve 1GB which is plenty. + // On 32-bit it's fairly scarce and we should keep it to a realistic number that + // permits some future growth but doesn't hog space: we use 100MB which is more than 2x + // the current requirement. + long addressSpaceToReserve = is64Bit ? 1 * 1024 * 1024 * 1024 : 100 * 1024 * 1024; sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve); if (sAddressSpaceReserved) { @@ -253,106 +220,7 @@ public class WebViewLibraryLoader { return result; } - /** - * Fetch WebView's native library paths from {@param packageInfo}. - * @hide - */ - @Nullable - @VisibleForTesting - public static WebViewNativeLibrary getWebViewNativeLibrary(PackageInfo packageInfo, - boolean is64bit) throws WebViewFactory.MissingWebViewPackageException { - ApplicationInfo ai = packageInfo.applicationInfo; - final String nativeLibFileName = WebViewFactory.getWebViewLibrary(ai); - - String dir = getWebViewNativeLibraryDirectory(ai, is64bit /* 64bit */); - - WebViewNativeLibrary lib = findNativeLibrary(ai, nativeLibFileName, - is64bit ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS, dir); - - if (DEBUG) { - Log.v(LOGTAG, String.format("Native %d-bit lib: %s", is64bit ? 64 : 32, lib.path)); - } - return lib; - } - - /** - * @return the directory of the native WebView library with bitness {@param is64bit}. - * @hide - */ - @VisibleForTesting - public static String getWebViewNativeLibraryDirectory(ApplicationInfo ai, boolean is64bit) { - // Primary arch has the same bitness as the library we are looking for. - if (is64bit == VMRuntime.is64BitAbi(ai.primaryCpuAbi)) return ai.nativeLibraryDir; - - // Secondary arch has the same bitness as the library we are looking for. - if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) { - return ai.secondaryNativeLibraryDir; - } - - return ""; - } - - /** - * @return an object describing a native WebView library given the directory path of that - * library, or null if the library couldn't be found. - */ - @Nullable - private static WebViewNativeLibrary findNativeLibrary(ApplicationInfo ai, - String nativeLibFileName, String[] abiList, String libDirectory) - throws WebViewFactory.MissingWebViewPackageException { - if (TextUtils.isEmpty(libDirectory)) return null; - String libPath = libDirectory + "/" + nativeLibFileName; - File f = new File(libPath); - if (f.exists()) { - return new WebViewNativeLibrary(libPath, f.length()); - } else { - return getLoadFromApkPath(ai.sourceDir, abiList, nativeLibFileName); - } - } - - /** - * @hide - */ - @VisibleForTesting - public static class WebViewNativeLibrary { - public final String path; - public final long size; - - WebViewNativeLibrary(String path, long size) { - this.path = path; - this.size = size; - } - } - - private static WebViewNativeLibrary getLoadFromApkPath(String apkPath, - String[] abiList, - String nativeLibFileName) - throws WebViewFactory.MissingWebViewPackageException { - // Search the APK for a native library conforming to a listed ABI. - try (ZipFile z = new ZipFile(apkPath)) { - for (String abi : abiList) { - final String entry = "lib/" + abi + "/" + nativeLibFileName; - ZipEntry e = z.getEntry(entry); - if (e != null && e.getMethod() == ZipEntry.STORED) { - // Return a path formatted for dlopen() load from APK. - return new WebViewNativeLibrary(apkPath + "!/" + entry, e.getSize()); - } - } - } catch (IOException e) { - throw new WebViewFactory.MissingWebViewPackageException(e); - } - return null; - } - - /** - * Sets the size of the memory area in which to store the relro section. - */ - private static void setWebViewZygoteVmSize(long vmSize) { - SystemProperties.set(WebViewFactory.CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY, - Long.toString(vmSize)); - } - static native boolean nativeReserveAddressSpace(long addressSpaceToReserve); - static native boolean nativeCreateRelroFile(String lib, String relro); + static native boolean nativeCreateRelroFile(String lib, String relro, ClassLoader clazzLoader); static native int nativeLoadWithRelroFile(String lib, String relro, ClassLoader clazzLoader); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 0645a162f377..d9132736232a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -313,6 +313,7 @@ import java.util.function.Supplier; * @attr ref android.R.styleable#TextView_fallbackLineSpacing * @attr ref android.R.styleable#TextView_letterSpacing * @attr ref android.R.styleable#TextView_fontFeatureSettings + * @attr ref android.R.styleable#TextView_fontVariationSettings * @attr ref android.R.styleable#TextView_breakStrategy * @attr ref android.R.styleable#TextView_hyphenationFrequency * @attr ref android.R.styleable#TextView_autoSizeTextType @@ -4382,6 +4383,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @see #getFontVariationSettings() * @see FontVariationAxis + * + * @attr ref android.R.styleable#TextView_fontVariationSettings */ public boolean setFontVariationSettings(@Nullable String fontVariationSettings) { final String existingSettings = mTextPaint.getFontVariationSettings(); diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java index 7b25a0691c40..3fd88dbb8704 100644 --- a/core/java/com/android/internal/colorextraction/types/Tonal.java +++ b/core/java/com/android/internal/colorextraction/types/Tonal.java @@ -51,10 +51,8 @@ public class Tonal implements ExtractionType { private static final boolean DEBUG = true; - public static final int THRESHOLD_COLOR_LIGHT = 0xffe0e0e0; public static final int MAIN_COLOR_LIGHT = 0xffe0e0e0; - public static final int THRESHOLD_COLOR_DARK = 0xff212121; - public static final int MAIN_COLOR_DARK = 0xff000000; + public static final int MAIN_COLOR_DARK = 0xff212121; private final TonalPalette mGreyPalette; private final ArrayList<TonalPalette> mTonalPalettes; @@ -197,12 +195,12 @@ public class Tonal implements ExtractionType { // light fallback or darker than our dark fallback. ColorUtils.colorToHSL(mainColor, mTmpHSL); final float mainLuminosity = mTmpHSL[2]; - ColorUtils.colorToHSL(THRESHOLD_COLOR_LIGHT, mTmpHSL); + ColorUtils.colorToHSL(MAIN_COLOR_LIGHT, mTmpHSL); final float lightLuminosity = mTmpHSL[2]; if (mainLuminosity > lightLuminosity) { return false; } - ColorUtils.colorToHSL(THRESHOLD_COLOR_DARK, mTmpHSL); + ColorUtils.colorToHSL(MAIN_COLOR_DARK, mTmpHSL); final float darkLuminosity = mTmpHSL[2]; if (mainLuminosity < darkLuminosity) { return false; diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index 1410ff9a0813..1e5b5c80f31b 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -334,6 +334,32 @@ public class InputMethodUtils { return getDefaultEnabledImes(context, imis, false /* onlyMinimum */); } + public static Locale constructLocaleFromString(String localeStr) { + if (TextUtils.isEmpty(localeStr)) { + return null; + } + // TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)}. + String[] localeParams = localeStr.split("_", 3); + if (localeParams.length >= 1 && "tl".equals(localeParams[0])) { + // Convert a locale whose language is "tl" to one whose language is "fil". + // For example, "tl_PH" will get converted to "fil_PH". + // Versions of Android earlier than Lollipop did not support three letter language + // codes, and used "tl" (Tagalog) as the language string for "fil" (Filipino). + // On Lollipop and above, the current three letter version must be used. + localeParams[0] = "fil"; + } + // The length of localeStr is guaranteed to always return a 1 <= value <= 3 + // because localeStr is not empty. + if (localeParams.length == 1) { + return new Locale(localeParams[0]); + } else if (localeParams.length == 2) { + return new Locale(localeParams[0], localeParams[1]); + } else if (localeParams.length == 3) { + return new Locale(localeParams[0], localeParams[1], localeParams[2]); + } + return null; + } + public static boolean containsSubtypeOf(final InputMethodInfo imi, @Nullable final Locale locale, final boolean checkCountry, final String mode) { if (locale == null) { @@ -1294,7 +1320,133 @@ public class InputMethodUtils { } } + // For spell checker service manager. + // TODO: Should we have TextServicesUtils.java? + private static final Locale LOCALE_EN_US = new Locale("en", "US"); + private static final Locale LOCALE_EN_GB = new Locale("en", "GB"); + + /** + * Returns a list of {@link Locale} in the order of appropriateness for the default spell + * checker service. + * + * <p>If the system language is English, and the region is also explicitly specified in the + * system locale, the following fallback order will be applied.</p> + * <ul> + * <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li> + * <li>(system-locale-language, system-locale-region)</li> + * <li>("en", "US")</li> + * <li>("en", "GB")</li> + * <li>("en")</li> + * </ul> + * + * <p>If the system language is English, but no region is specified in the system locale, + * the following fallback order will be applied.</p> + * <ul> + * <li>("en")</li> + * <li>("en", "US")</li> + * <li>("en", "GB")</li> + * </ul> + * + * <p>If the system language is not English, the following fallback order will be applied.</p> + * <ul> + * <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li> + * <li>(system-locale-language, system-locale-region) (if exists)</li> + * <li>(system-locale-language) (if exists)</li> + * <li>("en", "US")</li> + * <li>("en", "GB")</li> + * <li>("en")</li> + * </ul> + * + * @param systemLocale the current system locale to be taken into consideration. + * @return a list of {@link Locale}. The first one is considered to be most appropriate. + */ @VisibleForTesting + public static ArrayList<Locale> getSuitableLocalesForSpellChecker( + @Nullable final Locale systemLocale) { + final Locale systemLocaleLanguageCountryVariant; + final Locale systemLocaleLanguageCountry; + final Locale systemLocaleLanguage; + if (systemLocale != null) { + final String language = systemLocale.getLanguage(); + final boolean hasLanguage = !TextUtils.isEmpty(language); + final String country = systemLocale.getCountry(); + final boolean hasCountry = !TextUtils.isEmpty(country); + final String variant = systemLocale.getVariant(); + final boolean hasVariant = !TextUtils.isEmpty(variant); + if (hasLanguage && hasCountry && hasVariant) { + systemLocaleLanguageCountryVariant = new Locale(language, country, variant); + } else { + systemLocaleLanguageCountryVariant = null; + } + if (hasLanguage && hasCountry) { + systemLocaleLanguageCountry = new Locale(language, country); + } else { + systemLocaleLanguageCountry = null; + } + if (hasLanguage) { + systemLocaleLanguage = new Locale(language); + } else { + systemLocaleLanguage = null; + } + } else { + systemLocaleLanguageCountryVariant = null; + systemLocaleLanguageCountry = null; + systemLocaleLanguage = null; + } + + final ArrayList<Locale> locales = new ArrayList<>(); + if (systemLocaleLanguageCountryVariant != null) { + locales.add(systemLocaleLanguageCountryVariant); + } + + if (Locale.ENGLISH.equals(systemLocaleLanguage)) { + if (systemLocaleLanguageCountry != null) { + // If the system language is English, and the region is also explicitly specified, + // following fallback order will be applied. + // - systemLocaleLanguageCountry [if systemLocaleLanguageCountry is non-null] + // - en_US [if systemLocaleLanguageCountry is non-null and not en_US] + // - en_GB [if systemLocaleLanguageCountry is non-null and not en_GB] + // - en + if (systemLocaleLanguageCountry != null) { + locales.add(systemLocaleLanguageCountry); + } + if (!LOCALE_EN_US.equals(systemLocaleLanguageCountry)) { + locales.add(LOCALE_EN_US); + } + if (!LOCALE_EN_GB.equals(systemLocaleLanguageCountry)) { + locales.add(LOCALE_EN_GB); + } + locales.add(Locale.ENGLISH); + } else { + // If the system language is English, but no region is specified, following + // fallback order will be applied. + // - en + // - en_US + // - en_GB + locales.add(Locale.ENGLISH); + locales.add(LOCALE_EN_US); + locales.add(LOCALE_EN_GB); + } + } else { + // If the system language is not English, the fallback order will be + // - systemLocaleLanguageCountry [if non-null] + // - systemLocaleLanguage [if non-null] + // - en_US + // - en_GB + // - en + if (systemLocaleLanguageCountry != null) { + locales.add(systemLocaleLanguageCountry); + } + if (systemLocaleLanguage != null) { + locales.add(systemLocaleLanguage); + } + locales.add(LOCALE_EN_US); + locales.add(LOCALE_EN_GB); + locales.add(Locale.ENGLISH); + } + return locales; + } + public static boolean isSoftInputModeStateVisibleAllowed( int targetSdkVersion, int controlFlags) { if (targetSdkVersion < Build.VERSION_CODES.P) { @@ -1309,4 +1461,5 @@ public class InputMethodUtils { } return true; } + } diff --git a/core/java/com/android/internal/inputmethod/LocaleUtils.java b/core/java/com/android/internal/inputmethod/LocaleUtils.java index f7360eb0fe13..eeb3854bcc55 100644 --- a/core/java/com/android/internal/inputmethod/LocaleUtils.java +++ b/core/java/com/android/internal/inputmethod/LocaleUtils.java @@ -16,6 +16,8 @@ package com.android.internal.inputmethod; +import com.android.internal.annotations.VisibleForTesting; + import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -23,8 +25,6 @@ import android.icu.util.ULocale; import android.os.LocaleList; import android.text.TextUtils; -import com.android.internal.annotations.VisibleForTesting; - import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -208,151 +208,4 @@ public final class LocaleUtils { dest.add(sources.get(entry.mIndex)); } } - - public static Locale constructLocaleFromString(String localeStr) { - if (TextUtils.isEmpty(localeStr)) { - return null; - } - // TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)}. - String[] localeParams = localeStr.split("_", 3); - if (localeParams.length >= 1 && "tl".equals(localeParams[0])) { - // Convert a locale whose language is "tl" to one whose language is "fil". - // For example, "tl_PH" will get converted to "fil_PH". - // Versions of Android earlier than Lollipop did not support three letter language - // codes, and used "tl" (Tagalog) as the language string for "fil" (Filipino). - // On Lollipop and above, the current three letter version must be used. - localeParams[0] = "fil"; - } - // The length of localeStr is guaranteed to always return a 1 <= value <= 3 - // because localeStr is not empty. - if (localeParams.length == 1) { - return new Locale(localeParams[0]); - } else if (localeParams.length == 2) { - return new Locale(localeParams[0], localeParams[1]); - } else if (localeParams.length == 3) { - return new Locale(localeParams[0], localeParams[1], localeParams[2]); - } - return null; - } - - /** - * Returns a list of {@link Locale} in the order of appropriateness for the default spell - * checker service. - * - * <p>If the system language is English, and the region is also explicitly specified in the - * system locale, the following fallback order will be applied.</p> - * <ul> - * <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li> - * <li>(system-locale-language, system-locale-region)</li> - * <li>("en", "US")</li> - * <li>("en", "GB")</li> - * <li>("en")</li> - * </ul> - * - * <p>If the system language is English, but no region is specified in the system locale, - * the following fallback order will be applied.</p> - * <ul> - * <li>("en")</li> - * <li>("en", "US")</li> - * <li>("en", "GB")</li> - * </ul> - * - * <p>If the system language is not English, the following fallback order will be applied.</p> - * <ul> - * <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li> - * <li>(system-locale-language, system-locale-region) (if exists)</li> - * <li>(system-locale-language) (if exists)</li> - * <li>("en", "US")</li> - * <li>("en", "GB")</li> - * <li>("en")</li> - * </ul> - * - * @param systemLocale the current system locale to be taken into consideration. - * @return a list of {@link Locale}. The first one is considered to be most appropriate. - */ - public static ArrayList<Locale> getSuitableLocalesForSpellChecker( - @Nullable final Locale systemLocale) { - final Locale systemLocaleLanguageCountryVariant; - final Locale systemLocaleLanguageCountry; - final Locale systemLocaleLanguage; - if (systemLocale != null) { - final String language = systemLocale.getLanguage(); - final boolean hasLanguage = !TextUtils.isEmpty(language); - final String country = systemLocale.getCountry(); - final boolean hasCountry = !TextUtils.isEmpty(country); - final String variant = systemLocale.getVariant(); - final boolean hasVariant = !TextUtils.isEmpty(variant); - if (hasLanguage && hasCountry && hasVariant) { - systemLocaleLanguageCountryVariant = new Locale(language, country, variant); - } else { - systemLocaleLanguageCountryVariant = null; - } - if (hasLanguage && hasCountry) { - systemLocaleLanguageCountry = new Locale(language, country); - } else { - systemLocaleLanguageCountry = null; - } - if (hasLanguage) { - systemLocaleLanguage = new Locale(language); - } else { - systemLocaleLanguage = null; - } - } else { - systemLocaleLanguageCountryVariant = null; - systemLocaleLanguageCountry = null; - systemLocaleLanguage = null; - } - - final ArrayList<Locale> locales = new ArrayList<>(); - if (systemLocaleLanguageCountryVariant != null) { - locales.add(systemLocaleLanguageCountryVariant); - } - - if (Locale.ENGLISH.equals(systemLocaleLanguage)) { - if (systemLocaleLanguageCountry != null) { - // If the system language is English, and the region is also explicitly specified, - // following fallback order will be applied. - // - systemLocaleLanguageCountry [if systemLocaleLanguageCountry is non-null] - // - en_US [if systemLocaleLanguageCountry is non-null and not en_US] - // - en_GB [if systemLocaleLanguageCountry is non-null and not en_GB] - // - en - if (systemLocaleLanguageCountry != null) { - locales.add(systemLocaleLanguageCountry); - } - if (!Locale.US.equals(systemLocaleLanguageCountry)) { - locales.add(Locale.US); - } - if (!Locale.UK.equals(systemLocaleLanguageCountry)) { - locales.add(Locale.UK); - } - locales.add(Locale.ENGLISH); - } else { - // If the system language is English, but no region is specified, following - // fallback order will be applied. - // - en - // - en_US - // - en_GB - locales.add(Locale.ENGLISH); - locales.add(Locale.US); - locales.add(Locale.UK); - } - } else { - // If the system language is not English, the fallback order will be - // - systemLocaleLanguageCountry [if non-null] - // - systemLocaleLanguage [if non-null] - // - en_US - // - en_GB - // - en - if (systemLocaleLanguageCountry != null) { - locales.add(systemLocaleLanguageCountry); - } - if (systemLocaleLanguage != null) { - locales.add(systemLocaleLanguage); - } - locales.add(Locale.US); - locales.add(Locale.UK); - locales.add(Locale.ENGLISH); - } - return locales; - } } diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java index 16ca4fcdbe34..1038199cf109 100644 --- a/core/java/com/android/internal/util/ContrastColorUtil.java +++ b/core/java/com/android/internal/util/ContrastColorUtil.java @@ -455,7 +455,7 @@ public class ContrastColorUtil { * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT} */ public static int resolveColor(Context context, int color, boolean defaultBackgroundIsDark) { - if (color == Notification.COLOR_DEFAULT) { + if (color == Notification.COLOR_DEFAULT || defaultBackgroundIsDark) { int res = defaultBackgroundIsDark ? com.android.internal.R.color.notification_default_color_dark : com.android.internal.R.color.notification_default_color_light; diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS index 21d750c59a4e..e65d11404a4e 100644 --- a/core/java/com/android/internal/util/OWNERS +++ b/core/java/com/android/internal/util/OWNERS @@ -1,24 +1,4 @@ -per-file AsyncChannel*=lorenzo@google.com -per-file AsyncChannel*=satk@google.com -per-file AsyncChannel*=silberst@google.com -per-file BitUtils*=ek@google.com -per-file BitUtils*=lorenzo@google.com -per-file BitUtils*=satk@google.com -per-file MessageUtils*=ek@google.com -per-file MessageUtils*=lorenzo@google.com -per-file MessageUtils*=satk@google.com -per-file Protocol*=ek@google.com -per-file Protocol*=lorenzo@google.com -per-file Protocol*=quiche@google.com -per-file Protocol*=satk@google.com -per-file Protocol*=silberst@google.com -per-file RingBuffer*=ek@google.com -per-file RingBuffer*=lorenzo@google.com -per-file RingBuffer*=satk@google.com -per-file State*=ek@google.com -per-file State*=lorenzo@google.com -per-file State*=quiche@google.com -per-file State*=silberst@google.com -per-file TokenBucket*=ek@google.com -per-file TokenBucket*=lorenzo@google.com -per-file TokenBucket*=satk@google.com +per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com +per-file BitUtils*, MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com +per-file Protocol* = etancohen@google.com, lorenzo@google.com +per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 6e661e1ce5b2..3ead63318a01 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -72,6 +72,9 @@ message SecureSettingsProto { // List of the accessibility services to which the user has granted // permission to put the device into touch exploration mode. optional SettingProto touch_exploration_granted_accessibility_services = 31; + optional SettingProto minimum_ui_timeout_enabled = 32 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto minimum_ui_timeout_ms = 33 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } optional Accessibility accessibility = 2; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e7084a741495..9ab55d62839b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3352,7 +3352,7 @@ @hide @removed --> <permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" - android:protectionLevel="signature" /> + android:protectionLevel="signature|privileged" /> <!-- @SystemApi Allows an application to know what content is playing and control its playback. <p>Not for use by third-party applications due to privacy of media consumption</p> --> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 6932be373c0b..e153082045ec 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -284,7 +284,7 @@ <string name="permgrouprequest_sms" msgid="7168124215838204719">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'envoyer et d\'afficher des SMS ?"</string> <string name="permgrouplab_storage" msgid="1971118770546336966">"Stockage"</string> <string name="permgroupdesc_storage" msgid="637758554581589203">"accéder à des photos, à des contenus multimédias et à des fichiers sur votre appareil"</string> - <string name="permgrouprequest_storage" msgid="7885942926944299560">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'accéder aux photos, contenus multimédias et fichiers sur votre appareil ?"</string> + <string name="permgrouprequest_storage" msgid="7885942926944299560">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder aux photos, contenus multimédias et fichiers sur votre appareil ?"</string> <string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string> <string name="permgroupdesc_microphone" msgid="4988812113943554584">"enregistrer des fichiers audio"</string> <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'enregistrer des contenus audio ?"</string> @@ -1340,7 +1340,7 @@ <string name="ext_media_unmountable_notification_message" msgid="4193858924381066522">"Appuyez sur la notification pour résoudre le problème"</string> <string name="ext_media_unmountable_notification_message" product="tv" msgid="3941179940297874950">"La <xliff:g id="NAME">%s</xliff:g> est corrompue. Sélectionnez cette option pour résoudre le problème."</string> <string name="ext_media_unsupported_notification_title" msgid="3797642322958803257">"<xliff:g id="NAME">%s</xliff:g> non compatible"</string> - <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Cet appareil n\'est pas compatible avec la mémoire de stockage \"<xliff:g id="NAME">%s</xliff:g>\". Appuyez ici pour le configurer dans un format accepté."</string> + <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Cet appareil n\'est pas compatible avec le support \"<xliff:g id="NAME">%s</xliff:g>\". Appuyez ici pour le configurer dans un format accepté."</string> <string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"Cet appareil n\'est pas compatible avec cette <xliff:g id="NAME">%s</xliff:g>. Sélectionnez cette option pour la configurer dans un format accepté."</string> <string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"Retrait inattendu de mémoire \"<xliff:g id="NAME">%s</xliff:g>\""</string> <string name="ext_media_badremoval_notification_message" msgid="8556885808951260574">"Éjectez le périphérique externe avant de le retirer pour éviter toute perte de contenu"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index d4b800ad2e04..6afe603993ab 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1895,10 +1895,10 @@ <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> को <b><xliff:g id="LABEL">%2$s</xliff:g></b> में सेव करें?"</string> <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> को <b><xliff:g id="LABEL">%3$s</xliff:g></b> में सेव करें?"</string> <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को <b><xliff:g id="LABEL">%4$s</xliff:g></b> में सेव करें?"</string> - <string name="autofill_update_title" msgid="4879673117448810818">"<b><xliff:g id="LABEL">%1$s</xliff:g></b>? में अपडेट करें?"</string> - <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> को to <b><xliff:g id="LABEL">%2$s</xliff:g></b> में अपडेट करें?"</string> + <string name="autofill_update_title" msgid="4879673117448810818">"<b><xliff:g id="LABEL">%1$s</xliff:g></b> में अपडेट करें?"</string> + <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> को <b><xliff:g id="LABEL">%2$s</xliff:g></b> में अपडेट करें?"</string> <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> को <b><xliff:g id="LABEL">%3$s</xliff:g></b> में अपडेट करें?"</string> - <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को <b><xliff:g id="LABEL">%4$s</xliff:g></b> में अपडेट करें."</string> + <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को <b><xliff:g id="LABEL">%4$s</xliff:g></b> में अपडेट करें?"</string> <string name="autofill_save_yes" msgid="6398026094049005921">"सेव करें"</string> <string name="autofill_save_no" msgid="2625132258725581787">"नहीं, धन्यवाद"</string> <string name="autofill_update_yes" msgid="310358413273276958">"अपडेट करें"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 8b3f287d08aa..6a5f287d6f68 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -514,7 +514,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Թույլ է տալիս հավելվածին փոփոխել ձեր լուսանկարների հավաքածուն:"</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"ճանաչել տեղադրության մասին տվյալները մեդիա բովանդակության հավաքածուից"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Թույլ է տալիս հավելվածին ճանաչել տեղադրության մասին տվյալները ձեր մեդիա բովանդակության հավաքածուից:"</string> - <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Կենսաչափական սարք չի գտնվել"</string> + <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Կենսաչափական սարքը հասանելի չէ"</string> <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Մատնահետքը հայտնաբերվել է մասամբ: Փորձեք նորից:"</string> <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string> <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Մատնահետքերի սենսորն աղտոտված է: Մաքրեք այն և փորձեք նորից:"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 4cada6068fe7..4915111c9e07 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -284,7 +284,7 @@ <string name="permgrouprequest_sms" msgid="7168124215838204719">"SMS メッセージの送信と表示を <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> <string name="permgrouplab_storage" msgid="1971118770546336966">"ストレージ"</string> <string name="permgroupdesc_storage" msgid="637758554581589203">"端末内の写真、メディア、ファイルへのアクセス"</string> - <string name="permgrouprequest_storage" msgid="7885942926944299560">"端末内の写真、メディア、ファイルへのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> + <string name="permgrouprequest_storage" msgid="7885942926944299560">"端末内の写真、メディア、ファイルへのアクセスを「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string> <string name="permgrouplab_microphone" msgid="171539900250043464">"マイク"</string> <string name="permgroupdesc_microphone" msgid="4988812113943554584">"音声の録音"</string> <string name="permgrouprequest_microphone" msgid="9167492350681916038">"音声の録音を <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index dca94ff526e1..e228d35d7067 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -284,7 +284,7 @@ <string name="permgrouprequest_sms" msgid="7168124215838204719">"ಎಸ್ಎಂಎಸ್ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು ಮತ್ತು ವೀಕ್ಷಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> <string name="permgrouplab_storage" msgid="1971118770546336966">"ಸಂಗ್ರಹಣೆ"</string> <string name="permgroupdesc_storage" msgid="637758554581589203">"ಸಾಧನದಲ್ಲಿ ಫೋಟೋಗಳು, ಮಾಧ್ಯಮ ಮತ್ತು ಫೈಲ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು"</string> - <string name="permgrouprequest_storage" msgid="7885942926944299560">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಫೋಟೋಗಳು, ಮಾಧ್ಯಮ ಮತ್ತು ಫೈಲ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> + <string name="permgrouprequest_storage" msgid="7885942926944299560">"ಸಾಧನದಲ್ಲಿ ಫೋಟೋಗಳು, ಮಾಧ್ಯಮ, ಫೈಲ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> <string name="permgrouplab_microphone" msgid="171539900250043464">"ಮೈಕ್ರೋಫೋನ್"</string> <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string> <string name="permgrouprequest_microphone" msgid="9167492350681916038">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್ ಮಾಡಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 351f8ea8643e..6bbd2587d78d 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -27,5 +27,5 @@ <color name="notification_default_color_dark">#ddffffff</color> <!-- The background color of a notification card. --> - <color name="notification_material_background_color">@*android:color/material_grey_900</color> + <color name="notification_material_background_color">@color/black</color> </resources>
\ No newline at end of file diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml index 5e3675ac6c3e..c8d4d05a3507 100644 --- a/core/res/res/values-night/themes_device_defaults.xml +++ b/core/res/res/values-night/themes_device_defaults.xml @@ -51,4 +51,7 @@ easier. --> <!-- DeviceDefault theme for a window that should look like the Settings app. --> <style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault"/> + + <!-- Theme for the dialog shown when an app crashes or ANRs. --> + <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert" /> </resources> diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml index 4eb2ff3b5970..45cf0f0fedb3 100644 --- a/core/res/res/values-night/values.xml +++ b/core/res/res/values-night/values.xml @@ -26,7 +26,7 @@ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> <!-- QS panel background --> - <item name="colorBackgroundFloating">@color/material_grey_900</item> + <item name="colorBackgroundFloating">@color/black</item> <!-- volume background --> <item name="panelColorBackground">@color/material_grey_800</item> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index e31f161b5dd4..c14bce471bda 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -290,7 +290,7 @@ <string name="permgrouprequest_sms" msgid="7168124215838204719">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> отправлять и просматривать SMS?"</string> <string name="permgrouplab_storage" msgid="1971118770546336966">"Хранилище"</string> <string name="permgroupdesc_storage" msgid="637758554581589203">"доступ к фото, мультимедиа и файлам на вашем устройстве"</string> - <string name="permgrouprequest_storage" msgid="7885942926944299560">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> доступ к фото, мультимедиа и файлам на устройстве?"</string> + <string name="permgrouprequest_storage" msgid="7885942926944299560">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к фото, мультимедиа и файлам на устройстве?"</string> <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string> <string name="permgroupdesc_microphone" msgid="4988812113943554584">"записывать аудио"</string> <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> записывать аудио?"</string> @@ -305,7 +305,7 @@ <string name="permgrouprequest_phone" msgid="9166979577750581037">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> совершать звонки и управлять ими?"</string> <string name="permgrouplab_sensors" msgid="416037179223226722">"Нательные датчики"</string> <string name="permgroupdesc_sensors" msgid="7147968539346634043">"доступ к данным датчиков о состоянии организма"</string> - <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> доступ к данным датчиков о состоянии организма?"</string> + <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к данным датчиков о состоянии организма?"</string> <string name="permgrouplab_aural" msgid="965607064083134896">"Музыка"</string> <string name="permgroupdesc_aural" msgid="4870189506255958055">"доступ к музыке"</string> <string name="permgrouprequest_aural" msgid="6787926123071735620">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к музыке?"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 8b89a977faac..0f353f28b2f8 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -514,8 +514,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ඔබගේ ඡායාරූප එකතුව වෙනස් කිරීමට යෙදුමට ඉඩ දෙයි."</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"ඔබගේ මාධ්ය එකතුවෙන් ස්ථාන කියවන්න"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ඔබගේ මාධ්ය එකතුවෙන් ස්ථාන කියවීමට යෙදුමට ඉඩ දෙයි."</string> - <!-- no translation found for biometric_error_hw_unavailable (645781226537551036) --> - <skip /> + <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ජීවමිතික දෘඪාංග ලබා ගත නොහැකිය"</string> <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ඇඟිලි සලකුණ අඩ වශයෙන් අනාවරණය කර ගැනිණි. කරුණාකර නැවත උත්සාහ කරන්න."</string> <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ඇඟිලි සලකුණ පිරිසැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කරන්න."</string> <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ඇඟිලි සලකුණු සංවේදකය අපිරිසිදුයි. කරුණාකර පිරිසිදු කර නැවත උත්සාහ කරන්න."</string> @@ -523,8 +522,7 @@ <string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ඇඟිල්ල වඩා සෙමෙන් ගෙන යන ලදි. කරුණාකර නැවත උත්සාහ කරන්න."</string> <string-array name="fingerprint_acquired_vendor"> </string-array> - <!-- no translation found for biometric_not_recognized (5770511773560736082) --> - <skip /> + <string name="biometric_not_recognized" msgid="5770511773560736082">"හඳුනා නොගන්නා ලදී"</string> <string name="fingerprint_authenticated" msgid="5309333983002526448">"ඇඟිලි සලකුණ සත්යාපනය කරන ලදී"</string> <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැකිය."</string> <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ඇඟිලි සලකුණ ගබඩා කළ නොහැක. දැනට පවතින ඇඟිලි සලකුණක් ඉවත් කරන්න."</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index b175f75acea9..35592a696d68 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -284,7 +284,7 @@ <string name="permgrouprequest_sms" msgid="7168124215838204719">"மெசேஜ்களை அனுப்பவும், பார்க்கவும் <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை அனுமதிக்கவா?"</string> <string name="permgrouplab_storage" msgid="1971118770546336966">"சேமிப்பிடம்"</string> <string name="permgroupdesc_storage" msgid="637758554581589203">"உங்கள் சாதனத்தில் உள்ள படங்கள், மீடியா மற்றும் கோப்புகளை அணுக வேண்டும்"</string> - <string name="permgrouprequest_storage" msgid="7885942926944299560">"உங்கள் சாதனத்திலுள்ள படங்கள், மீடியா, ஃபைல்கள் ஆகியவற்றை அணுகுவதற்கு <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> பயன்பாட்டை அனுமதிக்கவா?"</string> + <string name="permgrouprequest_storage" msgid="7885942926944299560">"உங்கள் சாதனத்திலுள்ள படங்கள், மீடியா, ஃபைல்கள் ஆகியவற்றை அணுகுவதற்கு <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை அனுமதிக்கவா?"</string> <string name="permgrouplab_microphone" msgid="171539900250043464">"மைக்ரோஃபோன்"</string> <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ஒலிப் பதிவு செய்யலாம்"</string> <string name="permgrouprequest_microphone" msgid="9167492350681916038">"ஆடியோவைப் பதிவு செய்ய <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> பயன்பாட்டை அனுமதிக்கவா?"</string> diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml index 377982a409c9..0712cbcfc024 100644 --- a/core/res/res/values-television/themes.xml +++ b/core/res/res/values-television/themes.xml @@ -15,7 +15,7 @@ --> <resources> <style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> - <style name="Theme.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" /> + <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" /> <style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" /> <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> <style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" /> diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml index 04df1809878a..1be47baf4e7f 100644 --- a/core/res/res/values-watch/themes.xml +++ b/core/res/res/values-watch/themes.xml @@ -15,7 +15,7 @@ --> <resources> <!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. --> - <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert"> + <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert"> <item name="windowContentTransitions">false</item> <item name="windowActivityTransitions">false</item> <item name="windowCloseOnTouchOutside">false</item> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 0b04e0875718..966a5a53ffd8 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -272,9 +272,9 @@ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问您的通讯录吗?"</string> <string name="permgrouplab_location" msgid="7275582855722310164">"位置信息"</string> <string name="permgroupdesc_location" msgid="1346617465127855033">"获取此设备的位置信息"</string> - <string name="permgrouprequest_location" msgid="3788275734953323491">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>获取此设备的位置信息吗?"</string> + <string name="permgrouprequest_location" msgid="3788275734953323491">"要允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”获取此设备的位置信息吗?"</string> <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"只有当您使用该应用时,该应用才有权获取位置信息。"</string> - <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"一律允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>获取此设备的位置信息吗?"</string> + <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"一律允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”获取此设备的位置信息吗?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"即使您并未使用该应用,该应用也将始终有权获取位置信息。"</string> <string name="permgrouplab_calendar" msgid="5863508437783683902">"日历"</string> <string name="permgroupdesc_calendar" msgid="3889615280211184106">"访问您的日历"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index bd4ea2a6610e..bd36ed654915 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -514,7 +514,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"允許應用程式修改你的相片收藏。"</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"讀取你的媒體收藏的位置資訊"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"允許應用程式讀取你的媒體收藏的位置資訊。"</string> - <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"找不到生物特徵辨識硬體"</string> + <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"無法使用生物特徵辨識硬體"</string> <string name="fingerprint_acquired_partial" msgid="735082772341716043">"僅偵測到部分指紋,請再試一次。"</string> <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋,請再試一次。"</string> <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器有髒汙。請清潔感應器,然後再試一次。"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 3fed8a3a80d3..2de2b9eef5b8 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3573,6 +3573,11 @@ {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> <attr name="notificationTimeout" format="integer" /> + <!-- The minimum timeout in milliseconds that UI controls need to remain on the screen. + This setting can be changed at runtime by calling + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + <attr name="minimumUiTimeout" format="integer" /> <!-- Additional flags as specified in {@link android.accessibilityservice.AccessibilityServiceInfo}. This setting can be changed at runtime by calling diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index bf7e0682e91b..8bca211899d1 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -172,4 +172,7 @@ <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_HIDE_TOOLTIP}. --> <item type="id" name="accessibilityActionHideTooltip" /> + + <!-- Accessibility action to notify a window there is an outside touch. --> + <item type="id" name="accessibilityActionOutsideTouch" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index fa31dce5c1ec..c751af3d0c82 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2914,6 +2914,7 @@ <public-group type="attr" first-id="0x0101058d"> <!-- @hide For use by platform and tools only. Developers should not specify this value. --> <public name="usesNonSdkApi" /> + <public name="minimumUiTimeout" /> </public-group> <public-group type="style" first-id="0x010302e2"> diff --git a/core/res/res/values/styles_permission_controller.xml b/core/res/res/values/styles_permission_controller.xml index 339f9c71a57e..e6e0de3a6f30 100644 --- a/core/res/res/values/styles_permission_controller.xml +++ b/core/res/res/values/styles_permission_controller.xml @@ -15,8 +15,8 @@ limitations under the License. --> -<!-- styles for the permission grant dialog. --> <resources> + <!-- styles for the permission grant dialog. --> <style name="PermissionGrantDialog"> <item name="background">?attr/windowBackground</item> <item name="elevation">?attr/windowElevation</item> @@ -95,4 +95,37 @@ <item name="layout_marginEnd">16dp</item> <item name="layout_marginBottom">4dp</item> </style> + + <!-- styles for the permission review screen. --> + <style name="PermissionReviewDescription"> + <item name="layout_marginTop">20dp</item> + <item name="layout_marginStart">24dp</item> + <item name="layout_marginBottom">16dp</item> + <item name="layout_marginEnd">24dp</item> + </style> + + <style name="PermissionReviewTitleIcon"> + <item name="layout_marginTop">4dp</item> + <item name="layout_width">36dp</item> + <item name="layout_height">36dp</item> + <item name="scaleType">fitCenter</item> + </style> + + <style name="PermissionReviewTitleMessage" + parent="@style/TextAppearance.DeviceDefault"> + <item name="paddingStart">22dp</item> + <item name="textSize">20sp</item> + <item name="textColor">?attr/textColorPrimary</item> + </style> + + <style name="PermissionReviewSettings"> + <item name="layout_marginStart">8dp</item> + <item name="layout_marginEnd">8dp</item> + </style> + + <style name="PermissionReviewButtonBar"> + <item name="layout_marginStart">24dp</item> + <item name="layout_marginEnd">16dp</item> + <item name="layout_marginBottom">4dp</item> + </style> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 92cca72e055a..7b6e064c9cf9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -218,6 +218,7 @@ <java-symbol type="id" name="selection_end_handle" /> <java-symbol type="id" name="insertion_handle" /> <java-symbol type="id" name="accessibilityActionClickOnClickableSpan" /> + <java-symbol type="id" name="accessibilityActionOutsideTouch" /> <java-symbol type="id" name="camera" /> <java-symbol type="id" name="mic" /> <java-symbol type="id" name="overlay" /> @@ -2099,7 +2100,7 @@ <java-symbol type="string" name="vpn_lockdown_error" /> <java-symbol type="string" name="vpn_lockdown_config" /> <java-symbol type="string" name="wallpaper_binding_label" /> - <java-symbol type="style" name="Theme.Dialog.AppError" /> + <java-symbol type="style" name="Theme.DeviceDefault.Dialog.AppError" /> <java-symbol type="style" name="Theme.Leanback.Dialog.Alert" /> <java-symbol type="style" name="Theme.Toast" /> <java-symbol type="xml" name="storage_list" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 3937af5c933f..a7530cea5d73 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -869,13 +869,6 @@ please see themes_device_defaults.xml. <!-- System themes --> <eat-comment /> - <!-- Theme for the dialog shown when an app crashes or ANRs. --> - <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog.Alert"> - <item name="windowContentTransitions">false</item> - <item name="windowActivityTransitions">false</item> - <item name="windowCloseOnTouchOutside">false</item> - </style> - <!-- Special theme for the recent apps dialog, to allow customization with overlays. --> <style name="Theme.Dialog.RecentApplications" parent="Theme.DeviceDefault.Light.Dialog"> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 14e5082bdebb..92096abfcfab 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -719,6 +719,13 @@ easier. <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item> </style> + <!-- Theme for the dialog shown when an app crashes or ANRs. --> + <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog.Alert"> + <item name="windowContentTransitions">false</item> + <item name="windowActivityTransitions">false</item> + <item name="windowCloseOnTouchOutside">false</item> + </style> + <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar"> <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> diff --git a/core/res/res/values/themes_permission_controller.xml b/core/res/res/values/themes_permission_controller.xml index 3bc93cd03a54..369cee3d98f1 100644 --- a/core/res/res/values/themes_permission_controller.xml +++ b/core/res/res/values/themes_permission_controller.xml @@ -15,8 +15,8 @@ limitations under the License. --> -<!-- themes for the permission grant dialog. --> <resources> + <!-- themes for the permission grant dialog. --> <style name="Theme.DeviceDefault.PermissionGrantApp" parent="@style/Theme.DeviceDefault.Light.Panel"> <item name="windowIsFloating">false</item> @@ -32,4 +32,13 @@ <item name="checkboxStyle">@style/PermissionGrantCheckbox</item> <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item> </style> + + <!-- themes for the permission review dialog. --> + <style name="Theme.DeviceDefault.PermissionReviewApp" + parent="@style/Theme.DeviceDefault.Settings"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> + <item name="titleTextStyle">@style/PermissionReviewTitleMessage</item> + <item name="buttonBarStyle">@style/PermissionReviewButtonBar</item> + </style> </resources> diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java index 5ef30a828cf7..dcc51e121b32 100644 --- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java +++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java @@ -48,8 +48,8 @@ public class BrightnessConfigurationTest { @Test public void testSetCurveIsUnmodified() { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); - builder.setCurve(LUX_LEVELS, NITS_LEVELS); + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( + LUX_LEVELS, NITS_LEVELS); BrightnessConfiguration config = builder.build(); Pair<float[], float[]> curve = config.getCurve(); assertArrayEquals(LUX_LEVELS, curve.first, "lux"); @@ -58,45 +58,33 @@ public class BrightnessConfigurationTest { @Test(expected = IllegalArgumentException.class) public void testCurveMustHaveZeroLuxPoint() { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); lux[0] = 1f; - builder.setCurve(lux, NITS_LEVELS); - } - - @Test(expected = IllegalStateException.class) - public void testCurveMustBeSet() { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); - builder.build(); + new BrightnessConfiguration.Builder(lux, NITS_LEVELS); } @Test(expected = NullPointerException.class) public void testCurveMustNotHaveNullArrays() { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); - builder.setCurve(null, null); + new BrightnessConfiguration.Builder(null, null); } @Test(expected = IllegalArgumentException.class) public void testCurveMustNotHaveEmptyArrays() { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); - builder.setCurve(new float[0], new float[0]); + new BrightnessConfiguration.Builder(new float[0], new float[0]); } @Test public void testCurveMustNotHaveArraysOfDifferentLengths() { assertThrows(IllegalArgumentException.class, () -> { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length + 1); lux[lux.length - 1] = lux[lux.length - 2] + 1; - boolean exceptionThrown = false; - builder.setCurve(lux, NITS_LEVELS); + new BrightnessConfiguration.Builder(lux, NITS_LEVELS); }); assertThrows(IllegalArgumentException.class, () -> { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length + 1); nits[nits.length - 1] = nits[nits.length - 2] + 1; - builder.setCurve(LUX_LEVELS, nits); + new BrightnessConfiguration.Builder(LUX_LEVELS, nits); }); } @@ -105,23 +93,21 @@ public class BrightnessConfigurationTest { assertThrows(IllegalArgumentException.class, () -> { float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); lux[lux.length - 1] = Float.NaN; - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); - builder.setCurve(lux, NITS_LEVELS); + new BrightnessConfiguration.Builder(lux, NITS_LEVELS); }); assertThrows(IllegalArgumentException.class, () -> { float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length); nits[nits.length - 1] = Float.NaN; - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); - builder.setCurve(LUX_LEVELS, nits); + new BrightnessConfiguration.Builder(LUX_LEVELS, nits); }); } @Test public void testParceledConfigIsEquivalent() { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); - builder.setCurve(LUX_LEVELS, NITS_LEVELS); + BrightnessConfiguration.Builder builder = + new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS); BrightnessConfiguration config = builder.build(); Parcel p = Parcel.obtain(); p.writeParcelable(config, 0 /*flags*/); @@ -133,12 +119,11 @@ public class BrightnessConfigurationTest { @Test public void testEquals() { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); - builder.setCurve(LUX_LEVELS, NITS_LEVELS); + BrightnessConfiguration.Builder builder = + new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS); BrightnessConfiguration baseConfig = builder.build(); - builder = new BrightnessConfiguration.Builder(); - builder.setCurve(LUX_LEVELS, NITS_LEVELS); + builder = new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS); BrightnessConfiguration identicalConfig = builder.build(); assertEquals(baseConfig, identicalConfig); assertEquals("hashCodes must be equal for identical configs", @@ -146,15 +131,13 @@ public class BrightnessConfigurationTest { float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); lux[lux.length - 1] = lux[lux.length - 1] * 2; - builder = new BrightnessConfiguration.Builder(); - builder.setCurve(lux, NITS_LEVELS); + builder = new BrightnessConfiguration.Builder(lux, NITS_LEVELS); BrightnessConfiguration luxDifferConfig = builder.build(); assertNotEquals(baseConfig, luxDifferConfig); float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length); nits[nits.length - 1] = nits[nits.length - 1] * 2; - builder = new BrightnessConfiguration.Builder(); - builder.setCurve(LUX_LEVELS, nits); + builder = new BrightnessConfiguration.Builder(LUX_LEVELS, nits); BrightnessConfiguration nitsDifferConfig = builder.build(); assertNotEquals(baseConfig, nitsDifferConfig); } diff --git a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java index 920988be2eb3..9e445541ad3a 100644 --- a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java +++ b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java @@ -24,7 +24,6 @@ import android.os.Process; import android.provider.Settings; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; /** * Tests ActivityManager#setPersistentVrThread and ActivityManager#setVrThread's @@ -76,9 +75,11 @@ public class SetPersistentVrThreadTest extends ActivityInstrumentationTestCase2< } private void setPersistentVrModeEnabled(boolean enable) throws Throwable { - mVrManager.setPersistentVrModeEnabled(enable); - // Allow the system time to send out callbacks for persistent VR mode. - Thread.sleep(200); + if (mVrManager != null) { + mVrManager.setPersistentVrModeEnabled(enable); + // Allow the system time to send out callbacks for persistent VR mode. + Thread.sleep(200); + } } @SmallTest diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java index f731a4affb87..3064afaeaab1 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java @@ -71,11 +71,15 @@ public class InputMethodUtilsTest { private static final Locale LOCALE_FR = new Locale("fr"); private static final Locale LOCALE_FR_CA = new Locale("fr", "CA"); private static final Locale LOCALE_HI = new Locale("hi"); + private static final Locale LOCALE_JA = new Locale("ja"); private static final Locale LOCALE_JA_JP = new Locale("ja", "JP"); private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN"); private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW"); private static final Locale LOCALE_IN = new Locale("in"); private static final Locale LOCALE_ID = new Locale("id"); + private static final Locale LOCALE_TH = new Locale("ht"); + private static final Locale LOCALE_TH_TH = new Locale("ht", "TH"); + private static final Locale LOCALE_TH_TH_TH = new Locale("ht", "TH", "TH"); private static final String SUBTYPE_MODE_KEYBOARD = "keyboard"; private static final String SUBTYPE_MODE_VOICE = "voice"; private static final String SUBTYPE_MODE_HANDWRITING = "handwriting"; @@ -1083,6 +1087,122 @@ public class InputMethodUtilsTest { } @Test + public void testGetSuitableLocalesForSpellChecker() throws Exception { + { + final ArrayList<Locale> locales = + InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_US); + assertEquals(3, locales.size()); + assertEquals(LOCALE_EN_US, locales.get(0)); + assertEquals(LOCALE_EN_GB, locales.get(1)); + assertEquals(LOCALE_EN, locales.get(2)); + } + + { + final ArrayList<Locale> locales = + InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_GB); + assertEquals(3, locales.size()); + assertEquals(LOCALE_EN_GB, locales.get(0)); + assertEquals(LOCALE_EN_US, locales.get(1)); + assertEquals(LOCALE_EN, locales.get(2)); + } + + { + final ArrayList<Locale> locales = + InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN); + assertEquals(3, locales.size()); + assertEquals(LOCALE_EN, locales.get(0)); + assertEquals(LOCALE_EN_US, locales.get(1)); + assertEquals(LOCALE_EN_GB, locales.get(2)); + } + + { + final ArrayList<Locale> locales = + InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_IN); + assertEquals(4, locales.size()); + assertEquals(LOCALE_EN_IN, locales.get(0)); + assertEquals(LOCALE_EN_US, locales.get(1)); + assertEquals(LOCALE_EN_GB, locales.get(2)); + assertEquals(LOCALE_EN, locales.get(3)); + } + + { + final ArrayList<Locale> locales = + InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_JA_JP); + assertEquals(5, locales.size()); + assertEquals(LOCALE_JA_JP, locales.get(0)); + assertEquals(LOCALE_JA, locales.get(1)); + assertEquals(LOCALE_EN_US, locales.get(2)); + assertEquals(LOCALE_EN_GB, locales.get(3)); + assertEquals(Locale.ENGLISH, locales.get(4)); + } + + // Test 3-letter language code. + { + final ArrayList<Locale> locales = + InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_FIL_PH); + assertEquals(5, locales.size()); + assertEquals(LOCALE_FIL_PH, locales.get(0)); + assertEquals(LOCALE_FIL, locales.get(1)); + assertEquals(LOCALE_EN_US, locales.get(2)); + assertEquals(LOCALE_EN_GB, locales.get(3)); + assertEquals(Locale.ENGLISH, locales.get(4)); + } + + // Test variant. + { + final ArrayList<Locale> locales = + InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_TH_TH_TH); + assertEquals(6, locales.size()); + assertEquals(LOCALE_TH_TH_TH, locales.get(0)); + assertEquals(LOCALE_TH_TH, locales.get(1)); + assertEquals(LOCALE_TH, locales.get(2)); + assertEquals(LOCALE_EN_US, locales.get(3)); + assertEquals(LOCALE_EN_GB, locales.get(4)); + assertEquals(Locale.ENGLISH, locales.get(5)); + } + + // Test Locale extension. + { + final Locale localeWithoutVariant = LOCALE_JA_JP; + final Locale localeWithVariant = new Locale.Builder() + .setLocale(LOCALE_JA_JP) + .setExtension('x', "android") + .build(); + assertFalse(localeWithoutVariant.equals(localeWithVariant)); + + final ArrayList<Locale> locales = + InputMethodUtils.getSuitableLocalesForSpellChecker(localeWithVariant); + assertEquals(5, locales.size()); + assertEquals(LOCALE_JA_JP, locales.get(0)); + assertEquals(LOCALE_JA, locales.get(1)); + assertEquals(LOCALE_EN_US, locales.get(2)); + assertEquals(LOCALE_EN_GB, locales.get(3)); + assertEquals(Locale.ENGLISH, locales.get(4)); + } + } + + @Test + public void testConstructLocaleFromString() throws Exception { + assertEquals(new Locale("en"), InputMethodUtils.constructLocaleFromString("en")); + assertEquals(new Locale("en", "US"), InputMethodUtils.constructLocaleFromString("en_US")); + assertEquals(new Locale("en", "US", "POSIX"), + InputMethodUtils.constructLocaleFromString("en_US_POSIX")); + + // Special rewrite rule for "tl" for versions of Android earlier than Lollipop that did not + // support three letter language codes, and used "tl" (Tagalog) as the language string for + // "fil" (Filipino). + assertEquals(new Locale("fil"), InputMethodUtils.constructLocaleFromString("tl")); + assertEquals(new Locale("fil", "PH"), InputMethodUtils.constructLocaleFromString("tl_PH")); + assertEquals(new Locale("fil", "PH", "POSIX"), + InputMethodUtils.constructLocaleFromString("tl_PH_POSIX")); + + // So far rejecting an invalid/unexpected locale string is out of the scope of this method. + assertEquals(new Locale("a"), InputMethodUtils.constructLocaleFromString("a")); + assertEquals(new Locale("a b c"), InputMethodUtils.constructLocaleFromString("a b c")); + assertEquals(new Locale("en-US"), InputMethodUtils.constructLocaleFromString("en-US")); + } + + @Test public void testIsSoftInputModeStateVisibleAllowed() { // On pre-P devices, SOFT_INPUT_STATE_VISIBLE/SOFT_INPUT_STATE_ALWAYS_VISIBLE are always // allowed, regardless of the focused view state. diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java index 7d0e646181c0..549511a420eb 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java @@ -17,7 +17,6 @@ package com.android.internal.inputmethod; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import android.os.LocaleList; import android.support.test.filters.SmallTest; @@ -35,18 +34,6 @@ public class LocaleUtilsTest { private static final LocaleUtils.LocaleExtractor<Locale> sIdentityMapper = source -> source; - private static final Locale LOCALE_EN = new Locale("en"); - private static final Locale LOCALE_EN_US = new Locale("en", "US"); - private static final Locale LOCALE_EN_GB = new Locale("en", "GB"); - private static final Locale LOCALE_EN_IN = new Locale("en", "IN"); - private static final Locale LOCALE_FIL = new Locale("fil"); - private static final Locale LOCALE_FIL_PH = new Locale("fil", "PH"); - private static final Locale LOCALE_JA = new Locale("ja"); - private static final Locale LOCALE_JA_JP = new Locale("ja", "JP"); - private static final Locale LOCALE_TH = new Locale("ht"); - private static final Locale LOCALE_TH_TH = new Locale("ht", "TH"); - private static final Locale LOCALE_TH_TH_TH = new Locale("ht", "TH", "TH"); - @Test public void testFilterByLanguageEmptyLanguageList() throws Exception { final ArrayList<Locale> availableLocales = new ArrayList<>(); @@ -398,120 +385,4 @@ public class LocaleUtilsTest { assertEquals(availableLocales.get(3), dest.get(0)); } } - - @Test - public void testGetSuitableLocalesForSpellChecker() throws Exception { - { - final ArrayList<Locale> locales = - LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_US); - assertEquals(3, locales.size()); - assertEquals(LOCALE_EN_US, locales.get(0)); - assertEquals(LOCALE_EN_GB, locales.get(1)); - assertEquals(LOCALE_EN, locales.get(2)); - } - - { - final ArrayList<Locale> locales = - LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_GB); - assertEquals(3, locales.size()); - assertEquals(LOCALE_EN_GB, locales.get(0)); - assertEquals(LOCALE_EN_US, locales.get(1)); - assertEquals(LOCALE_EN, locales.get(2)); - } - - { - final ArrayList<Locale> locales = - LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_EN); - assertEquals(3, locales.size()); - assertEquals(LOCALE_EN, locales.get(0)); - assertEquals(LOCALE_EN_US, locales.get(1)); - assertEquals(LOCALE_EN_GB, locales.get(2)); - } - - { - final ArrayList<Locale> locales = - LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_IN); - assertEquals(4, locales.size()); - assertEquals(LOCALE_EN_IN, locales.get(0)); - assertEquals(LOCALE_EN_US, locales.get(1)); - assertEquals(LOCALE_EN_GB, locales.get(2)); - assertEquals(LOCALE_EN, locales.get(3)); - } - - { - final ArrayList<Locale> locales = - LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_JA_JP); - assertEquals(5, locales.size()); - assertEquals(LOCALE_JA_JP, locales.get(0)); - assertEquals(LOCALE_JA, locales.get(1)); - assertEquals(LOCALE_EN_US, locales.get(2)); - assertEquals(LOCALE_EN_GB, locales.get(3)); - assertEquals(Locale.ENGLISH, locales.get(4)); - } - - // Test 3-letter language code. - { - final ArrayList<Locale> locales = - LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_FIL_PH); - assertEquals(5, locales.size()); - assertEquals(LOCALE_FIL_PH, locales.get(0)); - assertEquals(LOCALE_FIL, locales.get(1)); - assertEquals(LOCALE_EN_US, locales.get(2)); - assertEquals(LOCALE_EN_GB, locales.get(3)); - assertEquals(Locale.ENGLISH, locales.get(4)); - } - - // Test variant. - { - final ArrayList<Locale> locales = - LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_TH_TH_TH); - assertEquals(6, locales.size()); - assertEquals(LOCALE_TH_TH_TH, locales.get(0)); - assertEquals(LOCALE_TH_TH, locales.get(1)); - assertEquals(LOCALE_TH, locales.get(2)); - assertEquals(LOCALE_EN_US, locales.get(3)); - assertEquals(LOCALE_EN_GB, locales.get(4)); - assertEquals(Locale.ENGLISH, locales.get(5)); - } - - // Test Locale extension. - { - final Locale localeWithoutVariant = LOCALE_JA_JP; - final Locale localeWithVariant = new Locale.Builder() - .setLocale(LOCALE_JA_JP) - .setExtension('x', "android") - .build(); - assertFalse(localeWithoutVariant.equals(localeWithVariant)); - - final ArrayList<Locale> locales = - LocaleUtils.getSuitableLocalesForSpellChecker(localeWithVariant); - assertEquals(5, locales.size()); - assertEquals(LOCALE_JA_JP, locales.get(0)); - assertEquals(LOCALE_JA, locales.get(1)); - assertEquals(LOCALE_EN_US, locales.get(2)); - assertEquals(LOCALE_EN_GB, locales.get(3)); - assertEquals(Locale.ENGLISH, locales.get(4)); - } - } - - @Test - public void testConstructLocaleFromString() throws Exception { - assertEquals(new Locale("en"), LocaleUtils.constructLocaleFromString("en")); - assertEquals(new Locale("en", "US"), LocaleUtils.constructLocaleFromString("en_US")); - assertEquals(new Locale("en", "US", "POSIX"), - LocaleUtils.constructLocaleFromString("en_US_POSIX")); - - // Special rewrite rule for "tl" for versions of Android earlier than Lollipop that did not - // support three letter language codes, and used "tl" (Tagalog) as the language string for - // "fil" (Filipino). - assertEquals(new Locale("fil"), LocaleUtils.constructLocaleFromString("tl")); - assertEquals(new Locale("fil", "PH"), LocaleUtils.constructLocaleFromString("tl_PH")); - assertEquals(new Locale("fil", "PH", "POSIX"), - LocaleUtils.constructLocaleFromString("tl_PH_POSIX")); - - // So far rejecting an invalid/unexpected locale string is out of the scope of this method. - assertEquals(new Locale("a"), LocaleUtils.constructLocaleFromString("a")); - assertEquals(new Locale("a b c"), LocaleUtils.constructLocaleFromString("a b c")); - assertEquals(new Locale("en-US"), LocaleUtils.constructLocaleFromString("en-US")); - } } diff --git a/core/tests/webkit/Android.mk b/core/tests/webkit/Android.mk deleted file mode 100644 index 45f6957c10e7..000000000000 --- a/core/tests/webkit/Android.mk +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) 2017 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. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) - - -# Include all test java files. -LOCAL_SRC_FILES := \ - $(call all-java-files-under, unit_tests_src) - -LOCAL_JAVA_LIBRARIES := android.test.runner - -LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-test - -LOCAL_PACKAGE_NAME := WebViewLoadingTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform - -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_REQUIRED_MODULES := \ - WebViewLoadingOnDiskTestApk \ - WebViewLoadingFromApkTestApk - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/core/tests/webkit/AndroidManifest.xml b/core/tests/webkit/AndroidManifest.xml deleted file mode 100644 index 42accdf66891..000000000000 --- a/core/tests/webkit/AndroidManifest.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.webkit.tests" - android:sharedUserId="android.uid.system"> - - <application> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation - android:name="android.support.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.webkit.tests" - android:label="Frameworks WebView Loader Tests" /> - -</manifest> diff --git a/core/tests/webkit/AndroidTest.xml b/core/tests/webkit/AndroidTest.xml deleted file mode 100644 index 4c50b7df3368..000000000000 --- a/core/tests/webkit/AndroidTest.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 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. ---> -<configuration description="Runs Frameworks WebView Loading Tests."> - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="apct-instrumentation" /> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="test-file-name" value="WebViewLoadingTests.apk" /> - <option name="test-file-name" value="WebViewLoadingOnDiskTestApk.apk" /> - <option name="test-file-name" value="WebViewLoadingFromApkTestApk.apk" /> - <option name="cleanup-apks" value="true" /> - <option name="alt-dir" value="out" /> - </target_preparer> - - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="com.android.webkit.tests" /> - </test> -</configuration> diff --git a/core/tests/webkit/OWNERS b/core/tests/webkit/OWNERS deleted file mode 100644 index 00e540a46ab2..000000000000 --- a/core/tests/webkit/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -changwan@google.com -tobiasjs@google.com -torne@google.com diff --git a/core/tests/webkit/apk_with_native_libs/Android.mk b/core/tests/webkit/apk_with_native_libs/Android.mk deleted file mode 100644 index e18a7e0df175..000000000000 --- a/core/tests/webkit/apk_with_native_libs/Android.mk +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) 2017 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. - -LOCAL_PATH := $(call my-dir) -MY_PATH := $(LOCAL_PATH) - -# Set shared variables -MY_MODULE_TAGS := optional -MY_JNI_SHARED_LIBRARIES := libwebviewtest_jni -MY_MODULE_PATH := $(TARGET_OUT_DATA_APPS) -MY_SRC_FILES := $(call all-java-files-under, src) -MY_CFLAGS := -Wall -Werror -MY_SDK_VERSION := system_current -MY_PROGUARD_ENABLED := disabled -MY_MULTILIB := both - -# Recurse down the file tree. -include $(call all-subdir-makefiles) - - - -# Builds an apk containing native libraries that will be unzipped on the device. -include $(CLEAR_VARS) - -LOCAL_PATH := $(MY_PATH) -LOCAL_PACKAGE_NAME := WebViewLoadingOnDiskTestApk -LOCAL_MANIFEST_FILE := ondisk/AndroidManifest.xml - -LOCAL_MODULE_TAGS := $(MY_MODULE_TAGS) -LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES) -LOCAL_MODULE_PATH := $(MY_MODULE_PATH) -LOCAL_SRC_FILES := $(MY_SRC_FILES) -LOCAL_CFLAGS := $(MY_CFLAGS) -LOCAL_SDK_VERSION := $(MY_SDK_VERSION) -LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED) -LOCAL_MULTILIB := $(MY_MULTILIB) -LOCAL_COMPATIBILITY_SUITE := device-tests - -include $(BUILD_PACKAGE) - - -# Builds an apk containing uncompressed native libraries that have to be -# accessed through the APK itself on the device. -include $(CLEAR_VARS) - -LOCAL_PATH := $(MY_PATH) -LOCAL_PACKAGE_NAME := WebViewLoadingFromApkTestApk -LOCAL_MANIFEST_FILE := inapk/AndroidManifest.xml - -LOCAL_MODULE_TAGS := $(MY_MODULE_TAGS) -LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES) -LOCAL_MODULE_PATH := $(MY_MODULE_PATH) -LOCAL_SRC_FILES := $(MY_SRC_FILES) -LOCAL_CFLAGS := $(MY_CFLAGS) -LOCAL_SDK_VERSION := $(MY_SDK_VERSION) -LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED) -LOCAL_MULTILIB := $(MY_MULTILIB) -LOCAL_COMPATIBILITY_SUITE := device-tests - -include $(BUILD_PACKAGE) diff --git a/core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml b/core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml deleted file mode 100644 index 868b2388d135..000000000000 --- a/core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.webviewloading_test_from_apk" - android:versionCode="1" - android:versionName="0.0.0.1"> - - <application android:label="WebView Loading Test APK" - android:multiArch="true" - android:extractNativeLibs="false"> - <meta-data android:name="com.android.webview.WebViewLibrary" - android:value="libwebviewtest_jni.so" /> - </application> -</manifest> diff --git a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp deleted file mode 100644 index 0ced4ee12ac5..000000000000 --- a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2017 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 <jni.h> - -jint JNI_OnLoad(JavaVM * /*vm*/, void * /*reserved*/) { - return JNI_VERSION_1_4; -} diff --git a/core/tests/webkit/apk_with_native_libs/ondisk/AndroidManifest.xml b/core/tests/webkit/apk_with_native_libs/ondisk/AndroidManifest.xml deleted file mode 100644 index ffffeb8e1630..000000000000 --- a/core/tests/webkit/apk_with_native_libs/ondisk/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.webviewloading_test_on_disk" - android:versionCode="1" - android:versionName="0.0.0.1"> - - <application android:label="WebView Loading Test APK" - android:multiArch="true"> - <meta-data android:name="com.android.webview.WebViewLibrary" - android:value="libwebviewtest_jni.so" /> - </application> -</manifest> diff --git a/core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java b/core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java deleted file mode 100644 index e2f2d37a4d68..000000000000 --- a/core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2017 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.webkit; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.util.Log; - -import android.support.test.filters.MediumTest; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; -import android.support.test.InstrumentationRegistry; - -import java.io.File; -import java.io.IOException; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Unit tests for {@link WebViewLibraryLoader}. - * Use the following command to run these tests: - * make WebViewLoadingTests \ - * && adb install -r -d \ - * ${ANDROID_PRODUCT_OUT}/data/app/WebViewLoadingTests/WebViewLoadingTests.apk \ - * && adb shell am instrument -e class 'android.webkit.WebViewLibraryLoaderTest' -w \ - * 'com.android.webkit.tests/android.support.test.runner.AndroidJUnitRunner' - */ -@RunWith(AndroidJUnit4.class) -public final class WebViewLibraryLoaderTest { - private static final String WEBVIEW_LIBS_ON_DISK_TEST_APK = - "com.android.webviewloading_test_on_disk"; - private static final String WEBVIEW_LIBS_IN_APK_TEST_APK = - "com.android.webviewloading_test_from_apk"; - private static final String WEBVIEW_LOADING_TEST_NATIVE_LIB = "libwebviewtest_jni.so"; - - private PackageInfo webviewOnDiskPackageInfo; - private PackageInfo webviewFromApkPackageInfo; - - @Before public void setUp() throws PackageManager.NameNotFoundException { - PackageManager pm = InstrumentationRegistry.getContext().getPackageManager(); - webviewOnDiskPackageInfo = - pm.getPackageInfo(WEBVIEW_LIBS_ON_DISK_TEST_APK, PackageManager.GET_META_DATA); - webviewFromApkPackageInfo = - pm.getPackageInfo(WEBVIEW_LIBS_IN_APK_TEST_APK, PackageManager.GET_META_DATA); - } - - private static boolean is64BitDevice() { - return Build.SUPPORTED_64_BIT_ABIS.length > 0; - } - - // We test the getWebViewNativeLibraryDirectory method here because it handled several different - // cases/combinations and it seems unnecessary to create one test-apk for each such combination - // and arch. - - /** - * Ensure we fetch the correct native library directories in the multi-arch case where - * the primary ABI is 64-bit. - */ - @SmallTest - @Test public void testGetWebViewLibDirMultiArchPrimary64bit() { - final String nativeLib = "nativeLib"; - final String secondaryNativeLib = "secondaryNativeLib"; - PackageInfo packageInfo = new PackageInfo(); - ApplicationInfo ai = new ApplicationInfoBuilder(). - // See VMRuntime.ABI_TO_INSTRUCTION_SET_MAP - setPrimaryCpuAbi("arm64-v8a"). - setNativeLibraryDir(nativeLib). - setSecondaryCpuAbi("armeabi"). - setSecondaryNativeLibraryDir(secondaryNativeLib). - create(); - packageInfo.applicationInfo = ai; - String actual32Lib = - WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, false /* is64bit */); - String actual64Lib = - WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, true /* is64bit */); - assertEquals(nativeLib, actual64Lib); - assertEquals(secondaryNativeLib, actual32Lib); - } - - /** - * Ensure we fetch the correct native library directory in the 64-bit single-arch case. - */ - @SmallTest - @Test public void testGetWebViewLibDirSingleArch64bit() { - final String nativeLib = "nativeLib"; - PackageInfo packageInfo = new PackageInfo(); - ApplicationInfo ai = new ApplicationInfoBuilder(). - // See VMRuntime.ABI_TO_INSTRUCTION_SET_MAP - setPrimaryCpuAbi("arm64-v8a"). - setNativeLibraryDir(nativeLib). - create(); - packageInfo.applicationInfo = ai; - String actual64Lib = - WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, true /* is64bit */); - assertEquals(nativeLib, actual64Lib); - } - - /** - * Ensure we fetch the correct native library directory in the 32-bit single-arch case. - */ - @SmallTest - @Test public void testGetWebViewLibDirSingleArch32bit() { - final String nativeLib = "nativeLib"; - PackageInfo packageInfo = new PackageInfo(); - ApplicationInfo ai = new ApplicationInfoBuilder(). - // See VMRuntime.ABI_TO_INSTRUCTION_SET_MAP - setPrimaryCpuAbi("armeabi-v7a"). - setNativeLibraryDir(nativeLib). - create(); - packageInfo.applicationInfo = ai; - String actual32Lib = - WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, false /* is64bit */); - assertEquals(nativeLib, actual32Lib); - } - - /** - * Ensure we fetch the correct 32-bit library path from an APK with 32-bit and 64-bit - * libraries unzipped onto disk. - */ - @MediumTest - @Test public void testGetWebViewLibraryPathOnDisk32Bit() - throws WebViewFactory.MissingWebViewPackageException { - WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib = - WebViewLibraryLoader.getWebViewNativeLibrary( - webviewOnDiskPackageInfo, false /* is64bit */); - String expectedLibaryDirectory = is64BitDevice() ? - webviewOnDiskPackageInfo.applicationInfo.secondaryNativeLibraryDir : - webviewOnDiskPackageInfo.applicationInfo.nativeLibraryDir; - String lib32Path = expectedLibaryDirectory + "/" + WEBVIEW_LOADING_TEST_NATIVE_LIB; - assertEquals("Fetched incorrect 32-bit path from WebView library.", - lib32Path, actualNativeLib.path); - } - - /** - * Ensure we fetch the correct 64-bit library path from an APK with 32-bit and 64-bit - * libraries unzipped onto disk. - */ - @MediumTest - @Test public void testGetWebViewLibraryPathOnDisk64Bit() - throws WebViewFactory.MissingWebViewPackageException { - // A 32-bit device will not unpack 64-bit libraries. - if (!is64BitDevice()) return; - - WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib = - WebViewLibraryLoader.getWebViewNativeLibrary( - webviewOnDiskPackageInfo, true /* is64bit */); - String lib64Path = webviewOnDiskPackageInfo.applicationInfo.nativeLibraryDir - + "/" + WEBVIEW_LOADING_TEST_NATIVE_LIB; - assertEquals("Fetched incorrect 64-bit path from WebView library.", - lib64Path, actualNativeLib.path); - } - - /** - * Check the size of the 32-bit library fetched from an APK with both 32-bit and 64-bit - * libraries unzipped onto disk. - */ - @MediumTest - @Test public void testGetWebView32BitLibrarySizeOnDiskIsNonZero() - throws WebViewFactory.MissingWebViewPackageException { - WebViewLibraryLoader.WebViewNativeLibrary actual32BitNativeLib = - WebViewLibraryLoader.getWebViewNativeLibrary( - webviewOnDiskPackageInfo, false /* is64bit */); - assertTrue(actual32BitNativeLib.size > 0); - } - - /** - * Check the size of the 64-bit library fetched from an APK with both 32-bit and 64-bit - * libraries unzipped onto disk. - */ - @MediumTest - @Test public void testGetWebView64BitLibrarySizeOnDiskIsNonZero() - throws WebViewFactory.MissingWebViewPackageException { - // A 32-bit device will not unpack 64-bit libraries. - if (!is64BitDevice()) return; - WebViewLibraryLoader.WebViewNativeLibrary actual64BitNativeLib = - WebViewLibraryLoader.getWebViewNativeLibrary( - webviewOnDiskPackageInfo, true /* is64bit */); - assertTrue(actual64BitNativeLib.size > 0); - } - - /** - * Ensure we fetch the correct 32-bit library path from an APK with both 32-bit and 64-bit - * libraries stored uncompressed in the APK. - */ - @MediumTest - @Test public void testGetWebView32BitLibraryPathFromApk() - throws WebViewFactory.MissingWebViewPackageException, IOException { - WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib = - WebViewLibraryLoader.getWebViewNativeLibrary( - webviewFromApkPackageInfo, false /* is64bit */); - // The device might have ignored the app's request to not extract native libs, so first - // check whether the library paths match those of extracted libraries. - String expectedLibaryDirectory = is64BitDevice() ? - webviewFromApkPackageInfo.applicationInfo.secondaryNativeLibraryDir : - webviewFromApkPackageInfo.applicationInfo.nativeLibraryDir; - String lib32Path = expectedLibaryDirectory + "/" + WEBVIEW_LOADING_TEST_NATIVE_LIB; - if (lib32Path.equals(actualNativeLib.path)) { - // If the libraries were extracted to disk, ensure that they're actually there. - assertTrue("The given WebView library doesn't exist.", - new File(actualNativeLib.path).exists()); - } else { // The libraries were not extracted to disk. - assertIsValidZipEntryPath(actualNativeLib.path, - webviewFromApkPackageInfo.applicationInfo.sourceDir); - } - } - - /** - * Ensure we fetch the correct 32-bit library path from an APK with both 32-bit and 64-bit - * libraries stored uncompressed in the APK. - */ - @MediumTest - @Test public void testGetWebView64BitLibraryPathFromApk() - throws WebViewFactory.MissingWebViewPackageException, IOException { - // A 32-bit device will not unpack 64-bit libraries. - if (!is64BitDevice()) return; - - WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib = - WebViewLibraryLoader.getWebViewNativeLibrary( - webviewFromApkPackageInfo, true /* is64bit */); - assertIsValidZipEntryPath(actualNativeLib.path, - webviewFromApkPackageInfo.applicationInfo.sourceDir); - } - - private static void assertIsValidZipEntryPath(String path, String zipFilePath) - throws IOException { - assertTrue("The path to a zip entry must start with the path to the zip file itself." - + "Expected zip path: " + zipFilePath + ", actual zip entry: " + path, - path.startsWith(zipFilePath + "!/")); - String[] pathSplit = path.split("!/"); - assertEquals("A zip file path should have two parts, the zip path, and the zip entry path.", - 2, pathSplit.length); - ZipFile zipFile = new ZipFile(pathSplit[0]); - assertNotNull("Path doesn't point to a valid zip entry: " + path, - zipFile.getEntry(pathSplit[1])); - } - - - /** - * Check the size of the 32-bit library fetched from an APK with both 32-bit and 64-bit - * libraries stored uncompressed in the APK. - */ - @MediumTest - @Test public void testGetWebView32BitLibrarySizeFromApkIsNonZero() - throws WebViewFactory.MissingWebViewPackageException { - WebViewLibraryLoader.WebViewNativeLibrary actual32BitNativeLib = - WebViewLibraryLoader.getWebViewNativeLibrary( - webviewFromApkPackageInfo, false /* is64bit */); - assertTrue(actual32BitNativeLib.size > 0); - } - - /** - * Check the size of the 64-bit library fetched from an APK with both 32-bit and 64-bit - * libraries stored uncompressed in the APK. - */ - @MediumTest - @Test public void testGetWebView64BitLibrarySizeFromApkIsNonZero() - throws WebViewFactory.MissingWebViewPackageException { - // A 32-bit device will not unpack 64-bit libraries. - if (!is64BitDevice()) return; - - WebViewLibraryLoader.WebViewNativeLibrary actual64BitNativeLib = - WebViewLibraryLoader.getWebViewNativeLibrary( - webviewFromApkPackageInfo, true /* is64bit */); - assertTrue(actual64BitNativeLib.size > 0); - } - - private static class ApplicationInfoBuilder { - ApplicationInfo ai; - - public ApplicationInfoBuilder setPrimaryCpuAbi(String primaryCpuAbi) { - ai.primaryCpuAbi = primaryCpuAbi; - return this; - } - - public ApplicationInfoBuilder setSecondaryCpuAbi(String secondaryCpuAbi) { - ai.secondaryCpuAbi = secondaryCpuAbi; - return this; - } - - public ApplicationInfoBuilder setNativeLibraryDir(String nativeLibraryDir) { - ai.nativeLibraryDir = nativeLibraryDir; - return this; - } - - public ApplicationInfoBuilder setSecondaryNativeLibraryDir( - String secondaryNativeLibraryDir) { - ai.secondaryNativeLibraryDir = secondaryNativeLibraryDir; - return this; - } - - public ApplicationInfoBuilder setMetaData(Bundle metaData) { - ai.metaData = metaData; - return this; - } - - public ApplicationInfoBuilder() { - ai = new android.content.pm.ApplicationInfo(); - } - - public ApplicationInfo create() { - return ai; - } - } -} diff --git a/libs/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h index d84a207697e9..949c9445b3e8 100644 --- a/libs/androidfw/include/androidfw/ByteBucketArray.h +++ b/libs/androidfw/include/androidfw/ByteBucketArray.h @@ -60,7 +60,7 @@ class ByteBucketArray { } T& editItemAt(size_t index) { - CHECK(index < size()) << "ByteBucketArray.getOrCreate(index=" << index + CHECK(index < size()) << "ByteBucketArray.editItemAt(index=" << index << ") with size=" << size(); uint8_t bucket_index = static_cast<uint8_t>(index) >> 4; diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index a2bf549dd475..d4c84b5b6c77 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -1014,7 +1014,11 @@ android_media_MediaPlayer2_invoke(JNIEnv *env, jobject thiz, jbyteArray requestD PlayerMessage response; request.ParseFromArray(pData, pDataLen); - media_player->invoke(request, &response); + process_media_player_call( env, thiz, media_player->invoke(request, &response), + "java.lang.RuntimeException", NULL ); + if (env->ExceptionCheck()) { + return NULL; + } int size = response.ByteSize(); jbyte* temp = new jbyte[size]; diff --git a/native/webview/loader/loader.cpp b/native/webview/loader/loader.cpp index adb371dde0fc..fee2a259cb54 100644 --- a/native/webview/loader/loader.cpp +++ b/native/webview/loader/loader.cpp @@ -64,7 +64,8 @@ jboolean DoReserveAddressSpace(jlong size) { return JNI_TRUE; } -jboolean DoCreateRelroFile(const char* lib, const char* relro) { +jboolean DoCreateRelroFile(JNIEnv* env, const char* lib, const char* relro, + jobject clazzLoader) { // Try to unlink the old file, since if this is being called, the old one is // obsolete. if (unlink(relro) != 0 && errno != ENOENT) { @@ -82,11 +83,19 @@ jboolean DoCreateRelroFile(const char* lib, const char* relro) { ALOGE("Failed to create temporary file %s: %s", relro_tmp, strerror(errno)); return JNI_FALSE; } + android_namespace_t* ns = + android::FindNamespaceByClassLoader(env, clazzLoader); + if (ns == NULL) { + ALOGE("Failed to find classloader namespace"); + return JNI_FALSE; + } android_dlextinfo extinfo; - extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO; + extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO | + ANDROID_DLEXT_USE_NAMESPACE; extinfo.reserved_addr = gReservedAddress; extinfo.reserved_size = gReservedSize; extinfo.relro_fd = tmp_fd; + extinfo.library_namespace = ns; void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo); int close_result = close(tmp_fd); if (handle == NULL) { @@ -143,13 +152,14 @@ jboolean ReserveAddressSpace(JNIEnv*, jclass, jlong size) { return DoReserveAddressSpace(size); } -jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro) { +jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro, + jobject clazzLoader) { jboolean ret = JNI_FALSE; const char* lib_utf8 = env->GetStringUTFChars(lib, NULL); if (lib_utf8 != NULL) { const char* relro_utf8 = env->GetStringUTFChars(relro, NULL); if (relro_utf8 != NULL) { - ret = DoCreateRelroFile(lib_utf8, relro_utf8); + ret = DoCreateRelroFile(env, lib_utf8, relro_utf8, clazzLoader); env->ReleaseStringUTFChars(relro, relro_utf8); } env->ReleaseStringUTFChars(lib, lib_utf8); @@ -179,7 +189,7 @@ const JNINativeMethod kJniMethods[] = { { "nativeReserveAddressSpace", "(J)Z", reinterpret_cast<void*>(ReserveAddressSpace) }, { "nativeCreateRelroFile", - "(Ljava/lang/String;Ljava/lang/String;)Z", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)Z", reinterpret_cast<void*>(CreateRelroFile) }, { "nativeLoadWithRelroFile", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)I", diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java index 6473f0d2778a..0467bff46e19 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java @@ -177,7 +177,7 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV } } textView.getViewTreeObserver().removeOnPreDrawListener(this); - return false; + return true; } }); textView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index ab2892f125d6..d29bd65ac733 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -135,7 +135,7 @@ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"테더링"</string> <string name="tether_settings_title_all" msgid="8356136101061143841">"테더링 및 휴대용 핫스팟"</string> <string name="managed_user_title" msgid="8109605045406748842">"모든 직장 앱"</string> - <string name="user_guest" msgid="8475274842845401871">"손님"</string> + <string name="user_guest" msgid="8475274842845401871">"게스트"</string> <string name="unknown" msgid="1592123443519355854">"알 수 없음"</string> <string name="running_process_item_user_label" msgid="3129887865552025943">"사용자: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="313159469856372621">"일부 기본값이 설정됨"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java index 0c4e02b59bec..e13e566ec901 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java @@ -33,7 +33,6 @@ import java.util.List; final class A2dpSinkProfile implements LocalBluetoothProfile { private static final String TAG = "A2dpSinkProfile"; - private static boolean V = true; private BluetoothA2dpSink mService; private boolean mIsProfileReady; @@ -56,7 +55,7 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { implements BluetoothProfile.ServiceListener { public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (V) Log.d(TAG,"Bluetooth service connected"); + Log.d(TAG, "Bluetooth service connected"); mService = (BluetoothA2dpSink) proxy; // We just bound to the service, so refresh the UI for any connected A2DP devices. List<BluetoothDevice> deviceList = mService.getConnectedDevices(); @@ -75,7 +74,7 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { } public void onServiceDisconnected(int profile) { - if (V) Log.d(TAG,"Bluetooth service disconnected"); + Log.d(TAG, "Bluetooth service disconnected"); mIsProfileReady=false; } } @@ -106,7 +105,9 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { } public List<BluetoothDevice> getConnectedDevices() { - if (mService == null) return new ArrayList<BluetoothDevice>(0); + if (mService == null) { + return new ArrayList<BluetoothDevice>(0); + } return mService.getDevicesMatchingConnectionStates( new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING, @@ -114,24 +115,18 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { } public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - List<BluetoothDevice> srcs = getConnectedDevices(); - if (srcs != null) { - for (BluetoothDevice src : srcs) { - if (src.equals(device)) { - // Connect to same device, Ignore it - Log.d(TAG,"Ignoring Connect"); - return true; - } - } + if (mService == null) { + return false; } return mService.connect(device); } public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; + if (mService == null) { + return false; + } // Downgrade priority as user is disconnecting the headset. - if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){ + if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { mService.setPriority(device, BluetoothProfile.PRIORITY_ON); } return mService.disconnect(device); @@ -145,17 +140,23 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { } public boolean isPreferred(BluetoothDevice device) { - if (mService == null) return false; + if (mService == null) { + return false; + } return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; } public int getPreferred(BluetoothDevice device) { - if (mService == null) return BluetoothProfile.PRIORITY_OFF; + if (mService == null) { + return BluetoothProfile.PRIORITY_OFF; + } return mService.getPriority(device); } public void setPreferred(BluetoothDevice device, boolean preferred) { - if (mService == null) return; + if (mService == null) { + return; + } if (preferred) { if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) { mService.setPriority(device, BluetoothProfile.PRIORITY_ON); @@ -166,7 +167,9 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { } boolean isA2dpPlaying() { - if (mService == null) return false; + if (mService == null) { + return false; + } List<BluetoothDevice> srcs = mService.getConnectedDevices(); if (!srcs.isEmpty()) { if (mService.isA2dpPlaying(srcs.get(0))) { @@ -208,7 +211,7 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { } protected void finalize() { - if (V) Log.d(TAG, "finalize()"); + Log.d(TAG, "finalize()"); if (mService != null) { try { BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP_SINK, diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 649900bc97bd..b9f7323b1a9e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -78,12 +78,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject"; - /** - * When we connect to multiple profiles, we only want to display a single - * error even if they all fail. This tracks that state. - */ - private boolean mIsConnectingErrorPossible; - public long getHiSyncId() { return mHiSyncId; } @@ -230,9 +224,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return; } - // Reset the only-show-one-error-dialog tracking variable - mIsConnectingErrorPossible = true; - int preferredProfiles = 0; for (LocalBluetoothProfile profile : mProfiles) { if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) { @@ -253,8 +244,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (!ensurePaired()) { return; } - // Reset the only-show-one-error-dialog tracking variable - mIsConnectingErrorPossible = true; for (LocalBluetoothProfile profile : mProfiles) { if (profile.isAutoConnectable()) { @@ -271,8 +260,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> */ public void connectProfile(LocalBluetoothProfile profile) { mConnectAttempted = SystemClock.elapsedRealtime(); - // Reset the only-show-one-error-dialog tracking variable - mIsConnectingErrorPossible = true; connectInt(profile); // Refresh the UI based on profile.connect() call refresh(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java new file mode 100644 index 000000000000..274fff83ea8a --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java @@ -0,0 +1,91 @@ +/* + * 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.settingslib.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothA2dpSink; +import android.bluetooth.BluetoothProfile; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadow.api.Shadow; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) +public class A2dpSinkProfileTest { + + @Mock + private CachedBluetoothDeviceManager mDeviceManager; + @Mock + private LocalBluetoothProfileManager mProfileManager; + @Mock + private BluetoothA2dpSink mService; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; + private BluetoothProfile.ServiceListener mServiceListener; + private A2dpSinkProfile mProfile; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + mProfile = new A2dpSinkProfile(RuntimeEnvironment.application, + mDeviceManager, mProfileManager); + mServiceListener = mShadowBluetoothAdapter.getServiceListener(); + mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, mService); + } + + @Test + public void connect_shouldConnectBluetoothA2dpSink() { + mProfile.connect(mBluetoothDevice); + verify(mService).connect(mBluetoothDevice); + } + + @Test + public void disconnect_shouldDisconnectBluetoothA2dpSink() { + mProfile.disconnect(mBluetoothDevice); + verify(mService).disconnect(mBluetoothDevice); + } + + @Test + public void getConnectionStatus_shouldReturnConnectionState() { + when(mService.getConnectionState(mBluetoothDevice)). + thenReturn(BluetoothProfile.STATE_CONNECTED); + assertThat(mProfile.getConnectionStatus(mBluetoothDevice)). + isEqualTo(BluetoothProfile.STATE_CONNECTED); + } +}
\ No newline at end of file diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index c6ea4807c2a9..bd21b8353136 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1617,6 +1617,12 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, SecureSettingsProto.Accessibility.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES); + dumpSetting(s, p, + Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, + SecureSettingsProto.Accessibility.MINIMUM_UI_TIMEOUT_ENABLED); + dumpSetting(s, p, + Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, + SecureSettingsProto.Accessibility.MINIMUM_UI_TIMEOUT_MS); p.end(accessibilityToken); dumpSetting(s, p, diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 4fc190d43056..ec35b3df73c9 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -16,6 +16,8 @@ package com.android.shell; +import static android.content.pm.PackageManager.FEATURE_LEANBACK; +import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static com.android.shell.BugreportPrefs.STATE_HIDE; @@ -235,6 +237,7 @@ public class BugreportProgressService extends Service { private static final Bundle sNotificationBundle = new Bundle(); private boolean mIsWatch; + private boolean mIsTv; private int mLastProgressPercent; @@ -255,6 +258,9 @@ public class BugreportProgressService extends Service { final Configuration conf = mContext.getResources().getConfiguration(); mIsWatch = (conf.uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH; + PackageManager packageManager = getPackageManager(); + mIsTv = packageManager.hasSystemFeature(FEATURE_LEANBACK) + || packageManager.hasSystemFeature(FEATURE_TELEVISION); NotificationManager nm = NotificationManager.from(mContext); nm.createNotificationChannel( new NotificationChannel(NOTIFICATION_CHANNEL_ID, @@ -500,8 +506,8 @@ public class BugreportProgressService extends Service { .setProgress(info.max, info.progress, false) .setOngoing(true); - // Wear bugreport doesn't need the bug info dialog, screenshot and cancel action. - if (!mIsWatch) { + // Wear and ATV bugreport doesn't need the bug info dialog, screenshot and cancel action. + if (!(mIsWatch || mIsTv)) { final Action cancelAction = new Action.Builder(null, mContext.getString( com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build(); final Intent infoIntent = new Intent(mContext, BugreportProgressService.class); diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 45d218548d1a..2c5120dd06f3 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -25,10 +25,10 @@ <color name="notification_legacy_background_color">@*android:color/notification_material_background_color</color> <!-- The color of the material notification background when dimmed --> - <color name="notification_material_background_dimmed_color">#aa212121</color> + <color name="notification_material_background_dimmed_color">#aa000000</color> <!-- The color of the dividing line between grouped notifications while . --> - <color name="notification_divider_color">#000</color> + <color name="notification_divider_color">#212121</color> <!-- The background color of the notification shade --> <color name="notification_shade_background_color">#181818</color> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 67db68deb928..216ed68e4fbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -1141,9 +1141,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private void updateNotificationColor() { + Configuration currentConfig = getResources().getConfiguration(); + boolean nightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) + == Configuration.UI_MODE_NIGHT_YES; + mNotificationColor = ContrastColorUtil.resolveContrastColor(mContext, getStatusBarNotification().getNotification().color, - getBackgroundColorWithoutTint()); + getBackgroundColorWithoutTint(), nightMode); mNotificationColorAmbient = ContrastColorUtil.resolveAmbientColor(mContext, getStatusBarNotification().getNotification().color); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java index 2cbb78a4a8f5..3744105b8b94 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java @@ -356,10 +356,11 @@ public class CarVolumeDialogImpl implements VolumeDialog { if (supplementalIconId != 0) { Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId); supplementalIcon.mutate().setTint(color); - listItem.setSupplementalIcon(supplementalIcon, true, - supplementalIconOnClickListener); + listItem.setSupplementalIcon(supplementalIcon, true); + listItem.setSupplementalIconListener(supplementalIconOnClickListener); } else { listItem.setSupplementalEmptyIcon(true); + listItem.setSupplementalIconListener(null); } mVolumeLineItems.add(listItem); diff --git a/packages/VpnDialogs/res/values-es-rUS/strings.xml b/packages/VpnDialogs/res/values-es-rUS/strings.xml index 3732ebc6a333..21cfc042e707 100644 --- a/packages/VpnDialogs/res/values-es-rUS/strings.xml +++ b/packages/VpnDialogs/res/values-es-rUS/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string> - <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que permite controlar el tráfico de la red. Acéptala solo si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparece en la parte superior de la pantalla cuando la VPN está activa."</string> + <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN capaz de controlar el tráfico de la red. Acéptala solo si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparece en la parte superior de la pantalla cuando se activa la VPN."</string> <string name="legacy_title" msgid="192936250066580964">"La VPN está conectada."</string> <string name="session" msgid="6470628549473641030">"Sesión:"</string> <string name="duration" msgid="3584782459928719435">"Duración:"</string> diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index b2e06f9b8c10..9bee8dba87ad 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -21,6 +21,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS; +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; @@ -1689,6 +1691,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub this, userState)); } + private void scheduleSetAllClientsMinimumUiTimeout(UserState userState) { + mMainHandler.sendMessage(obtainMessage( + AccessibilityManagerService::sendMinimumUiTimeoutChanged, + this, userState.mUserClients, userState.mMinimumUiTimeout)); + } + + private void sendMinimumUiTimeoutChanged( + RemoteCallbackList<IAccessibilityManagerClient> userClients, int uiTimeout) { + notifyClientsOfServicesMinimumUiTimeoutChange(mGlobalClients, uiTimeout); + notifyClientsOfServicesMinimumUiTimeoutChange(userClients, uiTimeout); + } + + private void notifyClientsOfServicesMinimumUiTimeoutChange( + RemoteCallbackList<IAccessibilityManagerClient> clients, int uiTimeout) { + clients.broadcast(ignoreRemoteException( + client -> client.setMinimumUiTimeout(uiTimeout))); + } + private void updateInputFilter(UserState userState) { if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) return; @@ -1822,6 +1842,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub scheduleUpdateClientsIfNeededLocked(userState); updateRelevantEventsLocked(userState); updateAccessibilityButtonTargetsLocked(userState); + updateMinimumUiTimeoutLocked(userState); } private void updateAccessibilityFocusBehaviorLocked(UserState userState) { @@ -1940,6 +1961,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub somethingChanged |= readAutoclickEnabledSettingLocked(userState); somethingChanged |= readAccessibilityShortcutSettingLocked(userState); somethingChanged |= readAccessibilityButtonSettingsLocked(userState); + somethingChanged |= readUserMinimumUiTimeoutSettingsLocked(userState); return somethingChanged; } @@ -2084,6 +2106,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } + private boolean readUserMinimumUiTimeoutSettingsLocked(UserState userState) { + final boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, 0, + userState.mUserId) == 1; + final int timeout = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, 0, + userState.mUserId); + if (enabled != userState.mUserMinimumUiTimeoutEnabled + || timeout != userState.mUserMinimumUiTimeout) { + userState.mUserMinimumUiTimeoutEnabled = enabled; + userState.mUserMinimumUiTimeout = timeout; + return true; + } + return false; + } + /** * Check if the service that will be enabled by the shortcut is installed. If it isn't, * clear the value and the associated setting so a sideloaded service can't spoof the @@ -2250,6 +2288,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + private void updateMinimumUiTimeoutLocked(UserState userState) { + int newUiTimeout = 0; + if (userState.mUserMinimumUiTimeoutEnabled) { + newUiTimeout = userState.mUserMinimumUiTimeout; + } else { + final List<AccessibilityServiceConnection> services = userState.mBoundServices; + final int numServices = services.size(); + for (int i = 0; i < numServices; i++) { + final int serviceUiTimeout = services.get(i).getServiceInfo() + .getMinimumUiTimeoutMillis(); + if (newUiTimeout < serviceUiTimeout) { + newUiTimeout = serviceUiTimeout; + } + } + } + if (newUiTimeout != userState.mMinimumUiTimeout) { + userState.mMinimumUiTimeout = newUiTimeout; + scheduleSetAllClientsMinimumUiTimeout(userState); + } + } + @GuardedBy("mLock") @Override public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) { @@ -2388,6 +2447,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mFingerprintGestureDispatcher.onFingerprintGesture(gestureKeyCode); } + /** + * Get the minimum timeout for changes to the UI needed by this user. Controls should remain + * on the screen for at least this long to give users time to react. + * + * @return The minimum timeout for the current user in milliseconds. + */ + @Override + public int getMinimumUiTimeout() { + synchronized(mLock) { + final UserState userState = getCurrentUserStateLocked(); + return userState.mMinimumUiTimeout; + } + } + @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; @@ -2405,6 +2478,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub pw.append(", navBarMagnificationEnabled=" + userState.mIsNavBarMagnificationEnabled); pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled); + pw.append(", minimumUiTimeout=" + userState.mMinimumUiTimeout); if (mUiAutomationManager.isUiAutomationRunningLocked()) { pw.append(", "); mUiAutomationManager.dumpUiAutomationService(fd, pw, args); @@ -2549,6 +2623,38 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return -1; } + private void notifyOutsideTouchIfNeeded(int targetWindowId, int action, Bundle arguments, + int interactionId, IAccessibilityInteractionConnectionCallback callback, int fetchFlags, + int interrogatingPid, long interrogatingTid) { + if (action != ACTION_CLICK && action != ACTION_LONG_CLICK) { + return; + } + + final List<Integer> outsideWindowsIds; + final List<RemoteAccessibilityConnection> connectionList = new ArrayList<>(); + synchronized (mLock) { + outsideWindowsIds = mSecurityPolicy.getWatchOutsideTouchWindowId(targetWindowId); + for (int i = 0; i < outsideWindowsIds.size(); i++) { + connectionList.add(getConnectionLocked(outsideWindowsIds.get(i))); + } + } + for (int i = 0; i < connectionList.size(); i++) { + final RemoteAccessibilityConnection connection = connectionList.get(i); + if (connection != null) { + try { + connection.mConnection.performAccessibilityAction( + AccessibilityNodeInfo.ROOT_ITEM_ID, + R.id.accessibilityActionOutsideTouch, arguments, interactionId, + callback, fetchFlags, interrogatingPid, interrogatingTid); + } catch (RemoteException re) { + if (DEBUG) { + Slog.e(LOG_TAG, "Error calling performAccessibilityAction: " + re); + } + } + } + } + } + @Override public void ensureWindowsAvailableTimed() { synchronized (mLock) { @@ -2628,6 +2734,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mPowerManager.userActivity(SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0); + notifyOutsideTouchIfNeeded(resolvedWindowId, action, arguments, interactionId, callback, + fetchFlags, interrogatingPid, interrogatingTid); if (activityToken != null) { LocalServices.getService(ActivityTaskManagerInternal.class) .setFocusedActivity(activityToken); @@ -2955,6 +3063,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; private boolean mTouchInteractionInProgress; + private boolean mHasWatchOutsideTouchWindow; private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) { final int eventType = event.getEventType(); @@ -3112,6 +3221,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mWindowInfoById.valueAt(i).recycle(); } mWindowInfoById.clear(); + mHasWatchOutsideTouchWindow = false; mFocusedWindowId = INVALID_WINDOW_ID; if (!mTouchInteractionInProgress) { @@ -3156,6 +3266,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub activeWindowGone = false; } } + if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) { + mHasWatchOutsideTouchWindow = true; + } mWindows.add(window); mA11yWindowInfoById.put(windowId, window); mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo)); @@ -3574,6 +3687,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mWindowInfoById.get(windowId); } + private List<Integer> getWatchOutsideTouchWindowId(int targetWindowId) { + if (mWindowInfoById != null && mHasWatchOutsideTouchWindow) { + final List<Integer> outsideWindowsId = new ArrayList<>(); + final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId); + for (int i = 0; i < mWindowInfoById.size(); i++) { + WindowInfo window = mWindowInfoById.valueAt(i); + if (window.layer < targetWindow.layer + && window.hasFlagWatchOutsideTouch) { + outsideWindowsId.add(mWindowInfoById.keyAt(i)); + } + } + return outsideWindowsId; + } + return Collections.emptyList(); + } + private AccessibilityWindowInfo getPictureInPictureWindow() { if (mWindows != null) { final int windowCount = mWindows.size(); @@ -3660,6 +3789,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public ComponentName mServiceToEnableWithShortcut; public int mLastSentClientState = -1; + public int mMinimumUiTimeout = 0; private int mSoftKeyboardShowMode = 0; @@ -3674,6 +3804,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public boolean mIsPerformGesturesEnabled; public boolean mIsFilterKeyEventsEnabled; public boolean mAccessibilityFocusOnlyInActiveWindow; + public boolean mUserMinimumUiTimeoutEnabled; + public int mUserMinimumUiTimeout; public boolean mBindInstantServiceAllowed; @@ -3713,6 +3845,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Clear event management state. mLastSentClientState = -1; + // clear minimum ui timeout + mMinimumUiTimeout = 0; + // Clear state persisted in settings. mEnabledServices.clear(); mTouchExplorationGrantedServices.clear(); @@ -3722,6 +3857,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mServiceAssignedToAccessibilityButton = null; mIsNavBarMagnificationAssignedToAccessibilityButton = false; mIsAutoclickEnabled = false; + mUserMinimumUiTimeoutEnabled = false; + mUserMinimumUiTimeout = 0; } public void addServiceLocked(AccessibilityServiceConnection serviceConnection) { @@ -3948,6 +4085,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Uri mAccessibilityButtonComponentIdUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT); + private final Uri mUserMinimumUiTimeoutEnabledUri = Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED); + + private final Uri mUserMinimumUiTimeoutUri = Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS); + public AccessibilityContentObserver(Handler handler) { super(handler); } @@ -3982,6 +4125,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL); + contentResolver.registerContentObserver( + mUserMinimumUiTimeoutEnabledUri, false, this, UserHandle.USER_ALL); + contentResolver.registerContentObserver( + mUserMinimumUiTimeoutUri, false, this, UserHandle.USER_ALL); } @Override @@ -4032,6 +4179,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (readAccessibilityButtonSettingsLocked(userState)) { onUserStateChangedLocked(userState); } + } else if (mUserMinimumUiTimeoutEnabledUri.equals(uri) + || mUserMinimumUiTimeoutUri.equals(uri)) { + if (readUserMinimumUiTimeoutSettingsLocked(userState)) { + updateMinimumUiTimeoutLocked(userState); + } } } } diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java new file mode 100644 index 000000000000..1ae598ec9920 --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java @@ -0,0 +1,89 @@ +/* + * 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.backup.encryption.chunk; + +import com.android.internal.util.Preconditions; +import java.util.Arrays; +import java.util.Base64; + +/** + * Represents the SHA-256 hash of the plaintext of a chunk, which is frequently used as a key. + * + * <p>This class is {@link Comparable} and implements {@link #equals(Object)} and {@link + * #hashCode()}. + */ +public class ChunkHash implements Comparable<ChunkHash> { + /** The length of the hash in bytes. The hash is a SHA-256, so this is 256 bits. */ + public static final int HASH_LENGTH_BYTES = 256 / 8; + + private static final int UNSIGNED_MASK = 0xFF; + + private final byte[] mHash; + + /** Constructs a new instance which wraps the given SHA-256 hash bytes. */ + public ChunkHash(byte[] hash) { + Preconditions.checkArgument(hash.length == HASH_LENGTH_BYTES, "Hash must have 256 bits"); + mHash = hash; + } + + public byte[] getHash() { + return mHash; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ChunkHash)) { + return false; + } + + ChunkHash chunkHash = (ChunkHash) o; + return Arrays.equals(mHash, chunkHash.mHash); + } + + @Override + public int hashCode() { + return Arrays.hashCode(mHash); + } + + @Override + public int compareTo(ChunkHash other) { + return lexicographicalCompareUnsignedBytes(getHash(), other.getHash()); + } + + @Override + public String toString() { + return Base64.getEncoder().encodeToString(mHash); + } + + private static int lexicographicalCompareUnsignedBytes(byte[] left, byte[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = toInt(left[i]) - toInt(right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + private static int toInt(byte value) { + return value & UNSIGNED_MASK; + } +} diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 499c03d05bf2..ad2f82c9b19f 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -81,6 +81,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseLongArray; +import android.util.StatsLog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -3637,6 +3638,8 @@ class AlarmManagerService extends SystemService { if (DEBUG_BATCH) { Slog.v(TAG, "Time changed notification from kernel; rebatching"); } + // StatsLog requires currentTimeMillis(), which == nowRTC to within usecs. + StatsLog.write(StatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC); removeImpl(mTimeTickSender); removeImpl(mDateChangeSender); rebatchAllAlarms(); diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java index 4016d29fd2e6..e21a3d7917d1 100644 --- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java +++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java @@ -26,7 +26,6 @@ import static android.os.HardwarePropertiesManager.TEMPERATURE_THROTTLING; import static android.os.HardwarePropertiesManager.TEMPERATURE_THROTTLING_BELOW_VR_MIN; import android.Manifest; -import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.Context; @@ -34,8 +33,8 @@ import android.content.pm.PackageManager; import android.os.Binder; import android.os.CpuUsageInfo; import android.os.IHardwarePropertiesManager; -import android.os.Process; import android.os.UserHandle; + import com.android.internal.util.DumpUtils; import com.android.server.vr.VrManagerInternal; @@ -166,11 +165,11 @@ public class HardwarePropertiesManagerService extends IHardwarePropertiesManager final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); if (!dpm.isDeviceOwnerApp(callingPackage) - && !vrService.isCurrentVrListener(callingPackage, userId) && mContext.checkCallingOrSelfPermission(Manifest.permission.DEVICE_POWER) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("The caller is not a device owner, bound VrListenerService" - + ", or holding the DEVICE_POWER permission."); + != PackageManager.PERMISSION_GRANTED + && (vrService == null || !vrService.isCurrentVrListener(callingPackage, userId))) { + throw new SecurityException("The caller is neither a device owner" + + ", nor holding the DEVICE_POWER permission, nor the current VrListener."); } } } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 01e81525d5b5..380f6a7e581e 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -612,7 +612,7 @@ public class IpSecService extends IIpSecService.Stub { mSrvConfig .getNetdInstance() .ipSecDeleteSecurityAssociation( - mResourceId, + uid, mConfig.getSourceAddress(), mConfig.getDestinationAddress(), spi, @@ -679,7 +679,7 @@ public class IpSecService extends IIpSecService.Stub { mSrvConfig .getNetdInstance() .ipSecDeleteSecurityAssociation( - mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0); + uid, mSourceAddress, mDestinationAddress, mSpi, 0, 0); } } catch (ServiceSpecificException | RemoteException e) { Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e); @@ -821,13 +821,13 @@ public class IpSecService extends IIpSecService.Stub { for (int selAddrFamily : ADDRESS_FAMILIES) { netd.ipSecDeleteSecurityPolicy( - 0, + uid, selAddrFamily, IpSecManager.DIRECTION_OUT, mOkey, 0xffffffff); netd.ipSecDeleteSecurityPolicy( - 0, + uid, selAddrFamily, IpSecManager.DIRECTION_IN, mIkey, @@ -1083,7 +1083,8 @@ public class IpSecService extends IIpSecService.Stub { } checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex"); - UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); + int callingUid = Binder.getCallingUid(); + UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid); final int resourceId = mNextResourceId++; int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; @@ -1096,7 +1097,7 @@ public class IpSecService extends IIpSecService.Stub { spi = mSrvConfig .getNetdInstance() - .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi); + .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi); Log.d(TAG, "Allocated SPI " + spi); userRecord.mSpiRecords.put( resourceId, @@ -1264,7 +1265,8 @@ public class IpSecService extends IIpSecService.Stub { // TODO: Check that underlying network exists, and IP addresses not assigned to a different // network (b/72316676). - UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); + int callerUid = Binder.getCallingUid(); + UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid); if (!userRecord.mTunnelQuotaTracker.isAvailable()) { return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); } @@ -1285,7 +1287,7 @@ public class IpSecService extends IIpSecService.Stub { for (int selAddrFamily : ADDRESS_FAMILIES) { // Always send down correct local/remote addresses for template. netd.ipSecAddSecurityPolicy( - 0, // Use 0 for reqId + callerUid, selAddrFamily, IpSecManager.DIRECTION_OUT, localAddr, @@ -1294,7 +1296,7 @@ public class IpSecService extends IIpSecService.Stub { okey, 0xffffffff); netd.ipSecAddSecurityPolicy( - 0, // Use 0 for reqId + callerUid, selAddrFamily, IpSecManager.DIRECTION_IN, remoteAddr, @@ -1532,7 +1534,7 @@ public class IpSecService extends IIpSecService.Stub { mSrvConfig .getNetdInstance() .ipSecAddSecurityAssociation( - resourceId, + Binder.getCallingUid(), c.getMode(), c.getSourceAddress(), c.getDestinationAddress(), @@ -1623,13 +1625,14 @@ public class IpSecService extends IIpSecService.Stub { @Override public synchronized void applyTransportModeTransform( ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException { - UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); + int callingUid = Binder.getCallingUid(); + UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid); checkDirection(direction); // Get transform record; if no transform is found, will throw IllegalArgumentException TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId); // TODO: make this a function. - if (info.pid != getCallingPid() || info.uid != getCallingUid()) { + if (info.pid != getCallingPid() || info.uid != callingUid) { throw new SecurityException("Only the owner of an IpSec Transform may apply it!"); } @@ -1643,7 +1646,7 @@ public class IpSecService extends IIpSecService.Stub { .getNetdInstance() .ipSecApplyTransportModeTransform( socket.getFileDescriptor(), - resourceId, + callingUid, direction, c.getSourceAddress(), c.getDestinationAddress(), @@ -1675,7 +1678,8 @@ public class IpSecService extends IIpSecService.Stub { enforceTunnelPermissions(callingPackage); checkDirection(direction); - UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); + int callingUid = Binder.getCallingUid(); + UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid); // Get transform record; if no transform is found, will throw IllegalArgumentException TransformRecord transformInfo = @@ -1717,7 +1721,7 @@ public class IpSecService extends IIpSecService.Stub { mSrvConfig .getNetdInstance() .ipSecUpdateSecurityPolicy( - 0, // Use 0 for reqId + callingUid, selAddrFamily, direction, tunnelInterfaceInfo.getLocalAddress(), diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index daf870dfebf5..1d163eed00a4 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -1565,10 +1565,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub try { // TODO: support quota shared across interfaces - mConnector.execute("bandwidth", "setiquota", iface, quotaBytes); + mNetdService.bandwidthSetInterfaceQuota(iface, quotaBytes); + mActiveQuotas.put(iface, quotaBytes); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } synchronized (mTetheringStatsProviders) { @@ -1599,9 +1600,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub try { // TODO: support quota shared across interfaces - mConnector.execute("bandwidth", "removeiquota", iface); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.bandwidthRemoveInterfaceQuota(iface); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } synchronized (mTetheringStatsProviders) { @@ -1633,10 +1634,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub try { // TODO: support alert shared across interfaces - mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes); + mNetdService.bandwidthSetInterfaceAlert(iface, alertBytes); mActiveAlerts.put(iface, alertBytes); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } } @@ -1653,10 +1654,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub try { // TODO: support alert shared across interfaces - mConnector.execute("bandwidth", "removeinterfacealert", iface); + mNetdService.bandwidthRemoveInterfaceAlert(iface); mActiveAlerts.remove(iface); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } } @@ -1666,18 +1667,15 @@ public class NetworkManagementService extends INetworkManagementService.Stub mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("bandwidth", "setglobalalert", alertBytes); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.bandwidthSetGlobalAlert(alertBytes); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } private void setUidOnMeteredNetworkList(int uid, boolean blacklist, boolean enable) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - final String chain = blacklist ? "naughtyapps" : "niceapps"; - final String suffix = enable ? "add" : "remove"; - synchronized (mQuotaLock) { boolean oldEnable; SparseBooleanArray quotaList; @@ -1692,7 +1690,19 @@ public class NetworkManagementService extends INetworkManagementService.Stub Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "inetd bandwidth"); try { - mConnector.execute("bandwidth", suffix + chain, uid); + if (blacklist) { + if (enable) { + mNetdService.bandwidthAddNaughtyApp(uid); + } else { + mNetdService.bandwidthRemoveNaughtyApp(uid); + } + } else { + if (enable) { + mNetdService.bandwidthAddNiceApp(uid); + } else { + mNetdService.bandwidthRemoveNiceApp(uid); + } + } synchronized (mRulesLock) { if (enable) { quotaList.put(uid, true); @@ -1700,8 +1710,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub quotaList.delete(uid); } } - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java index 708350d6f174..40f81b3c2850 100644 --- a/services/core/java/com/android/server/TextServicesManagerService.java +++ b/services/core/java/com/android/server/TextServicesManagerService.java @@ -20,7 +20,7 @@ import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_S import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; -import com.android.internal.inputmethod.LocaleUtils; +import com.android.internal.inputmethod.InputMethodUtils; import com.android.internal.textservice.ISpellCheckerService; import com.android.internal.textservice.ISpellCheckerServiceCallback; import com.android.internal.textservice.ISpellCheckerSession; @@ -461,7 +461,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { // is pre-installed or not. final Locale systemLocal = mContext.getResources().getConfiguration().locale; final ArrayList<Locale> suitableLocales = - LocaleUtils.getSuitableLocalesForSpellChecker(systemLocal); + InputMethodUtils.getSuitableLocalesForSpellChecker(systemLocal); if (DBG) { Slog.w(TAG, "findAvailSystemSpellCheckerLocked suitableLocales=" + Arrays.toString(suitableLocales.toArray(new Locale[suitableLocales.size()]))); @@ -475,7 +475,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { final int subtypeCount = info.getSubtypeCount(); for (int subtypeIndex = 0; subtypeIndex < subtypeCount; ++subtypeIndex) { final SpellCheckerSubtype subtype = info.getSubtypeAt(subtypeIndex); - final Locale subtypeLocale = LocaleUtils.constructLocaleFromString( + final Locale subtypeLocale = InputMethodUtils.constructLocaleFromString( subtype.getLocale()); if (locale.equals(subtypeLocale)) { // TODO: We may have more spell checkers that fall into this category. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2ee598fee535..b64e8b87aeec 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2382,7 +2382,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } int logSampleRate = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, -1); + Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0x200); if (logSampleRate < 0 || logSampleRate > 0x10000) { logSampleRate = -1; } @@ -11941,7 +11941,9 @@ public class ActivityManagerService extends IActivityManager.Stub printed = true; } pw.print(" "); UserHandle.formatUid(pw, reg.uid); - pw.print(" "); pw.print(reg.pkg); pw.print(":"); + pw.print(" "); pw.print(reg.pkg); + final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i); + pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":"); if ((reg.which&ActivityManager.UID_OBSERVER_IDLE) != 0) { pw.print(" IDLE"); } diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java index 347a357aae04..cd4d6a3e14c5 100644 --- a/services/core/java/com/android/server/am/BaseErrorDialog.java +++ b/services/core/java/com/android/server/am/BaseErrorDialog.java @@ -33,7 +33,7 @@ class BaseErrorDialog extends AlertDialog { private boolean mConsuming = true; public BaseErrorDialog(Context context) { - super(context, com.android.internal.R.style.Theme_Dialog_AppError); + super(context, com.android.internal.R.style.Theme_DeviceDefault_Dialog_AppError); context.assertRuntimeOverlayThemable(); getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index 1b688a6cd9c6..76c191d5e9ea 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -26,8 +26,8 @@ import android.util.Pair; import android.util.Slog; import android.util.Spline; -import com.android.internal.util.Preconditions; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import com.android.server.display.utils.Plog; import java.io.PrintWriter; @@ -77,8 +77,8 @@ public abstract class BrightnessMappingStrategy { Slog.w(TAG, "Screen brightness mapping does not cover whole range of available " + "backlight values, autobrightness functionality may be impaired."); } - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); - builder.setCurve(luxLevels, brightnessLevelsNits); + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( + luxLevels, brightnessLevelsNits); return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange, autoBrightnessAdjustmentMaxGamma); } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) { diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index b4bc7f507701..452b699667b7 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -46,9 +46,9 @@ public class NetworkPolicyLogger { static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE); private static final int MAX_LOG_SIZE = - ActivityManager.isLowRamDeviceStatic() ? 20 : 50; + ActivityManager.isLowRamDeviceStatic() ? 100 : 400; private static final int MAX_NETWORK_BLOCKED_LOG_SIZE = - ActivityManager.isLowRamDeviceStatic() ? 50 : 100; + ActivityManager.isLowRamDeviceStatic() ? 100 : 400; private static final int EVENT_TYPE_GENERIC = 0; private static final int EVENT_NETWORK_BLOCKED = 1; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 76f969508b28..48e09d744ff4 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -796,7 +796,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { mActivityManager.registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE, - ActivityManager.PROCESS_STATE_UNKNOWN, null); + NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android"); mNetworkManager.registerObserver(mAlertObserver); } catch (RemoteException e) { // ignored; both services live in system_server diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 340ae0ad637a..7751f5f9bcb8 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -694,7 +694,8 @@ abstract public class ManagedServices { for (int userId : userIds) { if (enabled) { - if (isPackageOrComponentAllowed(component.toString(), userId)) { + if (isPackageOrComponentAllowed(component.toString(), userId) + || isPackageOrComponentAllowed(component.getPackageName(), userId)) { registerServiceLocked(component, userId); } else { Slog.d(TAG, component + " no longer has permission to be bound"); diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 593e7cdf4d66..13ff6e8e551f 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -107,7 +107,7 @@ public class PreferencesHelper implements RankingConfig { } // pkg|uid => PackagePreferences - private final ArrayMap<String, PackagePreferences> mPackagePreferencess = new ArrayMap<>(); + private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>(); // pkg => PackagePreferences private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>(); @@ -142,109 +142,117 @@ public class PreferencesHelper implements RankingConfig { if (type != XmlPullParser.START_TAG) return; String tag = parser.getName(); if (!TAG_RANKING.equals(tag)) return; - // Clobber groups and channels with the xml, but don't delete other data that wasn't present - // at the time of serialization. - mRestoredWithoutUids.clear(); - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { - tag = parser.getName(); - if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) { - return; - } - if (type == XmlPullParser.START_TAG) { - if (TAG_PACKAGE.equals(tag)) { - int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID); - String name = parser.getAttributeValue(null, ATT_NAME); - if (!TextUtils.isEmpty(name)) { - if (forRestore) { - try { - //TODO: http://b/22388012 - uid = mPm.getPackageUidAsUser(name, - UserHandle.USER_SYSTEM); - } catch (PackageManager.NameNotFoundException e) { - // noop + synchronized (mPackagePreferences) { + // Clobber groups and channels with the xml, but don't delete other data that wasn't present + + // at the time of serialization. + mRestoredWithoutUids.clear(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + tag = parser.getName(); + if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) { + return; + } + if (type == XmlPullParser.START_TAG) { + if (TAG_PACKAGE.equals(tag)) { + int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID); + String name = parser.getAttributeValue(null, ATT_NAME); + if (!TextUtils.isEmpty(name)) { + if (forRestore) { + try { + //TODO: http://b/22388012 + uid = mPm.getPackageUidAsUser(name, + UserHandle.USER_SYSTEM); + } catch (PackageManager.NameNotFoundException e) { + // noop + } } - } - PackagePreferences r = getOrCreatePackagePreferences(name, uid, - XmlUtils.readIntAttribute( - parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE), - XmlUtils.readIntAttribute(parser, ATT_PRIORITY, DEFAULT_PRIORITY), - XmlUtils.readIntAttribute( - parser, ATT_VISIBILITY, DEFAULT_VISIBILITY), - XmlUtils.readBooleanAttribute( - parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE)); - r.importance = XmlUtils.readIntAttribute( - parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); - r.priority = XmlUtils.readIntAttribute( - parser, ATT_PRIORITY, DEFAULT_PRIORITY); - r.visibility = XmlUtils.readIntAttribute( - parser, ATT_VISIBILITY, DEFAULT_VISIBILITY); - r.showBadge = XmlUtils.readBooleanAttribute( - parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE); - r.lockedAppFields = XmlUtils.readIntAttribute(parser, - ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS); - - final int innerDepth = parser.getDepth(); - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > innerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } + PackagePreferences r = getOrCreatePackagePreferences(name, uid, + XmlUtils.readIntAttribute( + parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE), + XmlUtils.readIntAttribute(parser, ATT_PRIORITY, + DEFAULT_PRIORITY), + XmlUtils.readIntAttribute( + parser, ATT_VISIBILITY, DEFAULT_VISIBILITY), + XmlUtils.readBooleanAttribute( + parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE)); + r.importance = XmlUtils.readIntAttribute( + parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); + r.priority = XmlUtils.readIntAttribute( + parser, ATT_PRIORITY, DEFAULT_PRIORITY); + r.visibility = XmlUtils.readIntAttribute( + parser, ATT_VISIBILITY, DEFAULT_VISIBILITY); + r.showBadge = XmlUtils.readBooleanAttribute( + parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE); + r.lockedAppFields = XmlUtils.readIntAttribute(parser, + ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS); + + final int innerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } - String tagName = parser.getName(); - // Channel groups - if (TAG_GROUP.equals(tagName)) { - String id = parser.getAttributeValue(null, ATT_ID); - CharSequence groupName = parser.getAttributeValue(null, ATT_NAME); - if (!TextUtils.isEmpty(id)) { - NotificationChannelGroup group - = new NotificationChannelGroup(id, groupName); - group.populateFromXml(parser); - r.groups.put(id, group); + String tagName = parser.getName(); + // Channel groups + if (TAG_GROUP.equals(tagName)) { + String id = parser.getAttributeValue(null, ATT_ID); + CharSequence groupName = parser.getAttributeValue(null, + ATT_NAME); + if (!TextUtils.isEmpty(id)) { + NotificationChannelGroup group + = new NotificationChannelGroup(id, groupName); + group.populateFromXml(parser); + r.groups.put(id, group); + } } - } - // Channels - if (TAG_CHANNEL.equals(tagName)) { - String id = parser.getAttributeValue(null, ATT_ID); - String channelName = parser.getAttributeValue(null, ATT_NAME); - int channelImportance = XmlUtils.readIntAttribute( - parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); - if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) { - NotificationChannel channel = new NotificationChannel(id, - channelName, channelImportance); - if (forRestore) { - channel.populateFromXmlForRestore(parser, mContext); - } else { - channel.populateFromXml(parser); + // Channels + if (TAG_CHANNEL.equals(tagName)) { + String id = parser.getAttributeValue(null, ATT_ID); + String channelName = parser.getAttributeValue(null, ATT_NAME); + int channelImportance = XmlUtils.readIntAttribute( + parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); + if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) { + NotificationChannel channel = new NotificationChannel(id, + channelName, channelImportance); + if (forRestore) { + channel.populateFromXmlForRestore(parser, mContext); + } else { + channel.populateFromXml(parser); + } + r.channels.put(id, channel); } - r.channels.put(id, channel); } - } - // Delegate - if (TAG_DELEGATE.equals(tagName)) { - int delegateId = - XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID); - String delegateName = - XmlUtils.readStringAttribute(parser, ATT_NAME); - boolean delegateEnabled = XmlUtils.readBooleanAttribute( - parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED); - boolean userAllowed = XmlUtils.readBooleanAttribute( - parser, ATT_USER_ALLOWED, Delegate.DEFAULT_USER_ALLOWED); - Delegate d = null; - if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(delegateName)) { - d = new Delegate( - delegateName, delegateId, delegateEnabled, userAllowed); + // Delegate + if (TAG_DELEGATE.equals(tagName)) { + int delegateId = + XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID); + String delegateName = + XmlUtils.readStringAttribute(parser, ATT_NAME); + boolean delegateEnabled = XmlUtils.readBooleanAttribute( + parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED); + boolean userAllowed = XmlUtils.readBooleanAttribute( + parser, ATT_USER_ALLOWED, + Delegate.DEFAULT_USER_ALLOWED); + Delegate d = null; + if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty( + delegateName)) { + d = new Delegate( + delegateName, delegateId, delegateEnabled, + userAllowed); + } + r.delegate = d; } - r.delegate = d; - } - } + } - try { - deleteDefaultChannelIfNeeded(r); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e); + try { + deleteDefaultChannelIfNeeded(r); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e); + } } } } @@ -255,8 +263,8 @@ public class PreferencesHelper implements RankingConfig { private PackagePreferences getPackagePreferences(String pkg, int uid) { final String key = packagePreferencesKey(pkg, uid); - synchronized (mPackagePreferencess) { - return mPackagePreferencess.get(key); + synchronized (mPackagePreferences) { + return mPackagePreferences.get(key); } } @@ -268,10 +276,10 @@ public class PreferencesHelper implements RankingConfig { private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance, int priority, int visibility, boolean showBadge) { final String key = packagePreferencesKey(pkg, uid); - synchronized (mPackagePreferencess) { + synchronized (mPackagePreferences) { PackagePreferences r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg) - : mPackagePreferencess.get(key); + : mPackagePreferences.get(key); if (r == null) { r = new PackagePreferences(); r.pkg = pkg; @@ -290,7 +298,7 @@ public class PreferencesHelper implements RankingConfig { if (r.uid == UNKNOWN_UID) { mRestoredWithoutUids.put(pkg, r); } else { - mPackagePreferencess.put(key, r); + mPackagePreferences.put(key, r); } } return r; @@ -364,10 +372,10 @@ public class PreferencesHelper implements RankingConfig { out.startTag(null, TAG_RANKING); out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION)); - synchronized (mPackagePreferencess) { - final int N = mPackagePreferencess.size(); + synchronized (mPackagePreferences) { + final int N = mPackagePreferences.size(); for (int i = 0; i < N; i++) { - final PackagePreferences r = mPackagePreferencess.valueAt(i); + final PackagePreferences r = mPackagePreferences.valueAt(i); //TODO: http://b/22388012 if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) { continue; @@ -882,10 +890,10 @@ public class PreferencesHelper implements RankingConfig { public int getBlockedAppCount(int userId) { int count = 0; - synchronized (mPackagePreferencess) { - final int N = mPackagePreferencess.size(); + synchronized (mPackagePreferences) { + final int N = mPackagePreferences.size(); for (int i = 0; i < N; i++) { - final PackagePreferences r = mPackagePreferencess.valueAt(i); + final PackagePreferences r = mPackagePreferences.valueAt(i); if (userId == UserHandle.getUserId(r.uid) && r.importance == IMPORTANCE_NONE) { count++; @@ -896,11 +904,11 @@ public class PreferencesHelper implements RankingConfig { } public void updateChannelsBypassingDnd() { - synchronized (mPackagePreferencess) { - final int numPackagePreferencess = mPackagePreferencess.size(); + synchronized (mPackagePreferences) { + final int numPackagePreferencess = mPackagePreferences.size(); for (int PackagePreferencesIndex = 0; PackagePreferencesIndex < numPackagePreferencess; PackagePreferencesIndex++) { - final PackagePreferences r = mPackagePreferencess.valueAt(PackagePreferencesIndex); + final PackagePreferences r = mPackagePreferences.valueAt(PackagePreferencesIndex); final int numChannels = r.channels.size(); for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) { @@ -1064,8 +1072,8 @@ public class PreferencesHelper implements RankingConfig { pw.println("per-package config:"); pw.println("PackagePreferencess:"); - synchronized (mPackagePreferencess) { - dumpPackagePreferencess(pw, prefix, filter, mPackagePreferencess); + synchronized (mPackagePreferences) { + dumpPackagePreferencess(pw, prefix, filter, mPackagePreferences); } pw.println("Restored without uid:"); dumpPackagePreferencess(pw, prefix, filter, mRestoredWithoutUids); @@ -1073,9 +1081,9 @@ public class PreferencesHelper implements RankingConfig { public void dump(ProtoOutputStream proto, @NonNull NotificationManagerService.DumpFilter filter) { - synchronized (mPackagePreferencess) { + synchronized (mPackagePreferences) { dumpPackagePreferencess(proto, RankingHelperProto.RECORDS, filter, - mPackagePreferencess); + mPackagePreferences); } dumpPackagePreferencess(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter, mRestoredWithoutUids); @@ -1160,10 +1168,10 @@ public class PreferencesHelper implements RankingConfig { } catch (JSONException e) { // pass } - synchronized (mPackagePreferencess) { - final int N = mPackagePreferencess.size(); + synchronized (mPackagePreferences) { + final int N = mPackagePreferences.size(); for (int i = 0; i < N; i++) { - final PackagePreferences r = mPackagePreferencess.valueAt(i); + final PackagePreferences r = mPackagePreferences.valueAt(i); if (filter == null || filter.matches(r.pkg)) { JSONObject PackagePreferences = new JSONObject(); try { @@ -1240,11 +1248,11 @@ public class PreferencesHelper implements RankingConfig { } public Map<Integer, String> getPackageBans() { - synchronized (mPackagePreferencess) { - final int N = mPackagePreferencess.size(); + synchronized (mPackagePreferences) { + final int N = mPackagePreferences.size(); ArrayMap<Integer, String> packageBans = new ArrayMap<>(N); for (int i = 0; i < N; i++) { - final PackagePreferences r = mPackagePreferencess.valueAt(i); + final PackagePreferences r = mPackagePreferences.valueAt(i); if (r.importance == IMPORTANCE_NONE) { packageBans.put(r.uid, r.pkg); } @@ -1284,9 +1292,9 @@ public class PreferencesHelper implements RankingConfig { private Map<String, Integer> getPackageChannels() { ArrayMap<String, Integer> packageChannels = new ArrayMap<>(); - synchronized (mPackagePreferencess) { - for (int i = 0; i < mPackagePreferencess.size(); i++) { - final PackagePreferences r = mPackagePreferencess.valueAt(i); + synchronized (mPackagePreferences) { + for (int i = 0; i < mPackagePreferences.size(); i++) { + final PackagePreferences r = mPackagePreferences.valueAt(i); int channelCount = 0; for (int j = 0; j < r.channels.size(); j++) { if (!r.channels.valueAt(j).isDeleted()) { @@ -1300,22 +1308,22 @@ public class PreferencesHelper implements RankingConfig { } public void onUserRemoved(int userId) { - synchronized (mPackagePreferencess) { - int N = mPackagePreferencess.size(); + synchronized (mPackagePreferences) { + int N = mPackagePreferences.size(); for (int i = N - 1; i >= 0; i--) { - PackagePreferences PackagePreferences = mPackagePreferencess.valueAt(i); + PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i); if (UserHandle.getUserId(PackagePreferences.uid) == userId) { - mPackagePreferencess.removeAt(i); + mPackagePreferences.removeAt(i); } } } } protected void onLocaleChanged(Context context, int userId) { - synchronized (mPackagePreferencess) { - int N = mPackagePreferencess.size(); + synchronized (mPackagePreferences) { + int N = mPackagePreferences.size(); for (int i = 0; i < N; i++) { - PackagePreferences PackagePreferences = mPackagePreferencess.valueAt(i); + PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i); if (UserHandle.getUserId(PackagePreferences.uid) == userId) { if (PackagePreferences.channels.containsKey( NotificationChannel.DEFAULT_CHANNEL_ID)) { @@ -1341,8 +1349,8 @@ public class PreferencesHelper implements RankingConfig { for (int i = 0; i < size; i++) { final String pkg = pkgList[i]; final int uid = uidList[i]; - synchronized (mPackagePreferencess) { - mPackagePreferencess.remove(packagePreferencesKey(pkg, uid)); + synchronized (mPackagePreferences) { + mPackagePreferences.remove(packagePreferencesKey(pkg, uid)); } mRestoredWithoutUids.remove(pkg); updated = true; @@ -1355,8 +1363,8 @@ public class PreferencesHelper implements RankingConfig { try { r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId); mRestoredWithoutUids.remove(pkg); - synchronized (mPackagePreferencess) { - mPackagePreferencess.put(packagePreferencesKey(r.pkg, r.uid), r); + synchronized (mPackagePreferences) { + mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r); } updated = true; } catch (PackageManager.NameNotFoundException e) { @@ -1365,11 +1373,13 @@ public class PreferencesHelper implements RankingConfig { } // Package upgrade try { - PackagePreferences fullPackagePreferences = getPackagePreferences(pkg, - mPm.getPackageUidAsUser(pkg, changeUserId)); - if (fullPackagePreferences != null) { - createDefaultChannelIfNeeded(fullPackagePreferences); - deleteDefaultChannelIfNeeded(fullPackagePreferences); + synchronized (mPackagePreferences) { + PackagePreferences fullPackagePreferences = getPackagePreferences(pkg, + mPm.getPackageUidAsUser(pkg, changeUserId)); + if (fullPackagePreferences != null) { + createDefaultChannelIfNeeded(fullPackagePreferences); + deleteDefaultChannelIfNeeded(fullPackagePreferences); + } } } catch (PackageManager.NameNotFoundException e) { } diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index c73870189002..f1b03d1fc9d6 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -558,8 +558,8 @@ public final class OverlayManagerService extends SystemService { @Override public boolean setEnabledExclusive(@Nullable final String packageName, final boolean enable, int userId) throws RemoteException { - enforceChangeOverlayPackagesPermission("setEnabled"); - userId = handleIncomingUser(userId, "setEnabled"); + enforceChangeOverlayPackagesPermission("setEnabledExclusive"); + userId = handleIncomingUser(userId, "setEnabledExclusive"); if (packageName == null || !enable) { return false; } @@ -578,8 +578,8 @@ public final class OverlayManagerService extends SystemService { @Override public boolean setEnabledExclusiveInCategory(@Nullable String packageName, int userId) throws RemoteException { - enforceChangeOverlayPackagesPermission("setEnabled"); - userId = handleIncomingUser(userId, "setEnabled"); + enforceChangeOverlayPackagesPermission("setEnabledExclusiveInCategory"); + userId = handleIncomingUser(userId, "setEnabledExclusiveInCategory"); if (packageName == null) { return false; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index d305032bb6ce..8f2833f68192 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -59,6 +59,7 @@ import android.content.pm.PackageParser; import android.content.pm.PackageParser.ApkLite; import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.dex.DexMetadataHelper; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Binder; @@ -99,7 +100,6 @@ import com.android.server.LocalServices; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; -import android.content.pm.dex.DexMetadataHelper; import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; @@ -122,7 +122,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final boolean LOGD = true; private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed"; - private static final int MSG_EARLY_BIND = 0; private static final int MSG_COMMIT = 1; private static final int MSG_ON_PACKAGE_INSTALLED = 2; @@ -168,7 +167,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final int userId; final SessionParams params; final long createdMillis; - final int defaultContainerGid; /** Staging location where client data is written. */ final File stageDir; @@ -285,9 +283,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public boolean handleMessage(Message msg) { switch (msg.what) { - case MSG_EARLY_BIND: - earlyBindToDefContainer(); - break; case MSG_COMMIT: synchronized (mLock) { try { @@ -323,10 +318,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }; - private void earlyBindToDefContainer() { - mPm.earlyBindToDefContainer(); - } - /** * @return {@code true} iff the installing is app an device owner or affiliated profile owner. */ @@ -413,19 +404,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } } - - final long identity = Binder.clearCallingIdentity(); - try { - final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE, - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); - defaultContainerGid = UserHandle.getSharedAppGid(uid); - } finally { - Binder.restoreCallingIdentity(identity); - } - // attempt to bind to the DefContainer as early as possible - if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_EARLY_BIND)); - } } public SessionInfo generateInfo() { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index cacdccb2dc4e..adf95dc135b1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -88,8 +88,6 @@ import static android.content.pm.PackageParser.isApkFile; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.storage.StorageManager.FLAG_STORAGE_CE; import static android.os.storage.StorageManager.FLAG_STORAGE_DE; -import static android.system.OsConstants.O_CREAT; -import static android.system.OsConstants.O_RDWR; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT; @@ -137,7 +135,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; -import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.AppsQueryHelper; @@ -207,14 +204,12 @@ import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Environment; -import android.os.Environment.UserEnvironment; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; -import android.os.ParcelFileDescriptor; import android.os.PatternMatcher; import android.os.PersistableBundle; import android.os.Process; @@ -273,12 +268,10 @@ import android.view.Display; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; -import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.logging.MetricsLogger; -import com.android.internal.os.IParcelFileDescriptorFactory; import com.android.internal.os.SomeArgs; import com.android.internal.os.Zygote; import com.android.internal.telephony.CarrierAppUtils; @@ -551,12 +544,6 @@ public class PackageManagerService extends IPackageManager.Stub public static final String PLATFORM_PACKAGE_NAME = "android"; - public static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer"; - - public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( - DEFAULT_CONTAINER_PACKAGE, - "com.android.defcontainer.DefaultContainerService"); - private static final String KILL_APP_REASON_GIDS_CHANGED = "permission grant or revoke changed gids"; @@ -1238,18 +1225,9 @@ public class PackageManagerService extends IPackageManager.Stub } final PendingPackageBroadcasts mPendingBroadcasts = new PendingPackageBroadcasts(); - // Service Connection to remote media container service to copy - // package uri's from external media onto secure containers - // or internal storage. - private IMediaContainerService mContainerService = null; - static final int SEND_PENDING_BROADCAST = 1; - static final int MCS_BOUND = 3; static final int INIT_COPY = 5; - static final int MCS_UNBIND = 6; static final int POST_INSTALL = 9; - static final int MCS_RECONNECT = 10; - static final int MCS_GIVE_UP = 11; static final int WRITE_SETTINGS = 13; static final int WRITE_PACKAGE_RESTRICTIONS = 14; static final int PACKAGE_VERIFIED = 15; @@ -1258,7 +1236,6 @@ public class PackageManagerService extends IPackageManager.Stub static final int INTENT_FILTER_VERIFIED = 18; static final int WRITE_PACKAGE_LIST = 19; static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20; - static final int DEF_CONTAINER_BIND = 21; static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds @@ -1273,21 +1250,6 @@ public class PackageManagerService extends IPackageManager.Stub // Stores a list of users whose package restrictions file needs to be updated private ArraySet<Integer> mDirtyUsers = new ArraySet<>(); - final private DefaultContainerConnection mDefContainerConn = - new DefaultContainerConnection(); - class DefaultContainerConnection implements ServiceConnection { - public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected"); - final IMediaContainerService imcs = IMediaContainerService.Stub - .asInterface(Binder.allowBlocking(service)); - mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); - } - - public void onServiceDisconnected(ComponentName name) { - if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected"); - } - } - // Recordkeeping of restore-after-install operations that are currently in flight // between the Package Manager and the Backup Manager static class PostInstallData { @@ -1346,31 +1308,6 @@ public class PackageManagerService extends IPackageManager.Stub private final CompilerStats mCompilerStats = new CompilerStats(); class PackageHandler extends Handler { - private boolean mBound = false; - final ArrayList<HandlerParams> mPendingInstalls = - new ArrayList<>(); - - private boolean connectToService() { - if (DEBUG_INSTALL) Log.i(TAG, "Trying to bind to DefaultContainerService"); - Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); - Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - if (mContext.bindServiceAsUser(service, mDefContainerConn, - Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - mBound = true; - return true; - } - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - return false; - } - - private void disconnectService() { - mContainerService = null; - mBound = false; - Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - mContext.unbindService(mDefContainerConn); - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - } PackageHandler(Looper looper) { super(looper); @@ -1386,167 +1323,18 @@ public class PackageManagerService extends IPackageManager.Stub void doHandleMessage(Message msg) { switch (msg.what) { - case DEF_CONTAINER_BIND: - if (!mBound) { - Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "earlyBindingMCS", - System.identityHashCode(mHandler)); - if (!connectToService()) { - Slog.e(TAG, "Failed to bind to media container service"); - } - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "earlyBindingMCS", - System.identityHashCode(mHandler)); - } - break; case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; - int idx = mPendingInstalls.size(); - if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params); - // If a bind was already initiated we dont really - // need to do anything. The pending install - // will be processed later on. - if (!mBound) { - Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", - System.identityHashCode(mHandler)); - // If this is the only one pending we might - // have to bind to the service again. - if (!connectToService()) { - Slog.e(TAG, "Failed to bind to media container service"); - params.serviceError(); - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", - System.identityHashCode(mHandler)); - if (params.traceMethod != null) { - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod, - params.traceCookie); - } - return; - } else { - // Once we bind to the service, the first - // pending request will be processed. - mPendingInstalls.add(idx, params); - } - } else { - mPendingInstalls.add(idx, params); - // Already bound to the service. Just make - // sure we trigger off processing the first request. - if (idx == 0) { - mHandler.sendEmptyMessage(MCS_BOUND); - } - } - break; - } - case MCS_BOUND: { - if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound"); - if (msg.obj != null) { - mContainerService = (IMediaContainerService) msg.obj; - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", - System.identityHashCode(mHandler)); - } - if (mContainerService == null) { - if (!mBound) { - // Something seriously wrong since we are not bound and we are not - // waiting for connection. Bail out. - Slog.e(TAG, "Cannot bind to media container service"); - for (HandlerParams params : mPendingInstalls) { - // Indicate service bind error - params.serviceError(); - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", - System.identityHashCode(params)); - if (params.traceMethod != null) { - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, - params.traceMethod, params.traceCookie); - } - } - mPendingInstalls.clear(); - } else { - Slog.w(TAG, "Waiting to connect to media container service"); - } - } else if (mPendingInstalls.size() > 0) { - HandlerParams params = mPendingInstalls.get(0); - if (params != null) { - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", - System.identityHashCode(params)); - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy"); - if (params.startCopy()) { - // We are done... look for more work or to - // go idle. - if (DEBUG_SD_INSTALL) Log.i(TAG, - "Checking for more work or unbind..."); - // Delete pending install - if (mPendingInstalls.size() > 0) { - mPendingInstalls.remove(0); - } - if (mPendingInstalls.size() == 0) { - if (mBound) { - if (DEBUG_SD_INSTALL) Log.i(TAG, - "Posting delayed MCS_UNBIND"); - removeMessages(MCS_UNBIND); - Message ubmsg = obtainMessage(MCS_UNBIND); - // Unbind after a little delay, to avoid - // continual thrashing. - sendMessageDelayed(ubmsg, 10000); - } - } else { - // There are more pending requests in queue. - // Just post MCS_BOUND message to trigger processing - // of next pending install. - if (DEBUG_SD_INSTALL) Log.i(TAG, - "Posting MCS_BOUND for next work"); - mHandler.sendEmptyMessage(MCS_BOUND); - } - } - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } else { - // Should never happen ideally. - Slog.w(TAG, "Empty queue"); + if (params != null) { + if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params); + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", + System.identityHashCode(params)); + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy"); + params.startCopy(); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } break; } - case MCS_RECONNECT: { - if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect"); - if (mPendingInstalls.size() > 0) { - if (mBound) { - disconnectService(); - } - if (!connectToService()) { - Slog.e(TAG, "Failed to bind to media container service"); - for (HandlerParams params : mPendingInstalls) { - // Indicate service bind error - params.serviceError(); - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", - System.identityHashCode(params)); - } - mPendingInstalls.clear(); - } - } - break; - } - case MCS_UNBIND: { - // If there is no actual work left, then time to unbind. - if (DEBUG_INSTALL) Slog.i(TAG, "mcs_unbind"); - - if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) { - if (mBound) { - if (DEBUG_INSTALL) Slog.i(TAG, "calling disconnectService()"); - - disconnectService(); - } - } else if (mPendingInstalls.size() > 0) { - // There are more pending requests in queue. - // Just post MCS_BOUND message to trigger processing - // of next pending install. - mHandler.sendEmptyMessage(MCS_BOUND); - } - - break; - } - case MCS_GIVE_UP: { - if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries"); - HandlerParams params = mPendingInstalls.remove(0); - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", - System.identityHashCode(params)); - break; - } case SEND_PENDING_BROADCAST: { String packages[]; ArrayList<String> components[]; @@ -1685,11 +1473,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_ALLOW, user); - try { - ret = args.copyApk(mContainerService, true); - } catch (RemoteException e) { - Slog.e(TAG, "Could not contact the ContainerService"); - } + ret = args.copyApk(); } else { broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_REJECT, user); @@ -1699,7 +1483,6 @@ public class PackageManagerService extends IPackageManager.Stub TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); processPendingInstall(args, ret); - mHandler.sendEmptyMessage(MCS_UNBIND); } break; } @@ -1724,14 +1507,9 @@ public class PackageManagerService extends IPackageManager.Stub int ret; if (state.isInstallAllowed()) { - ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; broadcastPackageVerified(verificationId, originUri, response.code, state.getInstallArgs().getUser()); - try { - ret = args.copyApk(mContainerService, true); - } catch (RemoteException e) { - Slog.e(TAG, "Could not contact the ContainerService"); - } + ret = args.copyApk(); } else { ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; } @@ -1740,7 +1518,6 @@ public class PackageManagerService extends IPackageManager.Stub TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); processPendingInstall(args, ret); - mHandler.sendEmptyMessage(MCS_UNBIND); } break; @@ -12430,14 +12207,6 @@ public class PackageManagerService extends IPackageManager.Stub return installReason; } - /** - * Attempts to bind to the default container service explicitly instead of doing so lazily on - * install commit. - */ - void earlyBindToDefContainer() { - mHandler.sendMessage(mHandler.obtainMessage(DEF_CONTAINER_BIND)); - } - void installStage(String packageName, File stagedDir, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, @@ -13784,14 +13553,6 @@ public class PackageManagerService extends IPackageManager.Stub } private abstract class HandlerParams { - private static final int MAX_RETRIES = 4; - - /** - * Number of times startCopy() has been attempted and had a non-fatal - * error. - */ - private int mRetries = 0; - /** User handle for the user requesting the information or installation. */ private final UserHandle mUser; String traceMethod; @@ -13815,37 +13576,13 @@ public class PackageManagerService extends IPackageManager.Stub return this; } - final boolean startCopy() { - boolean res; - try { - if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this); - - if (++mRetries > MAX_RETRIES) { - Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); - mHandler.sendEmptyMessage(MCS_GIVE_UP); - handleServiceError(); - return false; - } else { - handleStartCopy(); - res = true; - } - } catch (RemoteException e) { - if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT"); - mHandler.sendEmptyMessage(MCS_RECONNECT); - res = false; - } - handleReturnCode(); - return res; - } - - final void serviceError() { - if (DEBUG_INSTALL) Slog.i(TAG, "serviceError"); - handleServiceError(); + final void startCopy() { + if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this); + handleStartCopy(); handleReturnCode(); } - abstract void handleStartCopy() throws RemoteException; - abstract void handleServiceError(); + abstract void handleStartCopy(); abstract void handleReturnCode(); } @@ -14088,7 +13825,7 @@ public class PackageManagerService extends IPackageManager.Stub * policy if needed and then create install arguments based * on the install location. */ - public void handleStartCopy() throws RemoteException { + public void handleStartCopy() { int ret = PackageManager.INSTALL_SUCCEEDED; // If we're already staged, we've firmly committed to an install location @@ -14114,8 +13851,8 @@ public class PackageManagerService extends IPackageManager.Stub Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { - pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, - packageAbiOverride); + pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, + origin.resolvedPath, installFlags, packageAbiOverride); if (DEBUG_INSTANT && ephemeral) { Slog.v(TAG, "pkgLite for install: " + pkgLite); @@ -14132,15 +13869,16 @@ public class PackageManagerService extends IPackageManager.Stub final long lowThreshold = storage.getStorageLowBytes( Environment.getDataDirectory()); - final long sizeBytes = mContainerService.calculateInstalledSize( + final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize( origin.resolvedPath, packageAbiOverride); - - try { - mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0); - pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, - installFlags, packageAbiOverride); - } catch (InstallerException e) { - Slog.w(TAG, "Failed to free cache", e); + if (sizeBytes >= 0) { + try { + mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0); + pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, + origin.resolvedPath, installFlags, packageAbiOverride); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to free cache", e); + } } /* @@ -14351,7 +14089,7 @@ public class PackageManagerService extends IPackageManager.Stub * No package verification is enabled, so immediately start * the remote call to initiate copy using temporary file. */ - ret = args.copyApk(mContainerService, true); + ret = args.copyApk(); } } @@ -14360,19 +14098,10 @@ public class PackageManagerService extends IPackageManager.Stub @Override void handleReturnCode() { - // If mArgs is null, then MCS couldn't be reached. When it - // reconnects, it will try again to install. At that point, this - // will succeed. if (mArgs != null) { processPendingInstall(mArgs, mRet); } } - - @Override - void handleServiceError() { - mArgs = createInstallArgs(this); - mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - } } private InstallArgs createInstallArgs(InstallParams params) { @@ -14439,7 +14168,7 @@ public class PackageManagerService extends IPackageManager.Stub this.installReason = installReason; } - abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException; + abstract int copyApk(); abstract int doPreInstall(int status); /** @@ -14547,16 +14276,16 @@ public class PackageManagerService extends IPackageManager.Stub this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; } - int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { + int copyApk() { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk"); try { - return doCopyApk(imcs, temp); + return doCopyApk(); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } - private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { + private int doCopyApk() { if (origin.staged) { if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy"); codeFile = origin.file; @@ -14575,25 +14304,8 @@ public class PackageManagerService extends IPackageManager.Stub return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } - final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() { - @Override - public ParcelFileDescriptor open(String name, int mode) throws RemoteException { - if (!FileUtils.isValidExtFilename(name)) { - throw new IllegalArgumentException("Invalid filename: " + name); - } - try { - final File file = new File(codeFile, name); - final FileDescriptor fd = Os.open(file.getAbsolutePath(), - O_RDWR | O_CREAT, 0644); - Os.chmod(file.getAbsolutePath(), 0644); - return new ParcelFileDescriptor(fd); - } catch (ErrnoException e) { - throw new RemoteException("Failed to open: " + e.getMessage()); - } - } - }; - - int ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); + int ret = PackageManagerServiceUtils.copyPackage( + origin.file.getAbsolutePath(), codeFile); if (ret != PackageManager.INSTALL_SUCCEEDED) { Slog.e(TAG, "Failed to copy package"); return ret; @@ -14754,7 +14466,7 @@ public class PackageManagerService extends IPackageManager.Stub params.installReason); } - int copyApk(IMediaContainerService imcs, boolean temp) { + int copyApk() { if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from " + move.fromUuid + " to " + move.toUuid); synchronized (mInstaller) { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 390c0ccb3c6e..36948fce4655 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -18,23 +18,21 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; +import static android.system.OsConstants.O_CREAT; +import static android.system.OsConstants.O_RDWR; + import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; import static com.android.server.pm.PackageManagerService.STUB_SUFFIX; import static com.android.server.pm.PackageManagerService.TAG; -import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; - -import com.android.internal.content.NativeLibraryHelper; -import com.android.internal.util.FastPrintWriter; -import com.android.server.EventLogTags; -import com.android.server.pm.dex.DexManager; -import com.android.server.pm.dex.PackageDexUsage; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; +import android.content.Context; import android.content.Intent; +import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; @@ -53,18 +51,24 @@ import android.system.ErrnoException; import android.system.Os; import android.util.ArraySet; import android.util.Log; -import android.util.PackageUtils; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.content.PackageHelper; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastPrintWriter; +import com.android.server.EventLogTags; +import com.android.server.pm.dex.DexManager; +import com.android.server.pm.dex.PackageDexUsage; + import dalvik.system.VMRuntime; import libcore.io.IoUtils; -import libcore.io.Libcore; -import libcore.io.Streams; import java.io.BufferedReader; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; @@ -73,8 +77,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.text.SimpleDateFormat; @@ -710,4 +712,120 @@ public class PackageManagerServiceUtils { final File[] compressedFiles = getCompressedFiles(codePath); return compressedFiles != null && compressedFiles.length > 0; } + + /** + * Parse given package and return minimal details. + */ + public static PackageInfoLite getMinimalPackageInfo(Context context, String packagePath, + int flags, String abiOverride) { + final PackageInfoLite ret = new PackageInfoLite(); + if (packagePath == null) { + Slog.i(TAG, "Invalid package file " + packagePath); + ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; + return ret; + } + + final File packageFile = new File(packagePath); + final PackageParser.PackageLite pkg; + final long sizeBytes; + try { + pkg = PackageParser.parsePackageLite(packageFile, 0); + sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride); + } catch (PackageParserException | IOException e) { + Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e); + + if (!packageFile.exists()) { + ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI; + } else { + ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; + } + + return ret; + } + + final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context, + pkg.packageName, pkg.installLocation, sizeBytes, flags); + + ret.packageName = pkg.packageName; + ret.splitNames = pkg.splitNames; + ret.versionCode = pkg.versionCode; + ret.versionCodeMajor = pkg.versionCodeMajor; + ret.baseRevisionCode = pkg.baseRevisionCode; + ret.splitRevisionCodes = pkg.splitRevisionCodes; + ret.installLocation = pkg.installLocation; + ret.verifiers = pkg.verifiers; + ret.recommendedInstallLocation = recommendedInstallLocation; + ret.multiArch = pkg.multiArch; + + return ret; + } + + /** + * Calculate estimated footprint of given package post-installation. + * + * @return -1 if there's some error calculating the size, otherwise installed size of the + * package. + */ + public static long calculateInstalledSize(String packagePath, String abiOverride) { + final File packageFile = new File(packagePath); + final PackageParser.PackageLite pkg; + try { + pkg = PackageParser.parsePackageLite(packageFile, 0); + return PackageHelper.calculateInstalledSize(pkg, abiOverride); + } catch (PackageParserException | IOException e) { + Slog.w(TAG, "Failed to calculate installed size: " + e); + return -1; + } + } + + /** + * Copy package to the target location. + * + * @param packagePath absolute path to the package to be copied. Can be + * a single monolithic APK file or a cluster directory + * containing one or more APKs. + * @return returns status code according to those in + * {@link PackageManager} + */ + public static int copyPackage(String packagePath, File targetDir) { + if (packagePath == null) { + return PackageManager.INSTALL_FAILED_INVALID_URI; + } + + try { + final File packageFile = new File(packagePath); + final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0); + copyFile(pkg.baseCodePath, targetDir, "base.apk"); + if (!ArrayUtils.isEmpty(pkg.splitNames)) { + for (int i = 0; i < pkg.splitNames.length; i++) { + copyFile(pkg.splitCodePaths[i], targetDir, + "split_" + pkg.splitNames[i] + ".apk"); + } + } + return PackageManager.INSTALL_SUCCEEDED; + } catch (PackageParserException | IOException | ErrnoException e) { + Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } + } + + private static void copyFile(String sourcePath, File targetDir, String targetName) + throws ErrnoException, IOException { + if (!FileUtils.isValidExtFilename(targetName)) { + throw new IllegalArgumentException("Invalid filename: " + targetName); + } + Slog.d(TAG, "Copying " + sourcePath + " to " + targetName); + + final File targetFile = new File(targetDir, targetName); + final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(), + O_RDWR | O_CREAT, 0644); + Os.chmod(targetFile.getAbsolutePath(), 0644); + FileInputStream source = null; + try { + source = new FileInputStream(sourcePath); + FileUtils.copy(source.getFD(), targetFd); + } finally { + IoUtils.closeQuietly(source); + } + } } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 5befc1f99cf3..846c7b7a7e0c 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -63,7 +63,6 @@ import android.util.Xml; import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; -import com.android.server.pm.PackageManagerService; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -455,13 +454,6 @@ public final class DefaultPermissionGrantPolicy { grantRuntimePermissions(storagePackage, STORAGE_PERMISSIONS, true, userId); } - // Container service - PackageParser.Package containerPackage = getSystemPackage( - PackageManagerService.DEFAULT_CONTAINER_PACKAGE); - if (containerPackage != null) { - grantRuntimePermissions(containerPackage, STORAGE_PERMISSIONS, true, userId); - } - // CertInstaller Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION); PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackage( diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a31f5b3ed31c..5272b663c2aa 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3734,6 +3734,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP windowInfo.focused = isFocused(); Task task = getTask(); windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode(); + windowInfo.hasFlagWatchOutsideTouch = + (mAttrs.flags & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0; if (mIsChildWindow) { windowInfo.parentToken = getParentWindow().mClient.asBinder(); diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java index ccc092d3b0e7..0176dd496a33 100644 --- a/services/net/java/android/net/ip/IpClient.java +++ b/services/net/java/android/net/ip/IpClient.java @@ -228,6 +228,9 @@ public class IpClient extends StateMachine { // Encourages logging of any available arguments, and all call sites // are necessarily logged identically. // + // NOTE: Log first because passed objects may or may not be thread-safe and + // once passed on to the callback they may be modified by another thread. + // // TODO: Find an lighter weight approach. private class LoggingCallbackWrapper extends Callback { private static final String PREFIX = "INVOKE "; @@ -243,63 +246,63 @@ public class IpClient extends StateMachine { @Override public void onPreDhcpAction() { - mCallback.onPreDhcpAction(); log("onPreDhcpAction()"); + mCallback.onPreDhcpAction(); } @Override public void onPostDhcpAction() { - mCallback.onPostDhcpAction(); log("onPostDhcpAction()"); + mCallback.onPostDhcpAction(); } @Override public void onNewDhcpResults(DhcpResults dhcpResults) { - mCallback.onNewDhcpResults(dhcpResults); log("onNewDhcpResults({" + dhcpResults + "})"); + mCallback.onNewDhcpResults(dhcpResults); } @Override public void onProvisioningSuccess(LinkProperties newLp) { - mCallback.onProvisioningSuccess(newLp); log("onProvisioningSuccess({" + newLp + "})"); + mCallback.onProvisioningSuccess(newLp); } @Override public void onProvisioningFailure(LinkProperties newLp) { - mCallback.onProvisioningFailure(newLp); log("onProvisioningFailure({" + newLp + "})"); + mCallback.onProvisioningFailure(newLp); } @Override public void onLinkPropertiesChange(LinkProperties newLp) { - mCallback.onLinkPropertiesChange(newLp); log("onLinkPropertiesChange({" + newLp + "})"); + mCallback.onLinkPropertiesChange(newLp); } @Override public void onReachabilityLost(String logMsg) { - mCallback.onReachabilityLost(logMsg); log("onReachabilityLost(" + logMsg + ")"); + mCallback.onReachabilityLost(logMsg); } @Override public void onQuit() { - mCallback.onQuit(); log("onQuit()"); + mCallback.onQuit(); } @Override public void installPacketFilter(byte[] filter) { - mCallback.installPacketFilter(filter); log("installPacketFilter(byte[" + filter.length + "])"); + mCallback.installPacketFilter(filter); } @Override public void startReadPacketFilter() { - mCallback.startReadPacketFilter(); log("startReadPacketFilter()"); + mCallback.startReadPacketFilter(); } @Override public void setFallbackMulticastFilter(boolean enabled) { - mCallback.setFallbackMulticastFilter(enabled); log("setFallbackMulticastFilter(" + enabled + ")"); + mCallback.setFallbackMulticastFilter(enabled); } @Override public void setNeighborDiscoveryOffload(boolean enable) { - mCallback.setNeighborDiscoveryOffload(enable); log("setNeighborDiscoveryOffload(" + enable + ")"); + mCallback.setNeighborDiscoveryOffload(enable); } } diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk index 2691701f79af..8b5977144db1 100644 --- a/services/robotests/Android.mk +++ b/services/robotests/Android.mk @@ -75,6 +75,7 @@ LOCAL_AIDL_INCLUDES := \ LOCAL_STATIC_JAVA_LIBRARIES := \ platform-robolectric-android-all-stubs \ android-support-test \ + guava \ mockito-robolectric-prebuilt \ platform-test-annotations \ truth-prebuilt \ diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java new file mode 100644 index 000000000000..3b6e038ec8cb --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java @@ -0,0 +1,101 @@ +/* + * 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.backup.encryption.chunk; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; +import com.android.server.testing.FrameworkRobolectricTestRunner; +import com.android.server.testing.SystemLoaderPackages; +import com.google.common.primitives.Bytes; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +@RunWith(FrameworkRobolectricTestRunner.class) +@Config(manifest = Config.NONE, sdk = 26) +@SystemLoaderPackages({"com.android.server.backup"}) +@Presubmit +public class ChunkHashTest { + private static final int HASH_LENGTH_BYTES = 256 / 8; + private static final byte[] TEST_HASH_1 = Arrays.copyOf(new byte[] {1}, HASH_LENGTH_BYTES); + private static final byte[] TEST_HASH_2 = Arrays.copyOf(new byte[] {2}, HASH_LENGTH_BYTES); + + @Test + public void testGetHash_returnsHash() { + ChunkHash chunkHash = new ChunkHash(TEST_HASH_1); + + byte[] hash = chunkHash.getHash(); + + assertThat(hash).asList().containsExactlyElementsIn(Bytes.asList(TEST_HASH_1)).inOrder(); + } + + @Test + public void testEquals() { + ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); + ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1); + ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); + + assertThat(chunkHash1).isEqualTo(equalChunkHash1); + assertThat(chunkHash1).isNotEqualTo(chunkHash2); + } + + @Test + public void testHashCode() { + ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); + ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1); + ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); + + int hash1 = chunkHash1.hashCode(); + int equalHash1 = equalChunkHash1.hashCode(); + int hash2 = chunkHash2.hashCode(); + + assertThat(hash1).isEqualTo(equalHash1); + assertThat(hash1).isNotEqualTo(hash2); + } + + @Test + public void testCompareTo_whenEqual_returnsZero() { + ChunkHash chunkHash = new ChunkHash(TEST_HASH_1); + ChunkHash equalChunkHash = new ChunkHash(TEST_HASH_1); + + int result = chunkHash.compareTo(equalChunkHash); + + assertThat(result).isEqualTo(0); + } + + @Test + public void testCompareTo_whenArgumentGreater_returnsNegative() { + ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); + ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); + + int result = chunkHash1.compareTo(chunkHash2); + + assertThat(result).isLessThan(0); + } + + @Test + public void testCompareTo_whenArgumentSmaller_returnsPositive() { + ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); + ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); + + int result = chunkHash2.compareTo(chunkHash1); + + assertThat(result).isGreaterThan(0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index eb28e1ae2f23..1eb88baafa48 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -100,6 +100,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; @@ -365,7 +366,7 @@ public class NetworkPolicyManagerServiceTest { return null; } }).when(mActivityManager).registerUidObserver(any(), anyInt(), - eq(ActivityManager.PROCESS_STATE_UNKNOWN), isNull(String.class)); + eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), isNull(String.class)); mFutureIntent = newRestrictBackgroundChangedFuture(); mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java index e6ca03b17092..48dda019f570 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -152,8 +152,7 @@ public class BrightnessMappingStrategyTest { final float[] lux = { 0f, 1f }; final float[] nits = { 0, PowerManager.BRIGHTNESS_ON }; - BrightnessConfiguration config = new BrightnessConfiguration.Builder() - .setCurve(lux, nits) + BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) .build(); strategy.setBrightnessConfiguration(config); assertNotEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/); @@ -214,8 +213,7 @@ public class BrightnessMappingStrategyTest { DISPLAY_RANGE_NITS[DISPLAY_RANGE_NITS.length - 1] }; - BrightnessConfiguration config = new BrightnessConfiguration.Builder() - .setCurve(lux, nits) + BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) .build(); strategy.setBrightnessConfiguration(config); assertEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/); diff --git a/startop/OWNERS b/startop/OWNERS new file mode 100644 index 000000000000..bfe96d3ed15c --- /dev/null +++ b/startop/OWNERS @@ -0,0 +1,5 @@ +# mailing list: startop-eng@google.com +chriswailes@google.com +eholk@google.com +iam@google.com +sehr@google.com diff --git a/startop/scripts/app_startup/analyze_metrics.py b/startop/scripts/app_startup/analyze_metrics.py new file mode 100755 index 000000000000..d74d6f68d823 --- /dev/null +++ b/startop/scripts/app_startup/analyze_metrics.py @@ -0,0 +1,457 @@ +#!/usr/bin/env python3 +# +# 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. + +""" +Perform statistical analysis on measurements produced by app_startup_runner.py + +Install: +$> sudo apt-get install python3-scipy + +Usage: +$> ./analyze_metrics.py <filename.csv> [<filename2.csv> ...] +$> ./analyze_metrics.py --help +""" + +import argparse +import csv +import itertools +import os +import subprocess +import sys +import tempfile +from typing import Any, List, Dict, Iterable, TextIO, Tuple + +from scipy import stats as sc +import numpy as np + + +# These CSV columns are considered labels. Everything after them in the same row are metrics. +_LABEL_COLUMNS=['packages', 'readaheads', 'compiler_filters'] +# The metric series with the 'cold' readahead is the baseline. +# All others (warm, jit, etc) are the potential improvements. + +#fixme: this should probably be an option +_BASELINE=('readaheads', 'cold') +# ignore this for some statistic calculations +_IGNORE_PAIR=('readaheads', 'warm') +_PLOT_SUBKEY='readaheads' +_PLOT_GROUPKEY='packages' +_PLOT_DATA_INDEX = 0 +_DELTA=50 +_DELTA2=100 +_PVALUE_THRESHOLD=0.10 +_debug = False # See -d/--debug flag. + +def parse_options(argv: List[str] = None): + """Parse command line arguments and return an argparse Namespace object.""" + parser = argparse.ArgumentParser(description="Perform statistical analysis on measurements produced by app_start_runner.py.") + parser.add_argument('input_files', metavar='file.csv', nargs='+', help='CSV file produced by app_startup_runner.py') + + parser.add_argument('-d', '--debug', dest='debug', action='store_true', help='Add extra debugging output') + parser.add_argument('-os', '--output-samples', dest='output_samples', default='/dev/null', action='store', help='Store CSV for per-sample data') + parser.add_argument('-oc', '--output-comparable', dest='output_comparable', default='/dev/null', action='store', help='Output CSV for comparable against baseline') + parser.add_argument('-ocs', '--output-comparable-significant', dest='output_comparable_significant', default='/dev/null', action='store', help='Output CSV for comparable against baseline (significant only)') + parser.add_argument('-pt', '--pvalue-threshold', dest='pvalue_threshold', type=float, default=_PVALUE_THRESHOLD, action='store') + parser.add_argument('-dt', '--delta-threshold', dest='delta_threshold', type=int, default=_DELTA, action='store') + + return parser.parse_args(argv) + +def _debug_print(*args, **kwargs): + """Print the args to sys.stderr if the --debug/-d flag was passed in.""" + global _debug + if _debug: + print(*args, **kwargs, file=sys.stderr) + +def _expand_gen_repr(args): + new_args_list = [] + for i in args: + # detect iterable objects that do not have their own override of __str__ + if hasattr(i, '__iter__'): + to_str = getattr(i, '__str__') + if to_str.__objclass__ == object: + # the repr for a generator is just type+address, expand it out instead. + new_args_list.append([_expand_gen_repr([j])[0] for j in i]) + continue + # normal case: uses the built-in to-string + new_args_list.append(i) + return new_args_list + +def _debug_print_gen(*args, **kwargs): + """Like _debug_print but will turn any iterable args into a list.""" + if not _debug: + return + + new_args_list = _expand_gen_repr(args) + _debug_print(*new_args_list, **kwargs) + +def read_headers(input_file: TextIO) -> Tuple[List[str], List[str]]: + _debug_print("read_headers for file: ", input_file.name) + csv_reader = csv.reader(input_file) + + label_num_columns = len(_LABEL_COLUMNS) + + try: + header = next(csv_reader) + except StopIteration: + header = None + _debug_print('header', header) + + if not header: + return (None, None) + + labels = header[0:label_num_columns] + data = header[label_num_columns:] + + return (labels, data) + +def read_labels_and_data(input_file: TextIO) -> Iterable[Tuple[List[str], List[int]]]: + _debug_print("print_analysis for file: ", input_file.name) + csv_reader = csv.reader(input_file) + + # Skip the header because it doesn't contain any data. + # To get the header see read_headers function. + try: + header = next(csv_reader) + except StopIteration: + header = None + + label_num_columns = len(_LABEL_COLUMNS) + + for row in csv_reader: + if len(row) > 0 and row[0][0] == ';': + _debug_print("skip comment line", row) + continue + + labels = row[0:label_num_columns] + data = [int(i) for i in row[label_num_columns:]] + +# _debug_print("labels:", labels) +# _debug_print("data:", data) + + yield (labels, data) + +def group_metrics_by_label(it: Iterable[Tuple[List[str], List[int]]]): + prev_labels = None + data_2d = [] + + for label_list, data_list in it: + if prev_labels != label_list: + if prev_labels: +# _debug_print("grouped labels:", prev_labels, "data_2d:", data_2d) + yield (prev_labels, data_2d) + data_2d = [] + + data_2d.append(data_list) + prev_labels = label_list + + if prev_labels: +# _debug_print("grouped labels:", prev_labels, "data_2d:", data_2d) + yield (prev_labels, data_2d) + +def data_to_numpy(it: Iterable[Tuple[List[str], List[List[int]]]]) -> Iterable[Tuple[List[str], Any]]: + for label_list, data_2d in it: + yield (label_list, np.asarray(data_2d, dtype=int)) + +def iterate_columns(np_data_2d): + for col in range(np_data_2d.shape[1]): + col_as_array = np_data_2d[:, col] + yield col_as_array + +def confidence_interval(np_data_2d, percent=0.95): + """ + Given some data [[a,b,c],[d,e,f,]...] + + We assume the same metric is in the column (e.g. [a,d]) + and that data in the rows (e.g. [b,e]) are separate metric values. + + We then calculate the CI for each metric individually returning it as a list of tuples. + """ + arr = [] + for col_2d in iterate_columns(np_data_2d): + mean = col_2d.mean() + sigma = col_2d.std() + + ci = sc.norm.interval(percent, loc=mean, scale=sigma / np.sqrt(len(col_2d))) + arr.append(ci) + + # TODO: This seems to be returning NaN when all the samples have the same exact value + # (e.g. stddev=0, which can trivially happen when sample count = 1). + + return arr + +def print_analysis(it, label_header: List[str], data_header: List[str], output_samples: str): + print(label_header) + + with open(output_samples, "w") as output_file: + + csv_writer = csv.writer(output_file) + csv_writer.writerow(label_header + ['mean', 'std', 'confidence_interval_a', 'confidence_interval_b']) + + for label_list, np_data_2d in it: + print("**********************") + print(label_list) + print() + print(" ", data_header) + # aggregate computation column-wise + print("Mean: ", np_data_2d.mean(axis=0)) + print("Std: ", np_data_2d.std(axis=0)) + print("CI95%:", confidence_interval(np_data_2d)) + print("SEM: ", stats_standard_error_one(np_data_2d, axis=0)) + + #ci = confidence_interval(np_data_2d)[_PLOT_DATA_INDEX] + sem = stats_standard_error_one(np_data_2d, axis=0)[_PLOT_DATA_INDEX] + mean = np_data_2d.mean(axis=0)[_PLOT_DATA_INDEX] + + ci = (mean - sem, mean + sem) + + csv_writer.writerow(label_list + [mean, np_data_2d.std(axis=0)[_PLOT_DATA_INDEX], ci[0], ci[1]]) + +def from_file_group_by_labels(input_file): + (label_header, data_header) = read_headers(input_file) + label_data_iter = read_labels_and_data(input_file) + grouped_iter = group_metrics_by_label(label_data_iter) + grouped_numpy_iter = data_to_numpy(grouped_iter) + + return grouped_numpy_iter, label_header, data_header + +def list_without_index(list, index): + return list[:index] + list[index+1:] + +def group_by_without_baseline_key(grouped_numpy_iter, label_header): + """ + Data is considered comparable if the only difference is the baseline key + (i.e. the readahead is different but the package, compilation filter, etc, are the same). + + Returns iterator that's grouped by the non-baseline labels to an iterator of + (label_list, data_2d). + """ + baseline_index = label_header.index(_BASELINE[0]) + + def get_label_without_baseline(tpl): + label_list, _ = tpl + return list_without_index(label_list, baseline_index) + # [['pkgname', 'compfilter', 'warm'], [data]] + # [['pkgname', 'compfilter', 'cold'], [data2]] + # [['pkgname2', 'compfilter', 'warm'], [data3]] + # + # -> + # ( [['pkgname', 'compfilter', 'warm'], [data]] # ignore baseline label change. + # [['pkgname', 'compfilter', 'cold'], [data2]] ), # split here because the pkgname changed. + # ( [['pkgname2', 'compfilter', 'warm'], [data3]] ) + for group_info, it in itertools.groupby(grouped_numpy_iter, key = get_label_without_baseline): + yield it + + # TODO: replace this messy manual iteration/grouping with pandas + +def iterate_comparable_metrics(without_baseline_iter, label_header): + baseline_index = label_header.index(_BASELINE[0]) + baseline_value = _BASELINE[1] + + _debug_print("iterate comparables") + + def is_baseline_fun(tp): + ll, dat = tp + return ll[baseline_index] == baseline_value + + # iterating here when everything but the baseline key is the same. + for it in without_baseline_iter: + it1, it2 = itertools.tee(it) + + # find all the baseline data. + baseline_filter_it = filter(is_baseline_fun, it1) + + # find non-baseline data. + nonbaseline_filter_it = itertools.filterfalse(is_baseline_fun, it2) + + yield itertools.product(baseline_filter_it, nonbaseline_filter_it) + +def stats_standard_error_one(a, axis): + a_std = a.std(axis=axis, ddof=0) + a_len = a.shape[axis] + + return a_std / np.sqrt(a_len) + +def stats_standard_error(a, b, axis): + a_std = a.std(axis=axis, ddof=0) + b_std = b.std(axis=axis, ddof=0) + + a_len = a.shape[axis] + b_len = b.shape[axis] + + temp1 = a_std*a_std/a_len + temp2 = b_std*b_std/b_len + + return np.sqrt(temp1 + temp2) + +def stats_tvalue(a, b, axis, delta = 0): + a_mean = a.mean(axis=axis) + b_mean = b.mean(axis=axis) + + return (a_mean - b_mean - delta) / stats_standard_error(a, b, axis) + +def stats_pvalue(a, b, axis, delta, left:bool = False): + """ + Single-tailed 2-sample t-test. + + Returns p-value for the null hypothesis: mean(a) - mean(b) >= delta. + :param a: numpy 2d array + :param b: numpy 2d array + :param axis: which axis to do the calculations across + :param delta: test value of mean differences + :param left: if true then use <= delta instead of >= delta + :return: p-value + """ + # implement our own pvalue calculation because the built-in t-test (t,p values) + # only offer delta=0 , e.g. m1-m1 ? 0 + # we are however interested in m1-m2 ? delta + t_value = stats_tvalue(a, b, axis, delta) + + # 2-sample degrees of freedom is using the array sizes - 2. + dof = a.shape[axis] + b.shape[axis] - 2 + + if left: + # left tailed test. e.g. m1-m2 <= delta + return sc.t.cdf(t_value, dof) + else: + # right tailed test. e.g. m1-m2 >= delta + return sc.t.sf(t_value, dof) + # a left+right tailed test is a 2-tail t-test and can be done using ttest_ind for delta=0 + +def print_comparable_analysis(comparable_metrics_iter, label_header, data_header, output_comparable: str, output_comparable_significant: str): + baseline_value = _BASELINE[1] + baseline_index = label_header.index(_BASELINE[0]) + + old_baseline_label_list = None + delta = _DELTA + filter_value = _IGNORE_PAIR[1] + filter_index = label_header.index(_IGNORE_PAIR[0]) + + pvalue_threshold = _PVALUE_THRESHOLD + ci_threshold = (1 - _PVALUE_THRESHOLD) * 100.0 + + with open(output_comparable, "w") as output_file: + + csv_writer = csv.writer(output_file) + csv_writer.writerow(label_header + ['mean', 'mean_diff', 'sem', 'pvalue_2tailed', 'pvalue_gt%d' %(_DELTA), 'pvalue_gt%d' %(_DELTA2)]) + + print("------------------------------------------------------------------") + print("Comparison against the baseline %s = %s" %(_BASELINE, baseline_value)) + print("--- Right-tailed t-test checks if the baseline >= current %s by at least %d" %(_BASELINE[0], delta)) + print() + + global_stats = {'better_than_delta': [], 'better_than_delta_p95': []} + + for nested_it in comparable_metrics_iter: + print("************************") + + better_than_delta = [] + better_than_delta_p95 = [] + + saw_baseline_once = False + + for ((baseline_label_list, baseline_np_data_2d), (rest_label_list, rest_np_data_2d)) in nested_it: + _debug_print("baseline_label_list:", baseline_label_list) + _debug_print("baseline_np_data_2d:", baseline_np_data_2d) + _debug_print("rest_label_list:", rest_label_list) + _debug_print("rest_np_data_2d:", rest_np_data_2d) + + mean_diff = baseline_np_data_2d.mean(axis=0) - rest_np_data_2d.mean(axis=0) + # 2-sample 2-tailed t-test with delta=0 + # e.g. "Is it true that usually the two sample means are different?" + t_statistic, t_pvalue = sc.ttest_ind(baseline_np_data_2d, rest_np_data_2d, axis=0) + + # 2-sample 1-tailed t-test with delta=50 + # e.g. "Is it true that usually the sample means better than 50ms?" + t2 = stats_tvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=delta) + p2 = stats_pvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=delta) + + t2_b = stats_tvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=_DELTA2) + p2_b = stats_pvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=_DELTA2) + + print("%s vs %s" %(rest_label_list, baseline_value)) + print(" ", data_header) + print("Mean Difference: ", mean_diff) + print("T-test (2-tailed) != 0: t=%s, p=%s" %(t_statistic, t_pvalue)) + print("T-test (right-tailed) >= %d: t=%s, p=%s" %(_DELTA, t2, p2)) + print("T-test (right-tailed) >= %d: t=%s, p=%s" %(_DELTA2, t2_b, p2_b)) + + def write_out_values(label_list, *args): + csv_writer.writerow(label_list + [i[_PLOT_DATA_INDEX] for i in args]) + + sem = stats_standard_error(baseline_np_data_2d, rest_np_data_2d, axis=0) + if saw_baseline_once == False: + saw_baseline_once = True + base_sem = stats_standard_error_one(baseline_np_data_2d, axis=0) + write_out_values(baseline_label_list, baseline_np_data_2d.mean(axis=0), [0], base_sem, [None], [None], [None]) + write_out_values(rest_label_list, rest_np_data_2d.mean(axis=0), mean_diff, sem, t_pvalue, p2, p2_b) + + # now do the global statistics aggregation + + if rest_label_list[filter_index] == filter_value: + continue + + if mean_diff > delta: + better_than_delta.append((mean_diff, p2, rest_label_list)) + + if p2 <= pvalue_threshold: + better_than_delta_p95.append((mean_diff, rest_label_list)) + + if better_than_delta: + global_stats['better_than_delta'].append(better_than_delta) + if better_than_delta_p95: + global_stats['better_than_delta_p95'].append(better_than_delta_p95) + + print("------------------------") + print("Global statistics:") + print("//// Rows with %s=%s are ignored here." %_IGNORE_PAIR) + print("- # of results with mean diff better than delta(%d) = %d" %(delta, len(global_stats['better_than_delta']))) + print(" > (meandiff, pvalue, labels)") + for i in global_stats['better_than_delta']: + print(" > %s" %i) + print("- # of results with mean diff better than delta(%d) CI%d%% = %d" %(delta, ci_threshold, len(global_stats['better_than_delta_p95']))) + print(" > (meandiff, labels)") + for i in global_stats['better_than_delta_p95']: + print(" > %s" %i) + +def main(): + global _debug + global _DELTA + global _PVALUE_THRESHOLD + + opts = parse_options() + _debug = opts.debug + _debug_print("parsed options: ", opts) + + _PVALUE_THRESHOLD = opts.pvalue_threshold or _PVALUE_THRESHOLD + + for file_name in opts.input_files: + with open(file_name, 'r') as input_file: + (grouped_numpy_iter, label_header, data_header) = from_file_group_by_labels(input_file) + print_analysis(grouped_numpy_iter, label_header, data_header, opts.output_samples) + + with open(file_name, 'r') as input_file: + (grouped_numpy_iter, label_header, data_header) = from_file_group_by_labels(input_file) + without_baseline_iter = group_by_without_baseline_key(grouped_numpy_iter, label_header) + #_debug_print_gen(without_baseline_iter) + + comparable_metrics_iter = iterate_comparable_metrics(without_baseline_iter, label_header) + print_comparable_analysis(comparable_metrics_iter, label_header, data_header, opts.output_comparable, opts.output_comparable_significant) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/startop/scripts/app_startup/app_startup_runner.py b/startop/scripts/app_startup/app_startup_runner.py new file mode 100755 index 000000000000..780bb4eaeeef --- /dev/null +++ b/startop/scripts/app_startup/app_startup_runner.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python3 +# +# 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. + +# +# +# Measure application start-up time by launching applications under various combinations. +# See --help for more details. +# +# +# Sample usage: +# $> ./app_startup_runner.py -p com.google.android.calculator -r warm -r cold -lc 10 -o out.csv +# $> ./analyze_metrics.py out.csv +# +# + +import argparse +import csv +import itertools +import os +import subprocess +import sys +import tempfile +from typing import Any, Callable, Dict, Generic, Iterable, List, NamedTuple, TextIO, Tuple, TypeVar, Optional, Union + +# The following command line options participate in the combinatorial generation. +# All other arguments have a global effect. +_COMBINATORIAL_OPTIONS=['packages', 'readaheads', 'compiler_filters'] +_TRACING_READAHEADS=['mlock', 'fadvise'] +_FORWARD_OPTIONS={'loop_count': '--count'} +_RUN_SCRIPT=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'run_app_with_prefetch') + +RunCommandArgs = NamedTuple('RunCommandArgs', [('package', str), ('readahead', str), ('compiler_filter', Optional[str])]) +CollectorPackageInfo = NamedTuple('CollectorPackageInfo', [('package', str), ('compiler_filter', str)]) +_COLLECTOR_SCRIPT=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'collector') +_COLLECTOR_TIMEOUT_MULTIPLIER = 2 # take the regular --timeout and multiply by 2; systrace starts up slowly. + +_UNLOCK_SCREEN_SCRIPT=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'unlock_screen') + +# This must be the only mutable global variable. All other global variables are constants to avoid magic literals. +_debug = False # See -d/--debug flag. +_DEBUG_FORCE = None # Ignore -d/--debug if this is not none. + +# Type hinting names. +T = TypeVar('T') +NamedTupleMeta = Callable[..., T] # approximation of a (S : NamedTuple<T> where S() == T) metatype. + +def parse_options(argv: List[str] = None): + """Parse command line arguments and return an argparse Namespace object.""" + parser = argparse.ArgumentParser(description="Run one or more Android applications under various settings in order to measure startup time.") + # argparse considers args starting with - and -- optional in --help, even though required=True. + # by using a named argument group --help will clearly say that it's required instead of optional. + required_named = parser.add_argument_group('required named arguments') + required_named.add_argument('-p', '--package', action='append', dest='packages', help='package of the application', required=True) + required_named.add_argument('-r', '--readahead', action='append', dest='readaheads', help='which readahead mode to use', choices=('warm', 'cold', 'mlock', 'fadvise'), required=True) + + # optional arguments + # use a group here to get the required arguments to appear 'above' the optional arguments in help. + optional_named = parser.add_argument_group('optional named arguments') + optional_named.add_argument('-c', '--compiler-filter', action='append', dest='compiler_filters', help='which compiler filter to use. if omitted it does not enforce the app\'s compiler filter', choices=('speed', 'speed-profile', 'quicken')) + optional_named.add_argument('-s', '--simulate', dest='simulate', action='store_true', help='Print which commands will run, but don\'t run the apps') + optional_named.add_argument('-d', '--debug', dest='debug', action='store_true', help='Add extra debugging output') + optional_named.add_argument('-o', '--output', dest='output', action='store', help='Write CSV output to file.') + optional_named.add_argument('-t', '--timeout', dest='timeout', action='store', type=int, help='Timeout after this many seconds when executing a single run.') + optional_named.add_argument('-lc', '--loop-count', dest='loop_count', default=1, type=int, action='store', help='How many times to loop a single run.') + optional_named.add_argument('-in', '--inodes', dest='inodes', type=str, action='store', help='Path to inodes file (system/extras/pagecache/pagecache.py -d inodes)') + + return parser.parse_args(argv) + +# TODO: refactor this with a common library file with analyze_metrics.py +def _debug_print(*args, **kwargs): + """Print the args to sys.stderr if the --debug/-d flag was passed in.""" + if _debug: + print(*args, **kwargs, file=sys.stderr) + +def _expand_gen_repr(args): + """Like repr but any generator-like object has its iterator consumed + and then called repr on.""" + new_args_list = [] + for i in args: + # detect iterable objects that do not have their own override of __str__ + if hasattr(i, '__iter__'): + to_str = getattr(i, '__str__') + if to_str.__objclass__ == object: + # the repr for a generator is just type+address, expand it out instead. + new_args_list.append([_expand_gen_repr([j])[0] for j in i]) + continue + # normal case: uses the built-in to-string + new_args_list.append(i) + return new_args_list + +def _debug_print_gen(*args, **kwargs): + """Like _debug_print but will turn any iterable args into a list.""" + if not _debug: + return + + new_args_list = _expand_gen_repr(args) + _debug_print(*new_args_list, **kwargs) + +def _debug_print_nd(*args, **kwargs): + """Like _debug_print but will turn any NamedTuple-type args into a string.""" + if not _debug: + return + + new_args_list = [] + for i in args: + if hasattr(i, '_field_types'): + new_args_list.append("%s: %s" %(i.__name__, i._field_types)) + else: + new_args_list.append(i) + + _debug_print(*new_args_list, **kwargs) + +def dict_lookup_any_key(dictionary: dict, *keys: List[Any]): + for k in keys: + if k in dictionary: + return dictionary[k] + raise KeyError("None of the keys %s were in the dictionary" %(keys)) + +def generate_run_combinations(named_tuple: NamedTupleMeta[T], opts_dict: Dict[str, List[Optional[str]]])\ + -> Iterable[T]: + """ + Create all possible combinations given the values in opts_dict[named_tuple._fields]. + + :type T: type annotation for the named_tuple type. + :param named_tuple: named tuple type, whose fields are used to make combinations for + :param opts_dict: dictionary of keys to value list. keys correspond to the named_tuple fields. + :return: an iterable over named_tuple instances. + """ + combinations_list = [] + for k in named_tuple._fields: + # the key can be either singular or plural , e.g. 'package' or 'packages' + val = dict_lookup_any_key(opts_dict, k, k + "s") + + # treat {'x': None} key value pairs as if it was [None] + # otherwise itertools.product throws an exception about not being able to iterate None. + combinations_list.append(val or [None]) + + _debug_print("opts_dict: ", opts_dict) + _debug_print_nd("named_tuple: ", named_tuple) + _debug_print("combinations_list: ", combinations_list) + + for combo in itertools.product(*combinations_list): + yield named_tuple(*combo) + +def key_to_cmdline_flag(key: str) -> str: + """Convert key into a command line flag, e.g. 'foo-bars' -> '--foo-bar' """ + if key.endswith("s"): + key = key[:-1] + return "--" + key.replace("_", "-") + +def as_run_command(tpl: NamedTuple) -> List[Union[str, Any]]: + """ + Convert a named tuple into a command-line compatible arguments list. + + Example: ABC(1, 2, 3) -> ['--a', 1, '--b', 2, '--c', 3] + """ + args = [] + for key, value in tpl._asdict().items(): + if value is None: + continue + args.append(key_to_cmdline_flag(key)) + args.append(value) + return args + +def generate_group_run_combinations(run_combinations: Iterable[NamedTuple], dst_nt: NamedTupleMeta[T])\ + -> Iterable[Tuple[T, Iterable[NamedTuple]]]: + + def group_by_keys(src_nt): + src_d = src_nt._asdict() + # now remove the keys that aren't legal in dst. + for illegal_key in set(src_d.keys()) - set(dst_nt._fields): + if illegal_key in src_d: + del src_d[illegal_key] + + return dst_nt(**src_d) + + for args_list_it in itertools.groupby(run_combinations, group_by_keys): + (group_key_value, args_it) = args_list_it + yield (group_key_value, args_it) + +def parse_run_script_csv_file(csv_file: TextIO) -> List[int]: + """Parse a CSV file full of integers into a flat int list.""" + csv_reader = csv.reader(csv_file) + arr = [] + for row in csv_reader: + for i in row: + if i: + arr.append(int(i)) + return arr + +def make_script_command_with_temp_output(script: str, args: List[str], **kwargs)\ + -> Tuple[str, TextIO]: + """ + Create a command to run a script given the args. + Appends --count <loop_count> --output <tmp-file-name>. + Returns a tuple (cmd, tmp_file) + """ + tmp_output_file = tempfile.NamedTemporaryFile(mode='r') + cmd = [script] + args + for key, value in kwargs.items(): + cmd += ['--%s' %(key), "%s" %(value)] + if _debug: + cmd += ['--verbose'] + cmd = cmd + ["--output", tmp_output_file.name] + return cmd, tmp_output_file + +def execute_arbitrary_command(cmd: List[str], simulate: bool, timeout: int) -> Tuple[bool, str]: + if simulate: + print(" ".join(cmd)) + return (True, "") + else: + _debug_print("[EXECUTE]", cmd) + proc = subprocess.Popen(cmd, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + universal_newlines=True) + try: + script_output = proc.communicate(timeout=timeout)[0] + except subprocess.TimeoutExpired: + print("[TIMEDOUT]") + proc.kill() + script_output = proc.communicate()[0] + + _debug_print("[STDOUT]", script_output) + return_code = proc.wait() + passed = (return_code == 0) + _debug_print("[$?]", return_code) + if not passed: + print("[FAILED, code:%s]" %(return_code), script_output, file=sys.stderr) + + return (passed, script_output) + +def execute_run_combos(grouped_run_combos: Iterable[Tuple[CollectorPackageInfo, Iterable[RunCommandArgs]]], simulate: bool, inodes_path: str, timeout: int, loop_count: int, need_trace: bool): + # nothing will work if the screen isn't unlocked first. + execute_arbitrary_command([_UNLOCK_SCREEN_SCRIPT], simulate, timeout) + + for collector_info, run_combos in grouped_run_combos: + #collector_args = ["--package", package_name] + collector_args = as_run_command(collector_info) + # TODO: forward --wait_time for how long systrace runs? + # TODO: forward --trace_buffer_size for size of systrace buffer size? + collector_cmd, collector_tmp_output_file = make_script_command_with_temp_output(_COLLECTOR_SCRIPT, collector_args, inodes=inodes_path) + + with collector_tmp_output_file: + collector_passed = True + if need_trace: + collector_timeout = timeout and _COLLECTOR_TIMEOUT_MULTIPLIER * timeout + (collector_passed, collector_script_output) = execute_arbitrary_command(collector_cmd, simulate, collector_timeout) + # TODO: consider to print a ; collector wrote file to <...> into the CSV file so we know it was ran. + + for combos in run_combos: + args = as_run_command(combos) + + cmd, tmp_output_file = make_script_command_with_temp_output(_RUN_SCRIPT, args, count=loop_count, input=collector_tmp_output_file.name) + with tmp_output_file: + (passed, script_output) = execute_arbitrary_command(cmd, simulate, timeout) + parsed_output = simulate and [1,2,3] or parse_run_script_csv_file(tmp_output_file) + yield (passed, script_output, parsed_output) + +def gather_results(commands: Iterable[Tuple[bool, str, List[int]]], key_list: List[str], value_list: List[Tuple[str, ...]]): + _debug_print("gather_results: key_list = ", key_list) + yield key_list + ["time(ms)"] + + stringify_none = lambda s: s is None and "<none>" or s + + for ((passed, script_output, run_result_list), values) in itertools.zip_longest(commands, value_list): + if not passed: + continue + for result in run_result_list: + yield [stringify_none(i) for i in values] + [result] + + yield ["; avg(%s), min(%s), max(%s), count(%s)" %(sum(run_result_list, 0.0) / len(run_result_list), min(run_result_list), max(run_result_list), len(run_result_list)) ] + +def eval_and_save_to_csv(output, annotated_result_values): + csv_writer = csv.writer(output) + for row in annotated_result_values: + csv_writer.writerow(row) + output.flush() # see the output live. + +def main(): + global _debug + + opts = parse_options() + _debug = opts.debug + if _DEBUG_FORCE is not None: + _debug = _DEBUG_FORCE + _debug_print("parsed options: ", opts) + need_trace = not not set(opts.readaheads).intersection(set(_TRACING_READAHEADS)) + if need_trace and not opts.inodes: + print("Error: Missing -in/--inodes, required when using a readahead of %s" %(_TRACING_READAHEADS), file=sys.stderr) + return 1 + + output_file = opts.output and open(opts.output, 'w') or sys.stdout + + combos = lambda: generate_run_combinations(RunCommandArgs, vars(opts)) + _debug_print_gen("run combinations: ", combos()) + + grouped_combos = lambda: generate_group_run_combinations(combos(), CollectorPackageInfo) + _debug_print_gen("grouped run combinations: ", grouped_combos()) + + exec = execute_run_combos(grouped_combos(), opts.simulate, opts.inodes, opts.timeout, opts.loop_count, need_trace) + results = gather_results(exec, _COMBINATORIAL_OPTIONS, combos()) + eval_and_save_to_csv(output_file, results) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/startop/scripts/app_startup/app_startup_runner_test.py b/startop/scripts/app_startup/app_startup_runner_test.py new file mode 100755 index 000000000000..f96f802a3aef --- /dev/null +++ b/startop/scripts/app_startup/app_startup_runner_test.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +# +# 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. +# + +""" +Unit tests for the app_startup_runner.py script. + +Install: + $> sudo apt-get install python3-pytest ## OR + $> pip install -U pytest +See also https://docs.pytest.org/en/latest/getting-started.html + +Usage: + $> ./app_startup_runner_test.py + $> pytest app_startup_runner_test.py + $> python -m pytest app_startup_runner_test.py + +See also https://docs.pytest.org/en/latest/usage.html +""" + +# global imports +from contextlib import contextmanager +import io +import shlex +import sys +import typing + +# pip imports +import pytest + +# local imports +import app_startup_runner as asr + +# +# Argument Parsing Helpers +# + +@contextmanager +def ignore_stdout_stderr(): + """Ignore stdout/stderr output for duration of this context.""" + old_stdout = sys.stdout + old_stderr = sys.stderr + sys.stdout = io.StringIO() + sys.stderr = io.StringIO() + try: + yield + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr + +@contextmanager +def argparse_bad_argument(msg): + """ + Assert that a SystemExit is raised when executing this context. + If the assertion fails, print the message 'msg'. + """ + with pytest.raises(SystemExit, message=msg): + with ignore_stdout_stderr(): + yield + +def assert_bad_argument(args, msg): + """ + Assert that the command line arguments in 'args' are malformed. + Prints 'msg' if the assertion fails. + """ + with argparse_bad_argument(msg): + parse_args(args) + +def parse_args(args): + """ + :param args: command-line like arguments as a single string + :return: dictionary of parsed key/values + """ + # "-a b -c d" => ['-a', 'b', '-c', 'd'] + return vars(asr.parse_options(shlex.split(args))) + +def default_dict_for_parsed_args(**kwargs): + """ + # Combine it with all of the "optional" parameters' default values. + """ + d = {'compiler_filters': None, 'simulate': False, 'debug': False, 'output': None, 'timeout': None, 'loop_count': 1, 'inodes': None} + d.update(kwargs) + return d + +def default_mock_dict_for_parsed_args(include_optional=True, **kwargs): + """ + Combine default dict with all optional parameters with some mock required parameters. + """ + d = {'packages': ['com.fake.package'], 'readaheads': ['warm']} + if include_optional: + d.update(default_dict_for_parsed_args()) + d.update(kwargs) + return d + +def parse_optional_args(str): + """ + Parse an argument string which already includes all the required arguments + in default_mock_dict_for_parsed_args. + """ + req = "--package com.fake.package --readahead warm" + return parse_args("%s %s" %(req, str)) + +def test_argparse(): + # missing arguments + assert_bad_argument("", "-p and -r are required") + assert_bad_argument("-r warm", "-p is required") + assert_bad_argument("--readahead warm", "-p is required") + assert_bad_argument("-p com.fake.package", "-r is required") + assert_bad_argument("--package com.fake.package", "-r is required") + + # required arguments are parsed correctly + ad = default_dict_for_parsed_args # assert dict + + assert parse_args("--package xyz --readahead warm") == ad(packages=['xyz'], readaheads=['warm']) + assert parse_args("-p xyz -r warm") == ad(packages=['xyz'], readaheads=['warm']) + + assert parse_args("-p xyz -r warm -s") == ad(packages=['xyz'], readaheads=['warm'], simulate=True) + assert parse_args("-p xyz -r warm --simulate") == ad(packages=['xyz'], readaheads=['warm'], simulate=True) + + # optional arguments are parsed correctly. + mad = default_mock_dict_for_parsed_args # mock assert dict + assert parse_optional_args("--output filename.csv") == mad(output='filename.csv') + assert parse_optional_args("-o filename.csv") == mad(output='filename.csv') + + assert parse_optional_args("--timeout 123") == mad(timeout=123) + assert parse_optional_args("-t 456") == mad(timeout=456) + + assert parse_optional_args("--loop-count 123") == mad(loop_count=123) + assert parse_optional_args("-lc 456") == mad(loop_count=456) + + assert parse_optional_args("--inodes bar") == mad(inodes="bar") + assert parse_optional_args("-in baz") == mad(inodes="baz") + + +def generate_run_combinations(*args): + # expand out the generator values so that assert x == y works properly. + return [i for i in asr.generate_run_combinations(*args)] + +def test_generate_run_combinations(): + blank_nd = typing.NamedTuple('Blank') + assert generate_run_combinations(blank_nd, {}) == [()], "empty" + assert generate_run_combinations(blank_nd, {'a' : ['a1', 'a2']}) == [()], "empty filter" + a_nd = typing.NamedTuple('A', [('a', str)]) + assert generate_run_combinations(a_nd, {'a': None}) == [(None,)], "None" + assert generate_run_combinations(a_nd, {'a': ['a1', 'a2']}) == [('a1',), ('a2',)], "one item" + assert generate_run_combinations(a_nd, + {'a' : ['a1', 'a2'], 'b': ['b1', 'b2']}) == [('a1',), ('a2',)],\ + "one item filter" + ab_nd = typing.NamedTuple('AB', [('a', str), ('b', str)]) + assert generate_run_combinations(ab_nd, + {'a': ['a1', 'a2'], + 'b': ['b1', 'b2']}) == [ab_nd('a1', 'b1'), + ab_nd('a1', 'b2'), + ab_nd('a2', 'b1'), + ab_nd('a2', 'b2')],\ + "two items" + + assert generate_run_combinations(ab_nd, + {'as': ['a1', 'a2'], + 'bs': ['b1', 'b2']}) == [ab_nd('a1', 'b1'), + ab_nd('a1', 'b2'), + ab_nd('a2', 'b1'), + ab_nd('a2', 'b2')],\ + "two items plural" + +def test_key_to_cmdline_flag(): + assert asr.key_to_cmdline_flag("abc") == "--abc" + assert asr.key_to_cmdline_flag("foos") == "--foo" + assert asr.key_to_cmdline_flag("ba_r") == "--ba-r" + assert asr.key_to_cmdline_flag("ba_zs") == "--ba-z" + + +def test_make_script_command_with_temp_output(): + cmd_str, tmp_file = asr.make_script_command_with_temp_output("fake_script", args=[], count=1) + with tmp_file: + assert cmd_str == ["fake_script", "--count", "1", "--output", tmp_file.name] + + cmd_str, tmp_file = asr.make_script_command_with_temp_output("fake_script", args=['a', 'b'], count=2) + with tmp_file: + assert cmd_str == ["fake_script", "a", "b", "--count", "2", "--output", tmp_file.name] + +def test_parse_run_script_csv_file(): + # empty file -> empty list + f = io.StringIO("") + assert asr.parse_run_script_csv_file(f) == [] + + # common case + f = io.StringIO("1,2,3") + assert asr.parse_run_script_csv_file(f) == [1,2,3] + + # ignore trailing comma + f = io.StringIO("1,2,3,4,5,") + assert asr.parse_run_script_csv_file(f) == [1,2,3,4,5] + + +if __name__ == '__main__': + pytest.main() diff --git a/startop/scripts/app_startup/launch_application b/startop/scripts/app_startup/launch_application new file mode 100755 index 000000000000..bc4ec51d6d08 --- /dev/null +++ b/startop/scripts/app_startup/launch_application @@ -0,0 +1,41 @@ +#!/bin/bash +# +# 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. + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source "$DIR/lib/common" + +launch_application() { + local package="$1" + local activity="$2" + local am_output="$(adb shell am start -S -W "$package"/"$activity")" + verbose_print adb shell am start -S -W "$package"/"$activity" + if [[ $? -ne 0 ]]; then + echo "am start failed" >&2 + + return 1 + fi + + # for everything else use the am start "TotalTime" output. + verbose_print "$am_output" + local total_time="$(echo "$am_output" | grep 'TotalTime:' | sed 's/TotalTime: //g')" + verbose_print "total time: $total_time" + + # TODO: Extract alternative metrics such as the #reportFullyDrawn. + + echo "$total_time" +} + +launch_application "$@" diff --git a/startop/scripts/app_startup/lib/common b/startop/scripts/app_startup/lib/common new file mode 100755 index 000000000000..4d5a53e4bb0c --- /dev/null +++ b/startop/scripts/app_startup/lib/common @@ -0,0 +1,14 @@ +#!/bin/bash + +if [[ -z $ANDROID_BUILD_TOP ]]; then + echo "Please run source build/envsetup.sh first" >&2 + exit 1 +fi + +source $ANDROID_BUILD_TOP/build/envsetup.sh + +verbose_print() { + if [[ "$verbose" == "y" ]]; then + echo "$@" >&2 + fi +} diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch new file mode 100755 index 000000000000..1ff5fc64116f --- /dev/null +++ b/startop/scripts/app_startup/run_app_with_prefetch @@ -0,0 +1,344 @@ +#!/bin/bash +# +# 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. + +usage() { + cat <<EOF +Usage: run_app_with_prefetch --package <name> [OPTIONS]... + + -p, --package <name> package of the app to test + -a, --activity <name> activity to use + -h, --help usage information (this) + -v, --verbose enable extra verbose printing + -i, --input <file> trace file protobuf (default 'TraceFile.pb') + -r, --readahead <mode> cold, warm, fadvise, mlock (default 'warm') + -w, --when <when> aot or jit (default 'aot') + -c, --count <count> how many times to run (default 1) + -s, --sleep <sec> how long to sleep after readahead + -t, --timeout <sec> how many seconds to timeout in between each app run (default 10) + -o, --output <file.csv> what file to write the performance results into as csv (default stdout) +EOF +} + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source "$DIR/lib/common" + +needs_trace_file="n" +input_file="" +package="" +mode='warm' +count=2 +sleep_time=2 +timeout=10 +output="" # stdout by default +when="aot" +parse_arguments() { + while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -p|--package) + package="$2" + shift + ;; + -a|--activity) + activity="$2" + shift + ;; + -i|--input) + input_file="$2" + shift + ;; + -v|--verbose) + export verbose="y" + ;; + -r|--readahead) + mode="$2" + shift + ;; + -c|--count) + count="$2" + ((count+=1)) + shift + ;; + -s|--sleep) + sleep_time="$2" + shift + ;; + -t|--timeout) + timeout="$2" + shift + ;; + -o|--output) + output="$2" + shift + ;; + -w|--when) + when="$2" + shift + ;; + --compiler-filter) + # ignore any '--compiler-filter xyz' settings. + # FIXME: app_startup_runner.py should not be passing this flag. + shift + ;; + *) + echo "Invalid argument: $1" >&2 + exit 1 + esac + shift + done +} + +echo_to_output_file() { + if [[ "x$output" != x ]]; then + echo "$@" >> $output + fi + # Always echo to stdout as well. + echo "$@" +} + +get_activity_name() { + local package="$1" + local action_key="android.intent.action.MAIN:" + + local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package")" + #echo $activity_line + IFS="/" read -a array <<< "$activity_line" + local activity_name="${array[1]}" + echo "$activity_name" + #adb shell am start "$package/$activity_name" +} + +find_package_path() { + local pkg="$1" + + res="$(adb shell find "/data/app/$pkg"-'*' -maxdepth 0 2> /dev/null)" + if [[ -z $res ]]; then + res="$(adb shell find "/system/app/$pkg"-'*' -maxdepth 0 2> /dev/null)" + fi + echo "$res" +} + +remote_pkill() { + local what="$1" + adb shell "for i in $(pid $what); do kill \$i; done" +} + +# Main entry point +if [[ $# -eq 0 ]]; then + usage + exit 1 +else + parse_arguments "$@" + + # if we do not have have package exit early with an error + [[ "$package" == "" ]] && echo "--package not specified" 1>&2 && exit 1 + + if [[ $mode != "cold" && $mode != "warm" ]]; then + needs_trace_file="y" + if [[ -z "$input_file" ]] || ! [[ -f $input_file ]]; then + echo "--input not specified" 1>&2 + exit 1 + fi + fi + + if [[ "$activity" == "" ]]; then + activity="$(get_activity_name "$package")" + if [[ "$activity" == "" ]]; then + echo "Activity name could not be found, invalid package name?" 1>&2 + exit 1 + else + verbose_print "Activity name inferred: " "$activity" + fi + fi +fi + +adb root > /dev/null + +if [[ ($when == jit) || ($when == aot) ]] && [[ "$(adb shell getenforce)" != "Permissive" ]]; then + echo "Disable selinux permissions and restart framework." + adb shell setenforce 0 + adb shell stop + adb shell start + adb wait-for-device +fi + +# TODO: set performance governor etc, preferrably only once +# before every single app run. + +# Kill everything before running. +remote_pkill "$package" +sleep 1 + +timings_array=() + +package_path="$(find_package_path "$package")" +if [[ $? -ne 0 ]]; then + echo "Failed to detect package path for '$package'" >&2 + exit 1 +fi +verbose_print "Package was in path '$package_path'" + + + + +keep_application_trace_file=n +application_trace_file_path="$package_path/TraceFile.pb" +trace_file_directory="$package_path" +if [[ $needs_trace_file == y ]]; then + # system server always passes down the package path in a hardcoded spot. + if [[ $when == "jit" ]]; then + verbose_print adb push "$input_file" "$application_trace_file_path" + adb push "$input_file" "$application_trace_file_path" + keep_application_trace_file="y" + else + # otherwise use a temporary directory to get normal non-jit behavior. + trace_file_directory="/data/local/tmp/prefetch/$package" + adb shell mkdir -p "$trace_file_directory" + verbose_print adb push "$input_file" "$trace_file_directory/TraceFile.pb" + adb push "$input_file" "$trace_file_directory/TraceFile.pb" + fi +fi + +# Everything other than JIT: remove the trace file, +# otherwise system server activity hints will kick in +# and the new just-in-time app pre-warmup will happen. +if [[ $keep_application_trace_file == "n" ]]; then + adb shell "[[ -f '$application_trace_file_path' ]] && rm '$application_trace_file_path'" +fi + +# Perform AOT readahead/pinning/etc when an application is about to be launched. +# For JIT readahead, we allow the system to handle it itself (this is a no-op). +# +# For warm, cold, etc modes which don't need readahead this is always a no-op. +perform_aot() { + local the_when="$1" # user: aot, jit + local the_mode="$2" # warm, cold, fadvise, mlock, etc. + + if [[ $the_when != "aot" ]]; then + # TODO: just in time implementation.. should probably use system server. + return 0 + fi + + # any non-warm/non-cold modes should use the iorap-activity-hint wrapper script. + if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then + + # TODO: add activity_hint_sender.exp + verbose_print "starting with package=$package package_path=$trace_file_directory" + coproc hint_sender_fd { $ANDROID_BUILD_TOP/system/iorap/src/sh/activity_hint_sender.exp "$package" "$trace_file_directory" "$the_mode"; } + hint_sender_pid=$! + verbose_print "Activity hint sender began" + + notification_success="n" + while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do + verbose_print "$hint_sender_output" + if [[ "$hint_sender_output" == "Press any key to send completed event..."* ]]; then + verbose_print "WE DID SEE NOTIFICATION SUCCESS." + notification_success='y' + # Give it some time to actually perform the readaheads. + sleep $sleep_time + break + fi + done + + if [[ $notification_success == 'n' ]]; then + echo "[FATAL] Activity hint notification failed." 1>&2 + exit 1 + fi + fi +} + +perform_aot_cleanup() { + local the_when="$1" # user: aot, jit + local the_mode="$2" # warm, cold, fadvise, mlock, etc. + + if [[ $the_when != "aot" ]]; then + # TODO: just in time implementation.. should probably use system server. + return 0 + fi + + # any non-warm/non-cold modes should use the iorap-activity-hint wrapper script. + if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then + # Clean up the hint sender by telling it that the launch was completed, + # and to shutdown the watcher. + echo "Done\n" >&"${hint_sender_fd[1]}" + + while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do + verbose_print "$hint_sender_output" + done + + wait $hint_sender_pid + fi +} + +# TODO: This loop logic could probably be moved into app_startup_runner.py +for ((i=0;i<count;++i)) do + verbose_print "==========================================" + verbose_print "==== ITERATION $i ====" + verbose_print "==========================================" + if [[ $mode != "warm" ]]; then + verbose_print "Drop caches for non-warm start." + # Drop all caches to get cold starts. + adb shell "echo 3 > /proc/sys/vm/drop_caches" + fi + + perform_aot "$when" "$mode" + + verbose_print "Running with timeout $timeout" + + # TODO: multiple metrics output. + total_time="$(timeout $timeout $DIR/launch_application "$package" "$activity")" + + if [[ $? -ne 0 ]]; then + echo "WARNING: Skip bad result, try iteration again." >&2 + ((i=i-1)) + continue + fi + + perform_aot_cleanup "$when" "$mode" + + echo "Iteration $i. Total time was: $total_time" + + timings_array+=($total_time) +done + +# drop the first result which is usually garbage. +timings_array=("${timings_array[@]:1}") + + +# Print out interactive/debugging timings and averages. +# Other scripts should use the --output flag and parse the CSV. +for tim in "${timings_array[@]}"; do + echo_to_output_file -ne "$tim," +done +echo_to_output_file "" + +average_string=$(echo "${timings_array[@]}" | awk '{s+=$0}END{print "Average:",s/NR}' RS=" ") +echo -ne ${average_string}. +if [[ x$output != x ]]; then + echo " Saved results to '$output'" +fi + +# Temporary hack around multiple activities being launched with different package paths (for same app): +# Clean up all left-over TraceFile.pb +adb shell 'for i in $(find /data/app -name TraceFile.pb); do rm \$i; done' + +# Kill the process to ensure AM isn't keeping it around. +remote_pkill "$package" + +exit 0 diff --git a/core/tests/webkit/apk_with_native_libs/jni/Android.mk b/startop/scripts/app_startup/unlock_screen index fd5b5eb50c5f..478294c9f35d 100644..100755 --- a/core/tests/webkit/apk_with_native_libs/jni/Android.mk +++ b/startop/scripts/app_startup/unlock_screen @@ -1,32 +1,22 @@ +#!/bin/bash # -# Copyright (C) 2017 The Android Open Source Project +# 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 +# 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. -# - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE := libwebviewtest_jni - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := WebViewTestJniOnLoad.cpp -LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) - -LOCAL_SDK_VERSION := current - -LOCAL_MULTILIB := both - -include $(BUILD_SHARED_LIBRARY) +# This turns the screen on if it's off. +# If it's on it does nothing unless its on the home screen, in which case it opens up some background +# menu. +# +# However, this menu is ignored because "am start" commands still work as expected. +adb shell input keyevent MENU diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java index 383d10baeb12..bb066ad428c6 100644 --- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java +++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java @@ -195,6 +195,8 @@ public class ParcelableCallAnalytics implements Parcelable { public static final int BLOCK_CHECK_FINISHED_TIMING = 9; public static final int FILTERING_COMPLETED_TIMING = 10; public static final int FILTERING_TIMED_OUT_TIMING = 11; + /** {@hide} */ + public static final int START_CONNECTION_TO_REQUEST_DISCONNECT_TIMING = 12; public static final int INVALID = 999999; @@ -256,6 +258,27 @@ public class ParcelableCallAnalytics implements Parcelable { public static final int SIP_PHONE = 0x8; public static final int THIRD_PARTY_PHONE = 0x10; + /** + * Indicating the call source is not specified. + * + * @hide + */ + public static final int CALL_SOURCE_UNSPECIFIED = 0; + + /** + * Indicating the call is initiated via emergency dialer's dialpad. + * + * @hide + */ + public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1; + + /** + * Indicating the call is initiated via emergency dialer's shortcut button. + * + * @hide + */ + public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2; + public static final long MILLIS_IN_5_MINUTES = 1000 * 60 * 5; public static final long MILLIS_IN_1_SECOND = 1000; @@ -319,6 +342,9 @@ public class ParcelableCallAnalytics implements Parcelable { // A list of video events that have occurred. private List<VideoEvent> videoEvents; + // The source where user initiated this call. ONE OF the CALL_SOURCE_* constants. + private int callSource = CALL_SOURCE_UNSPECIFIED; + public ParcelableCallAnalytics(long startTimeMillis, long callDurationMillis, int callType, boolean isAdditionalCall, boolean isInterrupted, int callTechnologies, int callTerminationCode, boolean isEmergencyCall, String connectionService, @@ -356,6 +382,7 @@ public class ParcelableCallAnalytics implements Parcelable { isVideoCall = readByteAsBoolean(in); videoEvents = new LinkedList<>(); in.readTypedList(videoEvents, VideoEvent.CREATOR); + callSource = in.readInt(); } public void writeToParcel(Parcel out, int flags) { @@ -373,6 +400,7 @@ public class ParcelableCallAnalytics implements Parcelable { out.writeTypedList(eventTimings); writeBooleanAsByte(out, isVideoCall); out.writeTypedList(videoEvents); + out.writeInt(callSource); } /** {@hide} */ @@ -385,6 +413,11 @@ public class ParcelableCallAnalytics implements Parcelable { this.videoEvents = videoEvents; } + /** {@hide} */ + public void setCallSource(int callSource) { + this.callSource = callSource; + } + public long getStartTimeMillis() { return startTimeMillis; } @@ -443,6 +476,11 @@ public class ParcelableCallAnalytics implements Parcelable { return videoEvents; } + /** {@hide} */ + public int getCallSource() { + return callSource; + } + @Override public int describeContents() { return 0; diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 48c1e24db1f3..4e2282337ccd 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -622,6 +622,18 @@ public class TelecomManager { "android.telecom.extra.USE_ASSISTED_DIALING"; /** + * Optional extra for {@link #placeCall(Uri, Bundle)} containing an integer that specifies + * the source where user initiated this call. This data is used in metrics. + * Valid source are: + * {@link ParcelableCallAnalytics#CALL_SOURCE_UNSPECIFIED}, + * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_DIALPAD}, + * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_SHORTCUT}. + * + * @hide + */ + public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE"; + + /** * The following 4 constants define how properties such as phone numbers and names are * displayed to the user. */ diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 1d9e6052a31c..8ebfec47c4ea 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1589,12 +1589,14 @@ public class SubscriptionManager { } /** - * @return true if a valid subId else false - * @hide + * Checks if the supplied subscription ID is valid. + * Note: a valid subscription ID does not necessarily correspond to an active subscription. + * + * @param subscriptionId The subscription ID. + * @return true if the supplied subscriptionId is valid; false otherwise. */ - @UnsupportedAppUsage - public static boolean isValidSubscriptionId(int subId) { - return subId > INVALID_SUBSCRIPTION_ID ; + public static boolean isValidSubscriptionId(int subscriptionId) { + return subscriptionId > INVALID_SUBSCRIPTION_ID; } /** diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 102cb7c77055..99a5a69213fa 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -41,9 +41,9 @@ import android.net.Network; import android.net.NetworkUtils; import android.os.Binder; import android.os.ParcelFileDescriptor; -import android.test.mock.MockContext; import android.support.test.filters.SmallTest; import android.system.Os; +import android.test.mock.MockContext; import java.net.Socket; import java.util.Arrays; @@ -121,6 +121,7 @@ public class IpSecServiceParameterizedTest { IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; IpSecService mIpSecService; Network fakeNetwork = new Network(0xAB); + int mUid = Os.getuid(); private static final IpSecAlgorithm AUTH_ALGO = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4); @@ -181,7 +182,7 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd) .ipSecDeleteSecurityAssociation( - eq(spiResp.resourceId), + eq(mUid), anyString(), anyString(), eq(TEST_SPI), @@ -189,8 +190,7 @@ public class IpSecServiceParameterizedTest { anyInt()); // Verify quota and RefcountedResource objects cleaned up - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent); try { userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId); @@ -209,8 +209,7 @@ public class IpSecServiceParameterizedTest { mIpSecService.allocateSecurityParameterIndex( mDestinationAddr, TEST_SPI, new Binder()); - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); IpSecService.RefcountedResource refcountedRecord = userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId); @@ -218,7 +217,7 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd) .ipSecDeleteSecurityAssociation( - eq(spiResp.resourceId), + eq(mUid), anyString(), anyString(), eq(TEST_SPI), @@ -270,7 +269,7 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd) .ipSecAddSecurityAssociation( - eq(createTransformResp.resourceId), + eq(mUid), anyInt(), anyString(), anyString(), @@ -305,7 +304,7 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd) .ipSecAddSecurityAssociation( - eq(createTransformResp.resourceId), + eq(mUid), anyInt(), anyString(), anyString(), @@ -361,13 +360,12 @@ public class IpSecServiceParameterizedTest { IpSecTransformResponse createTransformResp = mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent); mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); verify(mMockNetd, times(0)) .ipSecDeleteSecurityAssociation( - eq(createTransformResp.resourceId), + eq(mUid), anyString(), anyString(), eq(TEST_SPI), @@ -389,7 +387,7 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd, times(1)) .ipSecDeleteSecurityAssociation( - eq(createTransformResp.resourceId), + eq(mUid), anyString(), anyString(), eq(TEST_SPI), @@ -397,8 +395,7 @@ public class IpSecServiceParameterizedTest { anyInt()); // Verify quota and RefcountedResource objects cleaned up - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent); assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent); @@ -433,8 +430,7 @@ public class IpSecServiceParameterizedTest { IpSecTransformResponse createTransformResp = mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); IpSecService.RefcountedResource refcountedRecord = userRecord.mTransformRecords.getRefcountedResourceOrThrow( createTransformResp.resourceId); @@ -443,7 +439,7 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd) .ipSecDeleteSecurityAssociation( - eq(createTransformResp.resourceId), + eq(mUid), anyString(), anyString(), eq(TEST_SPI), @@ -477,7 +473,7 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd) .ipSecApplyTransportModeTransform( eq(pfd.getFileDescriptor()), - eq(resourceId), + eq(mUid), eq(IpSecManager.DIRECTION_OUT), anyString(), anyString(), @@ -509,8 +505,7 @@ public class IpSecServiceParameterizedTest { createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage"); // Check that we have stored the tracking object, and retrieve it - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); IpSecService.RefcountedResource refcountedRecord = userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( createTunnelResp.resourceId); @@ -530,8 +525,7 @@ public class IpSecServiceParameterizedTest { IpSecTunnelInterfaceResponse createTunnelResp = createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage"); - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, "blessedPackage"); @@ -551,8 +545,7 @@ public class IpSecServiceParameterizedTest { IpSecTunnelInterfaceResponse createTunnelResp = createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage"); - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); IpSecService.RefcountedResource refcountedRecord = userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( createTunnelResp.resourceId); diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh index 29bf74c7a8b9..e50c70d0656a 100755 --- a/tools/aosp/aosp_sha.sh +++ b/tools/aosp/aosp_sha.sh @@ -1,18 +1,24 @@ #!/bin/bash -LOCAL_DIR="$( dirname ${BASH_SOURCE} )" +LOCAL_DIR="$( dirname "${BASH_SOURCE}" )" -if git branch -vv | grep "^*" | grep "\[aosp/master" > /dev/null; then +if git branch -vv | grep -q -P "^\*[^\[]+\[aosp/"; then # Change appears to be in AOSP exit 0 else # Change appears to be non-AOSP; search for files - git show --name-only --pretty=format: $1 | grep $2 | while read file; do - echo + count=0 + while read -r file ; do + if (( count == 0 )); then + echo + fi echo -e "\033[0;31mThe source of truth for '$file' is in AOSP.\033[0m" + (( count++ )) + done < <(git show --name-only --pretty=format: $1 | grep -- "$2") + if (( count != 0 )); then echo - echo "If your change contains no confidential details, please upload and merge" - echo "this change at https://android-review.googlesource.com/." + echo "If your change contains no confidential details (such as security fixes), please" + echo "upload and merge this change at https://android-review.googlesource.com/." echo exit 77 - done + fi fi |