Merge "Added new cell broadcast channels"
diff --git a/Android.bp b/Android.bp
index aeb3ceb..cb7f7a2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1221,7 +1221,7 @@
"--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 @@
],
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 @@
],
}
-droiddoc {
+droidstubs {
name: "hiddenapi-lists-docs",
defaults: ["metalava-api-stubs-default"],
arg_files: [
@@ -1540,7 +1539,7 @@
],
}
-droiddoc {
+droidstubs {
name: "api-stubs-docs",
defaults: ["metalava-api-stubs-default"],
api_filename: "public_api.txt",
@@ -1562,7 +1561,7 @@
},
}
-droiddoc {
+droidstubs {
name: "system-api-stubs-docs",
defaults: ["metalava-api-stubs-default"],
api_tag_name: "SYSTEM",
@@ -1586,7 +1585,7 @@
},
}
-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 e31f8a0..3a82b7c 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -934,6 +934,7 @@
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 @@
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
@@ -9337,6 +9340,7 @@
ctor public ContentUris();
method public static android.net.Uri.Builder appendId(android.net.Uri.Builder, long);
method public static long parseId(android.net.Uri);
+ method public static android.net.Uri removeId(android.net.Uri);
method public static android.net.Uri withAppendedId(android.net.Uri, long);
}
@@ -42654,6 +42658,7 @@
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 +49551,7 @@
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 1e1c621..e2c59cb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6785,7 +6785,6 @@
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 0f89dfd..28e9734 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1336,30 +1336,47 @@
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 @@
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 @@
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 @@
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 @@
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/Android.bp b/cmds/statsd/Android.bp
index 3fae586..94203f4f 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -46,6 +46,7 @@
// ==== java proto device library (for test only) ==============================
java_library {
name: "statsdprotolite",
+ no_framework_libs: true,
proto: {
type: "lite",
include_dirs: ["external/protobuf/src"],
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 8f3ad9d..2c07431 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -30,6 +30,7 @@
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
import "frameworks/base/proto/src/stats_enums.proto";
+import "frameworks/base/core/proto/android/service/procstats.proto";
/**
* The master atom class. This message defines all of the available
@@ -90,7 +91,7 @@
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;
@@ -165,6 +166,7 @@
DirectoryUsage directory_usage = 10026;
AppSize app_size = 10027;
CategorySize category_size = 10028;
+ android.service.procstats.ProcessStatsSectionProto proc_stats = 10029;
}
// DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -1477,6 +1479,18 @@
}
/**
+ * 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 eec90fc..afd8ec2 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -49,7 +49,6 @@
// 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 @@
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 bcfcd7a..02b9773 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -63,7 +63,6 @@
// 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 @@
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 @@
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 e3da5db..6379389 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -33,12 +33,11 @@
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 db7e680..ab0b23c 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -46,7 +46,7 @@
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 @@
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 ea6eb3f..5b6f167 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 @@
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 @@
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 @@
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 3de8d0d..5c1ef01 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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
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 @@
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/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 04b33b1..b683138 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -15605,7 +15605,6 @@
HSPLandroid/app/admin/IDevicePolicyManager;->getLockTaskPackages(Landroid/content/ComponentName;)[Ljava/lang/String;
HSPLandroid/app/admin/IDevicePolicyManager;->getLongSupportMessage(Landroid/content/ComponentName;)Ljava/lang/CharSequence;
HSPLandroid/app/admin/IDevicePolicyManager;->getLongSupportMessageForUser(Landroid/content/ComponentName;I)Ljava/lang/CharSequence;
-HSPLandroid/app/admin/IDevicePolicyManager;->getMandatoryBackupTransport()Landroid/content/ComponentName;
HSPLandroid/app/admin/IDevicePolicyManager;->getMaximumFailedPasswordsForWipe(Landroid/content/ComponentName;IZ)I
HSPLandroid/app/admin/IDevicePolicyManager;->getMaximumTimeToLock(Landroid/content/ComponentName;IZ)J
HSPLandroid/app/admin/IDevicePolicyManager;->getMeteredDataDisabledPackages(Landroid/content/ComponentName;)Ljava/util/List;
@@ -15747,7 +15746,6 @@
HSPLandroid/app/admin/IDevicePolicyManager;->setLockTaskPackages(Landroid/content/ComponentName;[Ljava/lang/String;)V
HSPLandroid/app/admin/IDevicePolicyManager;->setLogoutEnabled(Landroid/content/ComponentName;Z)V
HSPLandroid/app/admin/IDevicePolicyManager;->setLongSupportMessage(Landroid/content/ComponentName;Ljava/lang/CharSequence;)V
-HSPLandroid/app/admin/IDevicePolicyManager;->setMandatoryBackupTransport(Landroid/content/ComponentName;Landroid/content/ComponentName;)Z
HSPLandroid/app/admin/IDevicePolicyManager;->setMasterVolumeMuted(Landroid/content/ComponentName;Z)V
HSPLandroid/app/admin/IDevicePolicyManager;->setMaximumFailedPasswordsForWipe(Landroid/content/ComponentName;IZ)V
HSPLandroid/app/admin/IDevicePolicyManager;->setMaximumTimeToLock(Landroid/content/ComponentName;JZ)V
@@ -24288,7 +24286,6 @@
HSPLandroid/inputmethodservice/IInputMethodSessionWrapper;->getInternalInputMethodSession()Landroid/view/inputmethod/InputMethodSession;
HSPLandroid/inputmethodservice/IInputMethodWrapper$InputMethodSessionCallbackWrapper;->sessionCreated(Landroid/view/inputmethod/InputMethodSession;)V
HSPLandroid/inputmethodservice/IInputMethodWrapper;-><init>(Landroid/inputmethodservice/AbstractInputMethodService;Landroid/view/inputmethod/InputMethod;)V
-HSPLandroid/inputmethodservice/IInputMethodWrapper;->attachToken(Landroid/os/IBinder;)V
HSPLandroid/inputmethodservice/IInputMethodWrapper;->bindInput(Landroid/view/inputmethod/InputBinding;)V
HSPLandroid/inputmethodservice/IInputMethodWrapper;->createSession(Landroid/view/InputChannel;Lcom/android/internal/view/IInputSessionCallback;)V
HSPLandroid/inputmethodservice/IInputMethodWrapper;->executeMessage(Landroid/os/Message;)V
@@ -42280,11 +42277,9 @@
HSPLcom/android/internal/view/IInputContextCallback;->setTextAfterCursor(Ljava/lang/CharSequence;I)V
HSPLcom/android/internal/view/IInputContextCallback;->setTextBeforeCursor(Ljava/lang/CharSequence;I)V
HSPLcom/android/internal/view/IInputMethod$Stub$Proxy;->asBinder()Landroid/os/IBinder;
-HSPLcom/android/internal/view/IInputMethod$Stub$Proxy;->attachToken(Landroid/os/IBinder;)V
HSPLcom/android/internal/view/IInputMethod$Stub;-><init>()V
HSPLcom/android/internal/view/IInputMethod$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethod;
HSPLcom/android/internal/view/IInputMethod$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
-HSPLcom/android/internal/view/IInputMethod;->attachToken(Landroid/os/IBinder;)V
HSPLcom/android/internal/view/IInputMethod;->bindInput(Landroid/view/inputmethod/InputBinding;)V
HSPLcom/android/internal/view/IInputMethod;->changeInputMethodSubtype(Landroid/view/inputmethod/InputMethodSubtype;)V
HSPLcom/android/internal/view/IInputMethod;->createSession(Landroid/view/InputChannel;Lcom/android/internal/view/IInputSessionCallback;)V
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index f0a0e88..759443d 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -76,6 +76,7 @@
* @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 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 @@
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 @@
packageNames = other.packageNames;
feedbackType = other.feedbackType;
notificationTimeout = other.notificationTimeout;
+ mMinimumUiTimeout = other.mMinimumUiTimeout;
flags = other.flags;
}
@@ -755,6 +767,29 @@
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 @@
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 @@
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 @@
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/accounts/Account.java b/core/java/android/accounts/Account.java
index f07f5ec..d3b2238 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -20,13 +20,14 @@
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+
import com.android.internal.annotations.GuardedBy;
import java.util.Set;
@@ -45,6 +46,7 @@
public final String name;
public final String type;
+ private String mSafeName;
@UnsupportedAppUsage
private final @Nullable String accessId;
@@ -135,4 +137,37 @@
public String toString() {
return "Account {name=" + name + ", type=" + type + "}";
}
+
+ /**
+ * Return a string representation of the account that is safe to print
+ * to logs and other places where PII should be avoided.
+ * @hide
+ */
+ public String toSafeString() {
+ if (mSafeName == null) {
+ mSafeName = toSafeName(name, 'x');
+ }
+ return "Account {name=" + mSafeName + ", type=" + type + "}";
+ }
+
+ /**
+ * Given a name, replace all letter or digits with the replacement char.
+ * @param name The input name string.
+ * @param replacement the replacement character.
+ * @return the string after replacement.
+ * @hide
+ */
+ public static String toSafeName(String name, char replacement) {
+ final StringBuilder builder = new StringBuilder(64);
+ final int len = name.length();
+ for (int i = 0; i < len; i++) {
+ final char c = name.charAt(i);
+ if (Character.isLetterOrDigit(c)) {
+ builder.append(replacement);
+ } else {
+ builder.append(c);
+ }
+ }
+ return builder.toString();
+ }
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 041a5b0..7b86646 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1461,7 +1461,7 @@
*/
@Override
public AutofillId autofillClientGetNextAutofillId() {
- return new AutofillId(getAutofillManager(), getNextAutofillId());
+ return new AutofillId(getNextAutofillId());
}
/**
@@ -2310,7 +2310,7 @@
*
* @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 @@
* @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 @@
* @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 636366d..68b745d 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -127,7 +127,7 @@
}
@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/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d1ecf1e..bd7a2dd 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1183,14 +1183,6 @@
public static final String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
/**
- * Constant to indicate the feature of mandatory backups. Used as argument to
- * {@link #createAdminSupportIntent(String)}.
- * @see #setMandatoryBackupTransport(ComponentName, ComponentName)
- * @hide
- */
- public static final String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
-
- /**
* Constant to indicate the feature of suspending app. Use it as the value of
* {@link #EXTRA_RESTRICTION}.
* @hide
@@ -1200,8 +1192,7 @@
/**
* A String indicating a specific restricted feature. Can be a user restriction from the
* {@link UserManager}, e.g. {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the values
- * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
- * {@link #POLICY_MANDATORY_BACKUPS}.
+ * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
* @see #createAdminSupportIntent(String)
* @hide
*/
@@ -6919,7 +6910,7 @@
* @param restriction Indicates for which feature the dialog should be displayed. Can be a
* user restriction from {@link UserManager}, e.g.
* {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants
- * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+ * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
* @return Intent An intent to be used to start the dialog-activity if the restriction is
* set by an admin, or null if the restriction does not exist or no admin set it.
*/
@@ -8917,55 +8908,6 @@
}
/**
- * Makes backups mandatory and enforces the usage of the specified backup transport.
- *
- * <p>When a {@code null} backup transport is specified, backups are made optional again.
- * <p>Only device owner can call this method.
- * <p>If backups were disabled and a non-null backup transport {@link ComponentName} is
- * specified, backups will be enabled.
- * <p> If the backup service is disabled after the mandatory backup transport has been set, the
- * mandatory backup transport is cleared.
- *
- * <p>NOTE: The method shouldn't be called on the main thread.
- *
- * @param admin admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param backupTransportComponent The backup transport layer to be used for mandatory backups.
- * @return {@code true} if the backup transport was successfully set; {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a device owner.
- * @hide
- */
- @WorkerThread
- public boolean setMandatoryBackupTransport(
- @NonNull ComponentName admin,
- @Nullable ComponentName backupTransportComponent) {
- throwIfParentInstance("setMandatoryBackupTransport");
- try {
- return mService.setMandatoryBackupTransport(admin, backupTransportComponent);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- /**
- * Returns the backup transport which has to be used for backups if backups are mandatory or
- * {@code null} if backups are not mandatory.
- *
- * @return a {@link ComponentName} of the backup transport layer to be used if backups are
- * mandatory or {@code null} if backups are not mandatory.
- * @hide
- */
- @UnsupportedAppUsage
- public ComponentName getMandatoryBackupTransport() {
- throwIfParentInstance("getMandatoryBackupTransport");
- try {
- return mService.getMandatoryBackupTransport();
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
-
- /**
* Called by a device owner to control the network logging feature.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6a5f3de..35ea250 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -362,8 +362,6 @@
void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
boolean isBackupServiceEnabled(in ComponentName admin);
- boolean setMandatoryBackupTransport(in ComponentName admin, in ComponentName backupTransportComponent);
- ComponentName getMandatoryBackupTransport();
void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
boolean isNetworkLoggingEnabled(in ComponentName admin);
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index ee6a81d..173b766 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -32,7 +32,6 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import com.android.internal.util.Preconditions;
@@ -73,8 +72,6 @@
boolean mHaveData;
ComponentName mActivityComponent;
- // Not written to parcel, only used to set session id on virtual node children
- private final int mAutofillSessionId;
private boolean mIsHomeActivity;
private int mFlags;
@@ -1849,13 +1846,11 @@
@Override
public void setAutofillId(@NonNull AutofillId id) {
mNode.mAutofillId = id;
- mNode.mAutofillId.setSessionId(mAssist.mAutofillSessionId);
}
@Override
public void setAutofillId(@NonNull AutofillId parentId, int virtualId) {
mNode.mAutofillId = new AutofillId(parentId, virtualId);
- mNode.mAutofillId.setSessionId(mAssist.mAutofillSessionId);
}
@Override
@@ -2045,8 +2040,6 @@
public AssistStructure(Activity activity, boolean forAutoFill, int flags) {
mHaveData = true;
mActivityComponent = activity.getComponentName();
- final AutofillManager afm = activity.getSystemService(AutofillManager.class);
- mAutofillSessionId = afm == null ? AutofillManager.NO_SESSION : afm.getSessionId();
mFlags = flags;
ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
activity.getActivityToken());
@@ -2063,7 +2056,6 @@
public AssistStructure() {
mHaveData = true;
mActivityComponent = null;
- mAutofillSessionId = AutofillManager.NO_SESSION;
mFlags = 0;
}
@@ -2071,7 +2063,6 @@
public AssistStructure(Parcel in) {
mIsHomeActivity = in.readInt() == 1;
mReceiveChannel = in.readStrongBinder();
- mAutofillSessionId = AutofillManager.NO_SESSION;
}
/**
@@ -2091,10 +2082,6 @@
ensureData();
}
Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString());
- if (mAutofillSessionId != AutofillManager.NO_SESSION) {
- Log.i(TAG, "Autofill Session ID: " + mAutofillSessionId);
- }
-
Log.i(TAG, "Sanitize on write: " + mSanitizeOnWrite);
Log.i(TAG, "Flags: " + mFlags);
final int N = getWindowNodeCount();
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 1c55d8a..3e20748 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -299,8 +299,7 @@
*
* @param transport ComponentName of the service hosting the transport. This is different from
* the transport's name that is returned by {@link BackupTransport#name()}.
- * @param listener A listener object to get a callback on the transport being selected. It may
- * be {@code null}.
+ * @param listener A listener object to get a callback on the transport being selected.
*/
void selectBackupTransportAsync(in ComponentName transport, ISelectBackupTransportCallback listener);
diff --git a/core/java/android/content/ComponentCallbacks.java b/core/java/android/content/ComponentCallbacks.java
index b96c8d3..428545d 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 @@
*
* @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/content/ContentUris.java b/core/java/android/content/ContentUris.java
index dbe8a7c..fd7b372 100644
--- a/core/java/android/content/ContentUris.java
+++ b/core/java/android/content/ContentUris.java
@@ -18,6 +18,8 @@
import android.net.Uri;
+import java.util.List;
+
/**
* Utility methods useful for working with {@link android.net.Uri} objects
* that use the "content" (content://) scheme.
@@ -109,4 +111,30 @@
public static Uri withAppendedId(Uri contentUri, long id) {
return appendId(contentUri.buildUpon(), id).build();
}
+
+ /**
+ * Removes any ID from the end of the path.
+ *
+ * @param contentUri that ends with an ID
+ * @return a new URI with the ID removed from the end of the path
+ * @throws IllegalArgumentException when the given URI has no ID to remove
+ * from the end of the path
+ */
+ public static Uri removeId(Uri contentUri) {
+ // Verify that we have a valid ID to actually remove
+ final String last = contentUri.getLastPathSegment();
+ if (last == null) {
+ throw new IllegalArgumentException("No path segments to remove");
+ } else {
+ Long.parseLong(last);
+ }
+
+ final List<String> segments = contentUri.getPathSegments();
+ final Uri.Builder builder = contentUri.buildUpon();
+ builder.path(null);
+ for (int i = 0; i < segments.size() - 1; i++) {
+ builder.appendPath(segments.get(i));
+ }
+ return builder.build();
+ }
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index d9d1777..9e20503 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -234,7 +234,7 @@
/**
* Array of all signatures read from the package file. This is only filled
* in if the flag {@link PackageManager#GET_SIGNATURES} was set. A package
- * must be singed with at least one certificate which is at position zero.
+ * must be signed with at least one certificate which is at position zero.
* The package can be signed with additional certificates which appear as
* subsequent entries.
*
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 1ee3c93..cb33659 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -58,7 +58,7 @@
* 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 6d9ba77..7e52ca3 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -141,13 +141,6 @@
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 @@
* @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 @@
}
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/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 185215a..b0fca00 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -252,4 +252,12 @@
return;
}
+ /**
+ * Called when the user took some actions that should be taken into consideration to update the
+ * MRU list for input method rotation.
+ *
+ * @hide
+ */
+ public void notifyUserActionIfNecessary() {
+ }
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 00a1f6f..1030694 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -87,8 +87,8 @@
* guarantees that {@link #bindInput(InputBinding)},
* {@link #startInput(IBinder, IInputContext, int, EditorInfo, boolean)}, and
* {@link #unbindInput()} are called with the same order as the original calls
- * in {@link com.android.server.InputMethodManagerService}. See {@link IBinder#FLAG_ONEWAY}
- * for detailed semantics.</p>
+ * in {@link com.android.server.inputmethod.InputMethodManagerService}.
+ * See {@link IBinder#FLAG_ONEWAY} for detailed semantics.</p>
*/
AtomicBoolean mIsUnbindIssued = null;
@@ -160,11 +160,10 @@
args.recycle();
return;
}
-
case DO_INITIALIZE_INTERNAL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- inputMethod.initializeInternal((IBinder) args.arg1,
+ inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
(IInputMethodPrivilegedOperations) args.arg2);
} finally {
args.recycle();
@@ -253,9 +252,10 @@
@BinderThread
@Override
- public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps) {
+ public void initializeInternal(IBinder token, int displayId,
+ IInputMethodPrivilegedOperations privOps) {
mCaller.executeOrSendMessage(
- mCaller.obtainMessageOO(DO_INITIALIZE_INTERNAL, token, privOps));
+ mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index aa5cf09..34fa5b6 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -16,12 +16,14 @@
package android.inputmethodservice;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.AnyThread;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
@@ -79,6 +81,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
@@ -402,6 +405,10 @@
@BackDispositionMode
int mBackDisposition;
+ private Object mLock = new Object();
+ @GuardedBy("mLock")
+ private boolean mNotifyUserActionSent;
+
/**
* {@code true} when the previous IME had non-empty inset at the bottom of the screen and we
* have not shown our own window yet. In this situation, the previous inset continues to be
@@ -455,10 +462,11 @@
*/
@MainThread
@Override
- public final void initializeInternal(IBinder token,
+ public final void initializeInternal(IBinder token, int displayId,
IInputMethodPrivilegedOperations privilegedOperations) {
mPrivOps.set(privilegedOperations);
mImm.registerInputMethodPrivOps(token, mPrivOps);
+ updateInputMethodDisplay(displayId);
attachToken(token);
}
@@ -478,6 +486,22 @@
/**
* {@inheritDoc}
+ * @hide
+ */
+ @MainThread
+ @Override
+ public void updateInputMethodDisplay(int displayId) {
+ // Update display for adding IME window to the right display.
+ if (displayId != DEFAULT_DISPLAY) {
+ // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
+ // for update resources & configuration correctly when show soft input
+ // in non-default display.
+ updateDisplay(displayId);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
*
* <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
*/
@@ -594,7 +618,7 @@
@MainThread
@Override
public void changeInputMethodSubtype(InputMethodSubtype subtype) {
- onCurrentInputMethodSubtypeChanged(subtype);
+ dispatchOnCurrentInputMethodSubtypeChanged(subtype);
}
}
@@ -924,6 +948,9 @@
// If the previous IME has occupied non-empty inset in the screen, we need to decide whether
// we continue to use the same size of the inset or update it
mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
+ // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
+ // for update resources & configuration correctly when show soft input
+ // in non-default display.
mInflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
@@ -2792,6 +2819,13 @@
}
}
+ private void dispatchOnCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
+ synchronized (mLock) {
+ mNotifyUserActionSent = false;
+ }
+ onCurrentInputMethodSubtypeChanged(newSubtype);
+ }
+
// TODO: Handle the subtype change event
/**
* Called when the subtype was changed.
@@ -2848,6 +2882,22 @@
}
/**
+ * {@inheritDoc}
+ * @hide
+ */
+ @AnyThread
+ @Override
+ public final void notifyUserActionIfNecessary() {
+ synchronized (mLock) {
+ if (mNotifyUserActionSent) {
+ return;
+ }
+ mPrivOps.notifyUserActionAsync();
+ mNotifyUserActionSent = true;
+ }
+ }
+
+ /**
* Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
* permission to the content.
*
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 9bcc600..40465ce 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -197,7 +197,7 @@
*
* <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 0fef78d..3d4c00c 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -1601,12 +1601,13 @@
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/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 1c8029c..a9cb0d9 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,18 @@
package android.os;
+import static android.os.ParcelFileDescriptor.MODE_APPEND;
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.O_APPEND;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_RDONLY;
+import static android.system.OsConstants.O_RDWR;
+import static android.system.OsConstants.O_TRUNC;
+import static android.system.OsConstants.O_WRONLY;
import static android.system.OsConstants.SPLICE_F_MORE;
import static android.system.OsConstants.SPLICE_F_MOVE;
import static android.system.OsConstants.S_ISFIFO;
@@ -1061,8 +1073,13 @@
mimeTypeFromExt = ContentResolver.MIME_TYPE_DEFAULT;
}
- final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
- mimeType);
+ final String extFromMimeType;
+ if (ContentResolver.MIME_TYPE_DEFAULT.equals(mimeType)) {
+ extFromMimeType = null;
+ } else {
+ extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
+ }
+
if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
// Extension maps back to requested MIME type; allow it
} else {
@@ -1180,6 +1197,96 @@
}
/** {@hide} */
+ public static int translateModeStringToPosix(String mode) {
+ int res = 0;
+ if (mode.startsWith("rw")) {
+ res |= O_RDWR | O_CREAT;
+ } else if (mode.startsWith("w")) {
+ res |= O_WRONLY | O_CREAT;
+ } else if (mode.startsWith("r")) {
+ res |= O_RDONLY;
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ if (mode.indexOf('t') != -1) {
+ res |= O_TRUNC;
+ }
+ if (mode.indexOf('a') != -1) {
+ res |= O_APPEND;
+ }
+ return res;
+ }
+
+ /** {@hide} */
+ public static String translateModePosixToString(int mode) {
+ String res = "";
+ if ((mode & O_RDWR) == O_RDWR) {
+ res += "rw";
+ } else if ((mode & O_WRONLY) == O_WRONLY) {
+ res += "w";
+ } else if ((mode & O_RDONLY) == O_RDONLY) {
+ res += "r";
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ if ((mode & O_TRUNC) == O_TRUNC) {
+ res += "t";
+ }
+ if ((mode & O_APPEND) == O_APPEND) {
+ res += "a";
+ }
+ return res;
+ }
+
+ /** {@hide} */
+ public static int translateModePosixToPfd(int mode) {
+ int res = 0;
+ if ((mode & O_RDWR) == O_RDWR) {
+ res |= MODE_READ_WRITE;
+ } else if ((mode & O_WRONLY) == O_WRONLY) {
+ res |= MODE_WRITE_ONLY;
+ } else if ((mode & O_RDONLY) == O_RDONLY) {
+ res |= MODE_READ_ONLY;
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ if ((mode & O_CREAT) == O_CREAT) {
+ res |= MODE_CREATE;
+ }
+ if ((mode & O_TRUNC) == O_TRUNC) {
+ res |= MODE_TRUNCATE;
+ }
+ if ((mode & O_APPEND) == O_APPEND) {
+ res |= MODE_APPEND;
+ }
+ return res;
+ }
+
+ /** {@hide} */
+ public static int translateModePfdToPosix(int mode) {
+ int res = 0;
+ if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
+ res |= O_RDWR;
+ } else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
+ res |= O_WRONLY;
+ } else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) {
+ res |= O_RDONLY;
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ if ((mode & MODE_CREATE) == MODE_CREATE) {
+ res |= O_CREAT;
+ }
+ if ((mode & MODE_TRUNCATE) == MODE_TRUNCATE) {
+ res |= O_TRUNC;
+ }
+ if ((mode & MODE_APPEND) == MODE_APPEND) {
+ res |= O_APPEND;
+ }
+ return res;
+ }
+
+ /** {@hide} */
@VisibleForTesting
public static class MemoryPipe extends Thread implements AutoCloseable {
private final FileDescriptor[] pipe;
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 9b202f2..e03af9d 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -33,7 +33,7 @@
* them as they come out of the message queue.
*
* <p>There are two main uses for a Handler: (1) to schedule messages and
- * runnables to be executed as some point in the future; and (2) to enqueue
+ * runnables to be executed at some point in the future; and (2) to enqueue
* an action to be performed on a different thread than your own.
*
* <p>Scheduling messages is accomplished with the
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c9edc53..a54c589 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -559,28 +559,7 @@
* @throws IllegalArgumentException if the given string does not match a known file mode.
*/
public static int parseMode(String mode) {
- final int modeBits;
- if ("r".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
- } else if ("w".equals(mode) || "wt".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
- | ParcelFileDescriptor.MODE_CREATE
- | ParcelFileDescriptor.MODE_TRUNCATE;
- } else if ("wa".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
- | ParcelFileDescriptor.MODE_CREATE
- | ParcelFileDescriptor.MODE_APPEND;
- } else if ("rw".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_READ_WRITE
- | ParcelFileDescriptor.MODE_CREATE;
- } else if ("rwt".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_READ_WRITE
- | ParcelFileDescriptor.MODE_CREATE
- | ParcelFileDescriptor.MODE_TRUNCATE;
- } else {
- throw new IllegalArgumentException("Bad mode '" + mode + "'");
- }
- return modeBits;
+ return FileUtils.translateModePosixToPfd(FileUtils.translateModeStringToPosix(mode));
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e007398..026195e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6508,6 +6508,25 @@
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 @@
ZEN_SETTINGS_SUGGESTION_VIEWED,
CHARGING_SOUNDS_ENABLED,
CHARGING_VIBRATION_ENABLED,
+ ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED,
+ ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS,
};
/**
@@ -8349,6 +8370,8 @@
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/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java
index fa6bd65..271707a 100644
--- a/core/java/android/service/autofill/CharSequenceTransformation.java
+++ b/core/java/android/service/autofill/CharSequenceTransformation.java
@@ -90,7 +90,7 @@
try {
final Matcher matcher = field.first.matcher(value);
if (!matcher.find()) {
- if (sDebug) Log.d(TAG, "match for " + field.first + " failed on id " + id);
+ if (sDebug) Log.d(TAG, "Match for " + field.first + " failed on id " + id);
return;
}
// replaceAll throws an exception if the subst is invalid
@@ -103,6 +103,9 @@
throw e;
}
}
+ // Cannot log converted, it might have PII
+ Log.d(TAG, "Converting text on child " + childViewId + " to " + converted.length()
+ + "_chars");
parentTemplate.setCharSequence(childViewId, "setText", converted);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b5ade2a..0476cf8 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -39,7 +39,6 @@
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 0000000..209451b
--- /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 a94806a..a1ee61c 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -100,88 +100,12 @@
* 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 @@
}
}
- //
- // 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 @@
// 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 @@
}
/**
- * 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 0000000..9e2e95a
--- /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 c7bbb9f..03bf590 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -19,6 +19,8 @@
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 @@
}
}
}
+
+ /**
+ * 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/java/android/util/proto/WireTypeMismatchException.java b/core/java/android/util/proto/WireTypeMismatchException.java
new file mode 100644
index 0000000..d90b4f8
--- /dev/null
+++ b/core/java/android/util/proto/WireTypeMismatchException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.proto;
+
+import android.annotation.TestApi;
+
+/**
+ * Thrown when there is an error parsing protobuf data.
+ *
+ * @hide
+ */
+@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 45fa561..eb41e07 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -32,6 +32,7 @@
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 @@
// 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 c520a99..9aab419 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 @@
* @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 @@
* @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 @@
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/View.java b/core/java/android/view/View.java
index 4a61906..9bc53ed 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8251,7 +8251,7 @@
if (mAutofillId == null) {
// The autofill id needs to be unique, but its value doesn't matter,
// so it's better to reuse the accessibility id to save space.
- mAutofillId = new AutofillId(getAutofillManager(), getAutofillViewId());
+ mAutofillId = new AutofillId(getAutofillViewId());
}
return mAutofillId;
}
@@ -8313,15 +8313,11 @@
// Ignore reset because it was never explicitly set before.
return;
}
+ mAutofillId = id;
if (id != null) {
- // Must create a new id so the session id is preserved.
- final int oldSessionId = mAutofillId.getSessionId();
mAutofillViewId = id.getViewId();
- mAutofillId = new AutofillId(mAutofillViewId);
- mAutofillId.setSessionId(oldSessionId);
mPrivateFlags3 |= PFLAG3_AUTOFILLID_EXPLICITLY_SET;
} else {
- mAutofillId = null;
mAutofillViewId = NO_ID;
mPrivateFlags3 &= ~PFLAG3_AUTOFILLID_EXPLICITLY_SET;
}
@@ -8611,11 +8607,8 @@
structure.setContextClickable(true);
}
if (forAutofill) {
- final AutofillId autofillId = new AutofillId(getAutofillId(),
- AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()));
- final AutofillManager afm = getAutofillManager();
- autofillId.setSessionId(afm == null ? AutofillManager.NO_SESSION : afm.getSessionId());
- structure.setAutofillId(autofillId);
+ structure.setAutofillId(new AutofillId(getAutofillId(),
+ AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
}
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index 7bae28a..82e9a5c 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -49,6 +49,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 c59c491..e88682e 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -139,6 +139,8 @@
int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+ int mMinimumUiTimeout;
+
boolean mIsTouchExplorationEnabled;
@UnsupportedAppUsage
@@ -320,6 +322,11 @@
public void setRelevantEventTypes(int eventTypes) {
mRelevantEventTypes = eventTypes;
}
+
+ @Override
+ public void setMinimumUiTimeout(int uiTimeout) {
+ mMinimumUiTimeout = uiTimeout;
+ }
};
/**
@@ -827,6 +834,19 @@
}
/**
+ * 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 @@
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 c93e2c1..3e2ef18 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -72,4 +72,6 @@
// 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 9cc0315..d2ddca3 100644
--- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
@@ -29,4 +29,6 @@
void notifyServicesStateChanged();
void setRelevantEventTypes(int eventTypes);
+
+ void setMinimumUiTimeout(int uiTimeout);
}
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 5ae91a5..cb1d89c 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -15,10 +15,6 @@
*/
package android.view.autofill;
-import static android.view.autofill.Helper.sDebug;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,7 +28,6 @@
private final int mViewId;
private final boolean mVirtual;
private final int mVirtualId;
- private int mSessionId = AutofillManager.NO_SESSION;
/** @hide */
@TestApi
@@ -43,26 +38,18 @@
}
/** @hide */
- // NOTE: caller must set sessionId
@TestApi
- public AutofillId(@NonNull AutofillId parent, int virtualChildId) {
+ public AutofillId(AutofillId parent, int virtualChildId) {
mVirtual = true;
mViewId = parent.mViewId;
mVirtualId = virtualChildId;
}
/** @hide */
- public AutofillId(int sessionId, int parentId, int virtualChildId) {
+ public AutofillId(int parentId, int virtualChildId) {
mVirtual = true;
mViewId = parentId;
mVirtualId = virtualChildId;
- mSessionId = sessionId;
- }
-
- /** @hide */
- public AutofillId(@Nullable AutofillManager afm, int id) {
- this(id);
- mSessionId = afm == null ? AutofillManager.NO_SESSION : afm.getSessionId();
}
/** @hide */
@@ -80,16 +67,6 @@
return mVirtual;
}
- /** @hide */
- public int getSessionId() {
- return mSessionId;
- }
-
- /** @hide */
- public void setSessionId(int sessionId) {
- this.mSessionId = sessionId;
- }
-
/////////////////////////////////
// Object "contract" methods. //
/////////////////////////////////
@@ -100,7 +77,6 @@
int result = 1;
result = prime * result + mViewId;
result = prime * result + mVirtualId;
- result = prime * result + mSessionId;
return result;
}
@@ -112,7 +88,6 @@
final AutofillId other = (AutofillId) obj;
if (mViewId != other.mViewId) return false;
if (mVirtualId != other.mVirtualId) return false;
- if (mSessionId != other.mSessionId) return false;
return true;
}
@@ -122,9 +97,6 @@
if (mVirtual) {
builder.append(':').append(mVirtualId);
}
- if (mSessionId != AutofillManager.NO_SESSION && sDebug) {
- builder.append('<').append(mSessionId).append('>');
- }
return builder.toString();
}
@@ -138,14 +110,12 @@
parcel.writeInt(mViewId);
parcel.writeInt(mVirtual ? 1 : 0);
parcel.writeInt(mVirtualId);
- parcel.writeInt(mSessionId);
}
private AutofillId(Parcel parcel) {
mViewId = parcel.readInt();
mVirtual = parcel.readInt() == 1;
mVirtualId = parcel.readInt();
- mSessionId = parcel.readInt();
}
public static final Parcelable.Creator<AutofillId> CREATOR =
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 612888e..9419e93 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -916,7 +916,7 @@
boolean isVisible, boolean virtual) {
synchronized (mLock) {
if (mEnabled && isActiveLocked()) {
- final AutofillId id = virtual ? getAutofillIdLocked(view, virtualId)
+ final AutofillId id = virtual ? getAutofillId(view, virtualId)
: view.getAutofillId();
if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
if (!isVisible && mFillableIds != null) {
@@ -976,7 +976,7 @@
@GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
int flags) {
- final AutofillId id = getAutofillIdLocked(view, virtualId);
+ final AutofillId id = getAutofillId(view, virtualId);
AutofillCallback callback = null;
if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
@@ -1033,7 +1033,7 @@
if (mEnabled && isActiveLocked()) {
// don't notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
- final AutofillId id = getAutofillIdLocked(view, virtualId);
+ final AutofillId id = getAutofillId(view, virtualId);
// Update focus on existing session.
updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
@@ -1116,7 +1116,7 @@
return;
}
- final AutofillId id = getAutofillIdLocked(view, virtualId);
+ final AutofillId id = getAutofillId(view, virtualId);
updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
}
}
@@ -1137,11 +1137,7 @@
* @param virtualId id identifying the virtual child inside the parent view.
*/
public void notifyViewClicked(@NonNull View view, int virtualId) {
- final AutofillId id;
- synchronized (mLock) {
- id = getAutofillIdLocked(view, virtualId);
- }
- notifyViewClicked(id);
+ notifyViewClicked(getAutofillId(view, virtualId));
}
private void notifyViewClicked(AutofillId id) {
@@ -1538,9 +1534,8 @@
return id;
}
- @GuardedBy("mLock")
- private AutofillId getAutofillIdLocked(View parent, int virtualId) {
- return new AutofillId(mSessionId, parent.getAutofillViewId(), virtualId);
+ private static AutofillId getAutofillId(View parent, int virtualId) {
+ return new AutofillId(parent.getAutofillViewId(), virtualId);
}
@GuardedBy("mLock")
@@ -1571,8 +1566,6 @@
mSessionId = receiver.getIntResult();
if (mSessionId != NO_SESSION) {
mState = STATE_ACTIVE;
- // Need to update the view's autofill id with the session
- id.setSessionId(mSessionId);
}
client.autofillClientResetableStateAvailable();
} catch (RemoteException e) {
@@ -2217,13 +2210,6 @@
return getStateAsString(mState);
}
- /** @hide */
- public int getSessionId() {
- synchronized (mLock) {
- return mSessionId;
- }
- }
-
@NonNull
private static String getStateAsString(int state) {
switch (state) {
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index f45507c..e1600c4 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -89,14 +89,17 @@
*
* @param token special token for the system to identify
* {@link InputMethodService}
+ * @param displayId The id of the display that current IME shown.
+ * Used for {{@link #updateInputMethodDisplay(int)}}
* @param privilegedOperations IPC endpoint to do some privileged
* operations that are allowed only to the
* current IME.
* @hide
*/
@MainThread
- default void initializeInternal(IBinder token,
+ default void initializeInternal(IBinder token, int displayId,
IInputMethodPrivilegedOperations privilegedOperations) {
+ updateInputMethodDisplay(displayId);
attachToken(token);
}
@@ -115,6 +118,15 @@
public void attachToken(IBinder token);
/**
+ * Update context display according to given displayId.
+ *
+ * @param displayId The id of the display that need to update for context.
+ * @hide
+ */
+ @MainThread
+ public void updateInputMethodDisplay(int displayId);
+
+ /**
* Bind a new application environment in to the input method, so that it
* can later start and stop input processing.
* Typically this method is called when this input method is enabled in an
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 89d9ada..c51c5e2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -30,6 +30,7 @@
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -353,28 +354,6 @@
int mCursorCandEnd;
/**
- * Represents an invalid action notification sequence number.
- * {@link com.android.server.InputMethodManagerService} always issues a positive integer for
- * action notification sequence numbers. Thus {@code -1} is guaranteed to be different from any
- * valid sequence number.
- */
- private static final int NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1;
- /**
- * The next sequence number that is to be sent to
- * {@link com.android.server.InputMethodManagerService} via
- * {@link IInputMethodManager#notifyUserAction(int)} at once when a user action is observed.
- */
- private int mNextUserActionNotificationSequenceNumber =
- NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
-
- /**
- * The last sequence number that is already sent to
- * {@link com.android.server.InputMethodManagerService}.
- */
- private int mLastSentUserActionNotificationSequenceNumber =
- NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
-
- /**
* The instance that has previously been sent to the input method.
*/
private CursorAnchorInfo mCursorAnchorInfo = null;
@@ -420,7 +399,6 @@
static final int MSG_SEND_INPUT_EVENT = 5;
static final int MSG_TIMEOUT_INPUT_EVENT = 6;
static final int MSG_FLUSH_INPUT_EVENT = 7;
- static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9;
static final int MSG_REPORT_FULLSCREEN_MODE = 10;
private static boolean isAutofillUIShowing(View servedView) {
@@ -557,12 +535,6 @@
finishedInputEvent(msg.arg1, false, false);
return;
}
- case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
- synchronized (mH) {
- mNextUserActionNotificationSequenceNumber = msg.arg1;
- }
- return;
- }
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
InputConnection ic = null;
@@ -605,11 +577,6 @@
}
@Override
- protected void onUserAction() {
- mParentInputMethodManager.notifyUserAction();
- }
-
- @Override
public String toString() {
return "ControlledInputConnectionWrapper{"
+ "connection=" + getInputConnection()
@@ -656,12 +623,6 @@
}
@Override
- public void setUserActionNotificationSequenceNumber(int sequenceNumber) {
- mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER, sequenceNumber, 0)
- .sendToTarget();
- }
-
- @Override
public void reportFullscreenMode(boolean fullscreen) {
mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0)
.sendToTarget();
@@ -1354,8 +1315,6 @@
mBindSequence = res.sequence;
mCurMethod = res.method;
mCurId = res.id;
- mNextUserActionNotificationSequenceNumber =
- res.userActionNotificationSequenceNumber;
} else if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
@@ -2189,37 +2148,16 @@
/**
* Notify that a user took some action with this input method.
+ *
+ * @deprecated Just kept to avoid possible app compat issue.
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(trackingBug = 114740982, maxTargetSdk = Build.VERSION_CODES.P)
public void notifyUserAction() {
- synchronized (mH) {
- if (mLastSentUserActionNotificationSequenceNumber ==
- mNextUserActionNotificationSequenceNumber) {
- if (DEBUG) {
- Log.w(TAG, "Ignoring notifyUserAction as it has already been sent."
- + " mLastSentUserActionNotificationSequenceNumber: "
- + mLastSentUserActionNotificationSequenceNumber
- + " mNextUserActionNotificationSequenceNumber: "
- + mNextUserActionNotificationSequenceNumber);
- }
- return;
- }
- try {
- if (DEBUG) {
- Log.w(TAG, "notifyUserAction: "
- + " mLastSentUserActionNotificationSequenceNumber: "
- + mLastSentUserActionNotificationSequenceNumber
- + " mNextUserActionNotificationSequenceNumber: "
- + mNextUserActionNotificationSequenceNumber);
- }
- mService.notifyUserAction(mNextUserActionNotificationSequenceNumber);
- mLastSentUserActionNotificationSequenceNumber =
- mNextUserActionNotificationSequenceNumber;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ Log.w(TAG, "notifyUserAction() is a hidden method, which is now just a stub method"
+ + " that does nothing. Leave comments in b.android.com/114740982 if your "
+ + " application still depends on the previous behavior of this method.");
}
/**
@@ -2420,10 +2358,6 @@
+ " mCursorSelEnd=" + mCursorSelEnd
+ " mCursorCandStart=" + mCursorCandStart
+ " mCursorCandEnd=" + mCursorCandEnd);
- p.println(" mNextUserActionNotificationSequenceNumber="
- + mNextUserActionNotificationSequenceNumber
- + " mLastSentUserActionNotificationSequenceNumber="
- + mLastSentUserActionNotificationSequenceNumber);
}
/**
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index e9a9e8f..46b1f6e 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -51,9 +51,6 @@
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 cabba06..5a6aeba 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 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 @@
"/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 @@
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 @@
/**
* 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 @@
};
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 @@
* 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 @@
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 0645a16..d913273 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -313,6 +313,7 @@
* @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 @@
*
* @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 7b25a06..3fd88db 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 @@
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 @@
// 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/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index e998a52..c32894d 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -39,4 +39,6 @@
boolean switchToPreviousInputMethod();
boolean switchToNextInputMethod(boolean onlyCurrentIme);
boolean shouldOfferSwitchingToNextInputMethod();
+
+ oneway void notifyUserActionAsync();
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 7891540..f0e8dc5 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -346,4 +346,20 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#notifyUserActionAsync()}
+ */
+ @AnyThread
+ public void notifyUserActionAsync() {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.notifyUserActionAsync();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index 16ca4fc..1038199 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -455,7 +455,7 @@
* 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 21d750c..e65d114 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/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index d4c4ab3..6f9c87a 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -110,12 +110,6 @@
abstract protected boolean isActive();
- /**
- * Called when the user took some actions that should be taken into consideration to update the
- * LRU list for input method rotation.
- */
- abstract protected void onUserAction();
-
public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
}
@@ -342,7 +336,6 @@
return;
}
ic.commitText((CharSequence)msg.obj, msg.arg1);
- onUserAction();
return;
}
case DO_SET_SELECTION: {
@@ -397,7 +390,6 @@
return;
}
ic.setComposingText((CharSequence)msg.obj, msg.arg1);
- onUserAction();
return;
}
case DO_SET_COMPOSING_REGION: {
@@ -437,7 +429,6 @@
return;
}
ic.sendKeyEvent((KeyEvent)msg.obj);
- onUserAction();
return;
}
case DO_CLEAR_META_KEY_STATES: {
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index b6a654a..97d5a65 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -33,7 +33,7 @@
* {@hide}
*/
oneway interface IInputMethod {
- void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps);
+ void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps);
void bindInput(in InputBinding binding);
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 5c6dea8..2618356 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -27,6 +27,5 @@
// unbindReason corresponds to InputMethodClient.UnbindReason.
void onUnbindMethod(int sequence, int unbindReason);
void setActive(boolean active, boolean fullscreen);
- void setUserActionNotificationSequenceNumber(int sequenceNumber);
void reportFullscreenMode(boolean fullscreen);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5afc53f..543f4a5 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -79,6 +79,4 @@
// This is kept due to @UnsupportedAppUsage.
// TODO(Bug 113914148): Consider removing this.
int getInputMethodWindowVisibleHeight();
-
- oneway void notifyUserAction(int sequenceNumber);
}
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 9d4383f..7548c22 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -60,7 +60,7 @@
/**
* Indicates that this is a temporary binding until the
* {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
- * to {@link com.android.server.InputMethodManagerService} (IMMS).
+ * to {@link com.android.server.inputmethod.InputMethodManagerService} (IMMS).
*
* <p>Note that in this state the IMS is already bound to IMMS but the logical session
* is not yet established on top of the IPC channel.</p>
@@ -73,7 +73,7 @@
/**
* Indicates that this is a temporary binding until the
* {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
- * to {@link com.android.server.InputMethodManagerService} (IMMS).
+ * to {@link com.android.server.inputmethod.InputMethodManagerService} (IMMS).
*
* <p>Note that in this state the IMMS has already initiated a connection to the IMS but
* the binding process is not completed yet.</p>
@@ -91,12 +91,14 @@
int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 3;
/**
* Indicates somehow
- * {@link com.android.server.InputMethodManagerService#startInputOrWindowGainedFocus} is
- * trying to return null {@link InputBindResult}, which must never happen.
+ * {@link
+ * com.android.server.inputmethod.InputMethodManagerService#startInputOrWindowGainedFocus}
+ * is trying to return null {@link InputBindResult}, which must never happen.
*/
int ERROR_NULL = 4;
/**
- * Indicates that {@link com.android.server.InputMethodManagerService} recognizes no IME.
+ * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService}
+ * recognizes no IME.
*/
int ERROR_NO_IME = 5;
/**
@@ -114,8 +116,8 @@
*/
int ERROR_SYSTEM_NOT_READY = 7;
/**
- * Indicates that {@link com.android.server.InputMethodManagerService} tried to connect to
- * an {@link android.inputmethodservice.InputMethodService} but failed.
+ * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} tried to
+ * connect to an {@link android.inputmethodservice.InputMethodService} but failed.
*
* @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)
*/
@@ -137,7 +139,8 @@
* The client should try to restart input when its {@link android.view.Window} is focused
* again.</p>
*
- * @see com.android.server.wm.WindowManagerService#inputMethodClientHasFocus(IInputMethodClient)
+ * @see com.android.server.wm.WindowManagerInternal#inputMethodClientHasFocus(
+ * IInputMethodClient)
*/
int ERROR_NOT_IME_TARGET_WINDOW = 11;
/**
@@ -171,20 +174,13 @@
*/
public final int sequence;
- /**
- * Sequence number of user action notification.
- */
- public final int userActionNotificationSequenceNumber;
-
public InputBindResult(@ResultCode int _result,
- IInputMethodSession _method, InputChannel _channel,
- String _id, int _sequence, int _userActionNotificationSequenceNumber) {
+ IInputMethodSession _method, InputChannel _channel, String _id, int _sequence) {
result = _result;
method = _method;
channel = _channel;
id = _id;
sequence = _sequence;
- userActionNotificationSequenceNumber = _userActionNotificationSequenceNumber;
}
InputBindResult(Parcel source) {
@@ -197,14 +193,12 @@
}
id = source.readString();
sequence = source.readInt();
- userActionNotificationSequenceNumber = source.readInt();
}
@Override
public String toString() {
return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
+ " sequence=" + sequence
- + " userActionNotificationSequenceNumber=" + userActionNotificationSequenceNumber
+ "}";
}
@@ -226,7 +220,6 @@
}
dest.writeString(id);
dest.writeInt(sequence);
- dest.writeInt(userActionNotificationSequenceNumber);
}
/**
@@ -285,7 +278,7 @@
}
private static InputBindResult error(@ResultCode int result) {
- return new InputBindResult(result, null, null, null, -1, -1);
+ return new InputBindResult(result, null, null, null, -1);
}
/**
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 5b65bbe..3bd3072 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -371,6 +371,7 @@
public boolean commitText(CharSequence text, int newCursorPosition) {
try {
mIInputContext.commitText(text, newCursorPosition);
+ notifyUserActionIfNecessary();
return true;
} catch (RemoteException e) {
return false;
@@ -378,6 +379,16 @@
}
@AnyThread
+ private void notifyUserActionIfNecessary() {
+ final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ if (inputMethodService == null) {
+ // This basically should not happen, because it's the the caller of this method.
+ return;
+ }
+ inputMethodService.notifyUserActionIfNecessary();
+ }
+
+ @AnyThread
public boolean commitCompletion(CompletionInfo text) {
if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
// This method is not implemented.
@@ -449,6 +460,7 @@
public boolean setComposingText(CharSequence text, int newCursorPosition) {
try {
mIInputContext.setComposingText(text, newCursorPosition);
+ notifyUserActionIfNecessary();
return true;
} catch (RemoteException e) {
return false;
@@ -489,6 +501,7 @@
public boolean sendKeyEvent(KeyEvent event) {
try {
mIInputContext.sendKeyEvent(event);
+ notifyUserActionIfNecessary();
return true;
} catch (RemoteException e) {
return false;
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6e661e1..3ead633 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -72,6 +72,9 @@
// 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 f845bd0..9ab55d6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -584,7 +584,7 @@
<protected-broadcast android:name="android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED" />
<protected-broadcast android:name="android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
<protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
- <protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
+ <protected-broadcast android:name="com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
<!-- Time zone rules update intents fired by the system server -->
<protected-broadcast android:name="com.android.intent.action.timezone.RULES_UPDATE_OPERATION" />
@@ -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> -->
@@ -4120,11 +4120,6 @@
<permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS"
android:protectionLevel="signature" />
- <!-- Allows an application to read emergency info name.
- @hide <p>Not for use by third-party applications. -->
- <permission android:name="com.android.emergency.permission.READ_EMERGENCY_INFO_NAME"
- android:protectionLevel="signature" />
-
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 6932be3..e153082 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 d4b800a..6afe603 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 8b3f287..6a5f287 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 4cada60..4915111 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 dca94ff..e228d35 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 351f8ea..6bbd258 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 5e3675a..c8d4d05 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 @@
-->
<!-- 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 4eb2ff3..45cf0f0 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 e31f161..c14bce4 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 8b89a97..0f353f2 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 b175f75..35592a6 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 377982a..0712cbc 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 04df180..1be47ba 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 0b04e08..966a5a5 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 bd4ea2a..bd36ed6 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 3fed8a3..2de2b9e 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 bf7e068..8bca211 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 fa31dce..c751af3 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 339f9c7..e6e0de3 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 92cca72..7b6e064 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 3937af5..a7530ce 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -869,13 +869,6 @@
<!-- 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 14e5082..92096ab 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -719,6 +719,13 @@
<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 3bc93cd..369cee3 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 5ef30a8..dcc51e1 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 @@
@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 @@
@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 @@
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 @@
@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 @@
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/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 9c9f11b..20fe162 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -17,6 +17,22 @@
package android.os;
import static android.os.FileUtils.roundStorageSize;
+import static android.os.FileUtils.translateModePfdToPosix;
+import static android.os.FileUtils.translateModePosixToPfd;
+import static android.os.FileUtils.translateModePosixToString;
+import static android.os.FileUtils.translateModeStringToPosix;
+import static android.os.ParcelFileDescriptor.MODE_APPEND;
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.O_APPEND;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_RDONLY;
+import static android.system.OsConstants.O_RDWR;
+import static android.system.OsConstants.O_TRUNC;
+import static android.system.OsConstants.O_WRONLY;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
@@ -476,6 +492,32 @@
assertEquals(G64, roundStorageSize(G32 + 1));
}
+ @Test
+ public void testTranslateMode() throws Exception {
+ assertTranslate("r", O_RDONLY, MODE_READ_ONLY);
+
+ assertTranslate("rw", O_RDWR | O_CREAT,
+ MODE_READ_WRITE | MODE_CREATE);
+ assertTranslate("rwt", O_RDWR | O_CREAT | O_TRUNC,
+ MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
+ assertTranslate("rwa", O_RDWR | O_CREAT | O_APPEND,
+ MODE_READ_WRITE | MODE_CREATE | MODE_APPEND);
+
+ assertTranslate("w", O_WRONLY | O_CREAT,
+ MODE_WRITE_ONLY | MODE_CREATE | MODE_CREATE);
+ assertTranslate("wt", O_WRONLY | O_CREAT | O_TRUNC,
+ MODE_WRITE_ONLY | MODE_CREATE | MODE_TRUNCATE);
+ assertTranslate("wa", O_WRONLY | O_CREAT | O_APPEND,
+ MODE_WRITE_ONLY | MODE_CREATE | MODE_APPEND);
+ }
+
+ private static void assertTranslate(String string, int posix, int pfd) {
+ assertEquals(posix, translateModeStringToPosix(string));
+ assertEquals(string, translateModePosixToString(posix));
+ assertEquals(pfd, translateModePosixToPfd(posix));
+ assertEquals(posix, translateModePfdToPosix(pfd));
+ }
+
private static void assertNameEquals(String expected, File actual) {
assertEquals(expected, actual.getName());
}
diff --git a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
index 920988b..9e44554 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.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 @@
}
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/webkit/Android.mk b/core/tests/webkit/Android.mk
deleted file mode 100644
index 45f6957..0000000
--- 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 42accdf..0000000
--- 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 4c50b7d..0000000
--- 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 00e540a..0000000
--- 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 e18a7e0..0000000
--- 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 868b238..0000000
--- 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/Android.mk b/core/tests/webkit/apk_with_native_libs/jni/Android.mk
deleted file mode 100644
index fd5b5eb..0000000
--- a/core/tests/webkit/apk_with_native_libs/jni/Android.mk
+++ /dev/null
@@ -1,32 +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)
-
-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)
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 0ced4ee..0000000
--- 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 ffffeb8..0000000
--- 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/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java b/core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java
deleted file mode 100644
index 0efa4b4..0000000
--- a/core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java
+++ /dev/null
@@ -1,24 +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 com.android.webview.chromium;
-
-/**
- * An empty class for testing purposes.
- */
-public class WebViewLoadingTestClass {
-}
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 e2f2d37..0000000
--- 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 d84a207..949c9445 100644
--- a/libs/androidfw/include/androidfw/ByteBucketArray.h
+++ b/libs/androidfw/include/androidfw/ByteBucketArray.h
@@ -60,7 +60,7 @@
}
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/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 0476222..5ad7365 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -715,6 +715,7 @@
break;
case AudioSystem.STREAM_TTS:
mContentType = CONTENT_TYPE_SONIFICATION;
+ mFlags |= FLAG_BEACON;
break;
case AudioSystem.STREAM_ACCESSIBILITY:
mContentType = CONTENT_TYPE_SPEECH;
@@ -1039,6 +1040,10 @@
return fromGetVolumeControlStream ?
AudioSystem.STREAM_VOICE_CALL : AudioSystem.STREAM_BLUETOOTH_SCO;
}
+ if ((aa.getAllFlags() & FLAG_BEACON) == FLAG_BEACON) {
+ return fromGetVolumeControlStream ?
+ AudioSystem.STREAM_MUSIC : AudioSystem.STREAM_TTS;
+ }
// usage to stream type mapping
switch (aa.getUsage()) {
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 339a7ee..7681cc3 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -91,6 +91,7 @@
"android_media_Media2HTTPConnection.cpp",
"android_media_Media2HTTPService.cpp",
"android_media_Media2DataSource.cpp",
+ "android_media_MediaMetricsJNI.cpp",
"android_media_MediaPlayer2.cpp",
"android_media_SyncParams.cpp",
],
@@ -98,7 +99,6 @@
shared_libs: [
"android.hardware.cas@1.0", // for CasManager. VNDK???
"android.hardware.cas.native@1.0", // CasManager. VNDK???
- "libandroid_runtime", // ???
"libaudioclient", // for use of AudioTrack, AudioSystem. to be removed
"libbinder",
"libgui", // for VideoFrameScheduler
@@ -120,13 +120,9 @@
header_libs: ["libhardware_headers"],
static_libs: [
- "libbacktrace",
"libbase",
- "libc_malloc_debug_backtrace",
"libcrypto",
"libcutils",
- "libdexfile",
- "liblzma",
"libmedia_helper",
"libmedia_player2_util",
"libmediadrm",
@@ -135,7 +131,7 @@
"libmediaplayer2",
"libmediaplayer2-protos",
"libmediautils",
- "libnetd_client",
+ "libnetd_client", // for setNetworkForUser
"libprotobuf-cpp-lite",
"libstagefright_esds",
"libstagefright_foundation",
@@ -146,9 +142,6 @@
"libstagefright_player2",
"libstagefright_rtsp",
"libstagefright_timedtext2",
- "libunwindstack",
- "libutilscallstack",
- "libziparchive",
],
group_static_libs: true,
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp
new file mode 100644
index 0000000..3ded8c2
--- /dev/null
+++ b/media/jni/android_media_MediaMetricsJNI.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 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>
+#include <nativehelper/JNIHelp.h>
+
+#include "android_media_MediaMetricsJNI.h"
+#include <media/MediaAnalyticsItem.h>
+
+
+// Copeid from core/jni/ (libandroid_runtime.so)
+namespace android {
+
+// place the attributes into a java PersistableBundle object
+jobject MediaMetricsJNI::writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle) {
+
+ jclass clazzBundle = env->FindClass("android/os/PersistableBundle");
+ if (clazzBundle==NULL) {
+ ALOGD("can't find android/os/PersistableBundle");
+ return NULL;
+ }
+ // sometimes the caller provides one for us to fill
+ if (mybundle == NULL) {
+ // create the bundle
+ jmethodID constructID = env->GetMethodID(clazzBundle, "<init>", "()V");
+ mybundle = env->NewObject(clazzBundle, constructID);
+ if (mybundle == NULL) {
+ return NULL;
+ }
+ }
+
+ // grab methods that we can invoke
+ jmethodID setIntID = env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V");
+ jmethodID setLongID = env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V");
+ jmethodID setDoubleID = env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V");
+ jmethodID setStringID = env->GetMethodID(clazzBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V");
+
+ // env, class, method, {parms}
+ //env->CallVoidMethod(env, mybundle, setIntID, jstr, jint);
+
+ // iterate through my attributes
+ // -- get name, get type, get value
+ // -- insert appropriately into the bundle
+ for (size_t i = 0 ; i < item->mPropCount; i++ ) {
+ MediaAnalyticsItem::Prop *prop = &item->mProps[i];
+ // build the key parameter from prop->mName
+ jstring keyName = env->NewStringUTF(prop->mName);
+ // invoke the appropriate method to insert
+ switch (prop->mType) {
+ case MediaAnalyticsItem::kTypeInt32:
+ env->CallVoidMethod(mybundle, setIntID,
+ keyName, (jint) prop->u.int32Value);
+ break;
+ case MediaAnalyticsItem::kTypeInt64:
+ env->CallVoidMethod(mybundle, setLongID,
+ keyName, (jlong) prop->u.int64Value);
+ break;
+ case MediaAnalyticsItem::kTypeDouble:
+ env->CallVoidMethod(mybundle, setDoubleID,
+ keyName, (jdouble) prop->u.doubleValue);
+ break;
+ case MediaAnalyticsItem::kTypeCString:
+ env->CallVoidMethod(mybundle, setStringID, keyName,
+ env->NewStringUTF(prop->u.CStringValue));
+ break;
+ default:
+ ALOGE("to_String bad item type: %d for %s",
+ prop->mType, prop->mName);
+ break;
+ }
+ }
+
+ return mybundle;
+}
+
+}; // namespace android
+
diff --git a/media/jni/android_media_MediaMetricsJNI.h b/media/jni/android_media_MediaMetricsJNI.h
new file mode 100644
index 0000000..fd621ea
--- /dev/null
+++ b/media/jni/android_media_MediaMetricsJNI.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef _ANDROID_MEDIA_MEDIAMETRICSJNI_H_
+#define _ANDROID_MEDIA_MEDIAMETRICSJNI_H_
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <media/MediaAnalyticsItem.h>
+
+// Copeid from core/jni/ (libandroid_runtime.so)
+namespace android {
+
+class MediaMetricsJNI {
+public:
+ static jobject writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle);
+};
+
+}; // namespace android
+
+#endif // _ANDROID_MEDIA_MEDIAMETRICSJNI_H_
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 801dade..d4c84b5 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -909,7 +909,7 @@
}
static jboolean
-android_media_MediaPlayer2_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
+android_media_MediaPlayer2_setParameter(JNIEnv *env, jobject thiz, jint key, jobject)
{
ALOGV("setParameter: key %d", key);
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
@@ -918,9 +918,11 @@
return false;
}
- // TODO: parcelForJavaObject() shouldn't be used since it's dependent on
- // framework's Parcel implementation. This setParameter() is used
- // only with AudioAttribute. Can this be used as jobject with JAudioTrack?
+ return false;
+ // TODO: set/getParameter is temporarily disabled to remove android_runtime.so dependency.
+ // Once JAudioTrack migration is done, the AudioAttribute jobject
+ // should be directly passed to AudioTrack without native parcel conversion.
+ /*
Parcel *request = parcelForJavaObject(env, java_request);
status_t err = mp->setParameter(key, *request);
if (err == OK) {
@@ -928,6 +930,7 @@
} else {
return false;
}
+ */
}
static jobject
@@ -940,6 +943,11 @@
return NULL;
}
+ return NULL;
+ // TODO: set/getParameter is temporarily disabled to remove android_runtime.so dependency.
+ // Once JAudioTrack migration is done, the AudioAttribute jobject
+ // should be directly passed to AudioTrack without native parcel conversion.
+ /*
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
@@ -950,6 +958,7 @@
}
}
return jParcel;
+ */
}
static void
@@ -1005,7 +1014,11 @@
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];
@@ -1019,55 +1032,6 @@
return out;
}
-// Sends the new filter to the client.
-static jint
-android_media_MediaPlayer2_setMetadataFilter(JNIEnv *env, jobject thiz, jobject request)
-{
- sp<MediaPlayer2> media_player = getMediaPlayer(env, thiz);
- if (media_player == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return UNKNOWN_ERROR;
- }
-
- Parcel *filter = parcelForJavaObject(env, request);
-
- if (filter == NULL ) {
- jniThrowException(env, "java/lang/RuntimeException", "Filter is null");
- return UNKNOWN_ERROR;
- }
-
- return (jint) media_player->setMetadataFilter(*filter);
-}
-
-static jboolean
-android_media_MediaPlayer2_getMetadata(JNIEnv *env, jobject thiz, jboolean update_only,
- jboolean apply_filter, jobject reply)
-{
- sp<MediaPlayer2> media_player = getMediaPlayer(env, thiz);
- if (media_player == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return JNI_FALSE;
- }
-
- Parcel *metadata = parcelForJavaObject(env, reply);
-
- if (metadata == NULL ) {
- jniThrowException(env, "java/lang/RuntimeException", "Reply parcel is null");
- return JNI_FALSE;
- }
-
- metadata->freeData();
- // On return metadata is positioned at the beginning of the
- // metadata. Note however that the parcel actually starts with the
- // return code so you should not rewind the parcel using
- // setDataPosition(0).
- if (media_player->getMetadata(update_only, apply_filter, metadata) == OK) {
- return JNI_TRUE;
- } else {
- return JNI_FALSE;
- }
-}
-
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in MediaPlayer2, which won't run until the
// first time an instance of this class is used.
@@ -1532,8 +1496,6 @@
{"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping},
{"_setVolume", "(FF)V", (void *)android_media_MediaPlayer2_setVolume},
{"_invoke", "([B)[B", (void *)android_media_MediaPlayer2_invoke},
- {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer2_setMetadataFilter},
- {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer2_getMetadata},
{"native_init", "()V", (void *)android_media_MediaPlayer2_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer2_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer2_native_finalize},
diff --git a/native/webview/loader/loader.cpp b/native/webview/loader/loader.cpp
index adb371d..fee2a25 100644
--- a/native/webview/loader/loader.cpp
+++ b/native/webview/loader/loader.cpp
@@ -64,7 +64,8 @@
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 @@
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 @@
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 @@
{ "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/Android.bp b/packages/CarSystemUI/Android.bp
index f908595..86f9032 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -51,6 +51,7 @@
libs: [
"telephony-common",
"android.car",
+ "android.car.user",
],
manifest: "AndroidManifest.xml",
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 6473f0d..0467bff 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 @@
}
}
textView.getViewTreeObserver().removeOnPreDrawListener(this);
- return false;
+ return true;
}
});
textView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/CarrierDefaultApp/Android.mk b/packages/CarrierDefaultApp/Android.mk
index 5068b3b..df88afd 100644
--- a/packages/CarrierDefaultApp/Android.mk
+++ b/packages/CarrierDefaultApp/Android.mk
@@ -9,8 +9,6 @@
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
-LOCAL_STATIC_JAVA_LIBRARIES := services.net
-
include $(BUILD_PACKAGE)
# This finds and builds the test apk as well, so a single make does both.
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index b1933373..4f67350 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -32,7 +32,6 @@
import android.net.Proxy;
import android.net.TrafficStats;
import android.net.Uri;
-import android.net.dns.ResolvUtil;
import android.net.http.SslError;
import android.os.Bundle;
import android.telephony.CarrierConfigManager;
@@ -159,9 +158,9 @@
private void setNetwork(Network network) {
if (network != null) {
+ network = network.getPrivateDnsBypassingCopy();
mCm.bindProcessToNetwork(network);
- mCm.setProcessDefaultNetworkForHostResolution(
- ResolvUtil.getNetworkWithUseLocalNameserversFlag(network));
+ mCm.setProcessDefaultNetworkForHostResolution(network);
}
mNetwork = network;
}
@@ -242,7 +241,6 @@
private void testForCaptivePortal() {
mTestingThread = new Thread(new Runnable() {
public void run() {
- final Network network = ResolvUtil.makeNetworkWithPrivateDnsBypass(mNetwork);
// Give time for captive portal to open.
try {
Thread.sleep(1000);
@@ -253,7 +251,7 @@
int httpResponseCode = 500;
int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
try {
- urlConnection = (HttpURLConnection) network.openConnection(
+ urlConnection = (HttpURLConnection) mNetwork.openConnection(
new URL(mCm.getCaptivePortalServerUrl()));
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index ab2892f..d29bd65 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 0c4e02b..e13e566 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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 649900b..b9f7323 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 @@
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 @@
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 @@
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 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/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index f9f6233..4b6a22c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -36,7 +36,6 @@
*/
final class HfpClientProfile implements LocalBluetoothProfile {
private static final String TAG = "HfpClientProfile";
- private static boolean V = false;
private BluetoothHeadsetClient mService;
private boolean mIsProfileReady;
@@ -60,7 +59,7 @@
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (V) Log.d(TAG,"Bluetooth service connected");
+ Log.d(TAG, "Bluetooth service connected");
mService = (BluetoothHeadsetClient) proxy;
// We just bound to the service, so refresh the UI for any connected HFP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,7 +80,7 @@
@Override
public void onServiceDisconnected(int profile) {
- if (V) Log.d(TAG,"Bluetooth service disconnected");
+ Log.d(TAG, "Bluetooth service disconnected");
mIsProfileReady=false;
}
}
@@ -115,7 +114,9 @@
}
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,
@@ -124,23 +125,17 @@
@Override
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);
}
@Override
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){
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -158,19 +153,25 @@
@Override
public boolean isPreferred(BluetoothDevice device) {
- if (mService == null) return false;
+ if (mService == null) {
+ return false;
+ }
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
}
@Override
public int getPreferred(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ if (mService == null) {
+ return BluetoothProfile.PRIORITY_OFF;
+ }
return mService.getPriority(device);
}
@Override
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);
@@ -216,7 +217,7 @@
}
protected void finalize() {
- if (V) Log.d(TAG, "finalize()");
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
diff --git a/packages/SettingsLib/tests/robotests/config/robolectric.properties b/packages/SettingsLib/tests/robotests/config/robolectric.properties
index 34a2a1a..6b5b8e5 100644
--- a/packages/SettingsLib/tests/robotests/config/robolectric.properties
+++ b/packages/SettingsLib/tests/robotests/config/robolectric.properties
@@ -1,2 +1,5 @@
manifest=frameworks/base/packages/SettingsLib/tests/robotests/AndroidManifest.xml
-sdk=NEWEST_SDK
\ No newline at end of file
+sdk=NEWEST_SDK
+
+shadows=\
+ com.android.settingslib.testutils.shadow.ShadowXmlUtils
\ No newline at end of file
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 0000000..274fff8
--- /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/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
new file mode 100644
index 0000000..187be0b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.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.BluetoothHeadsetClient;
+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 HfpClientProfileTest {
+
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothHeadsetClient mService;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private HfpClientProfile mProfile;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mProfile = new HfpClientProfile(RuntimeEnvironment.application,
+ mDeviceManager, mProfileManager);
+ mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+ mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, mService);
+ }
+
+ @Test
+ public void connect_shouldConnectBluetoothHeadsetClient() {
+ mProfile.connect(mBluetoothDevice);
+ verify(mService).connect(mBluetoothDevice);
+ }
+
+ @Test
+ public void disconnect_shouldDisconnectBluetoothHeadsetClient() {
+ 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);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowXmlUtils.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowXmlUtils.java
new file mode 100644
index 0000000..3455765
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowXmlUtils.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.testutils.shadow;
+
+import static org.robolectric.shadow.api.Shadow.directlyOn;
+
+import com.android.internal.util.XmlUtils;
+
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.util.ReflectionHelpers;
+
+@Implements(XmlUtils.class)
+public class ShadowXmlUtils {
+
+ @Implementation
+ public static final int convertValueToInt(CharSequence charSeq, int defaultValue) {
+ final Class<?> xmlUtilsClass = ReflectionHelpers.loadClass(
+ Robolectric.class.getClassLoader(), "com.android.internal.util.XmlUtils");
+ try {
+ return directlyOn(xmlUtilsClass, "convertValueToInt",
+ ReflectionHelpers.ClassParameter.from(CharSequence.class, charSeq),
+ ReflectionHelpers.ClassParameter.from(int.class, new Integer(defaultValue)));
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+}
\ 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 c6ea480..bd21b83 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 @@
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 4fc190d..ec35b3d 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 @@
private static final Bundle sNotificationBundle = new Bundle();
private boolean mIsWatch;
+ private boolean mIsTv;
private int mLastProgressPercent;
@@ -255,6 +258,9 @@
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 @@
.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/Android.bp b/packages/SystemUI/Android.bp
index 0913503..9c33116 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -66,6 +66,7 @@
libs: [
"telephony-common",
"android.car",
+ "android.car.user",
],
aaptflags: [
@@ -119,6 +120,7 @@
"android.test.runner",
"telephony-common",
"android.car",
+ "android.car.user",
"android.test.base",
],
aaptflags: [
@@ -144,6 +146,7 @@
libs: [
"telephony-common",
"android.car",
+ "android.car.user",
],
dxflags: ["--multi-dex"],
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 45d2185..2c5120d 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 67db68d..216ed68 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 @@
}
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 2cbb78a..3744105 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 @@
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/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 6057614..aac37a2 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -38,7 +38,8 @@
android.test.runner \
telephony-common \
android.test.base \
- android.car
+ android.car \
+ android.car.user
LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard
diff --git a/packages/VpnDialogs/res/values-es-rUS/strings.xml b/packages/VpnDialogs/res/values-es-rUS/strings.xml
index 3732ebc..21cfc04 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/proto/src/metrics_constants/OWNERS b/proto/src/metrics_constants/OWNERS
new file mode 100644
index 0000000..7009282
--- /dev/null
+++ b/proto/src/metrics_constants/OWNERS
@@ -0,0 +1,4 @@
+cwren@android.com
+yanglu@google.com
+yaochen@google.com
+yro@google.com
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
similarity index 100%
rename from proto/src/metrics_constants.proto
rename to proto/src/metrics_constants/metrics_constants.proto
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b2e06f9..9bee8db 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.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 @@
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 @@
scheduleUpdateClientsIfNeededLocked(userState);
updateRelevantEventsLocked(userState);
updateAccessibilityButtonTargetsLocked(userState);
+ updateMinimumUiTimeoutLocked(userState);
}
private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
@@ -1940,6 +1961,7 @@
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
somethingChanged |= readAccessibilityButtonSettingsLocked(userState);
+ somethingChanged |= readUserMinimumUiTimeoutSettingsLocked(userState);
return somethingChanged;
}
@@ -2084,6 +2106,22 @@
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 @@
}
}
+ 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 @@
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 @@
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 @@
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 @@
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 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 @@
mWindowInfoById.valueAt(i).recycle();
}
mWindowInfoById.clear();
+ mHasWatchOutsideTouchWindow = false;
mFocusedWindowId = INVALID_WINDOW_ID;
if (!mTouchInteractionInProgress) {
@@ -3156,6 +3266,9 @@
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 @@
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 ComponentName mServiceToEnableWithShortcut;
public int mLastSentClientState = -1;
+ public int mMinimumUiTimeout = 0;
private int mSoftKeyboardShowMode = 0;
@@ -3674,6 +3804,8 @@
public boolean mIsPerformGesturesEnabled;
public boolean mIsFilterKeyEventsEnabled;
public boolean mAccessibilityFocusOnlyInActiveWindow;
+ public boolean mUserMinimumUiTimeoutEnabled;
+ public int mUserMinimumUiTimeout;
public boolean mBindInstantServiceAllowed;
@@ -3713,6 +3845,9 @@
// 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 @@
mServiceAssignedToAccessibilityButton = null;
mIsNavBarMagnificationAssignedToAccessibilityButton = false;
mIsAutoclickEnabled = false;
+ mUserMinimumUiTimeoutEnabled = false;
+ mUserMinimumUiTimeout = 0;
}
public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -3948,6 +4085,12 @@
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 @@
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 @@
if (readAccessibilityButtonSettingsLocked(userState)) {
onUserStateChangedLocked(userState);
}
+ } else if (mUserMinimumUiTimeoutEnabledUri.equals(uri)
+ || mUserMinimumUiTimeoutUri.equals(uri)) {
+ if (readUserMinimumUiTimeoutSettingsLocked(userState)) {
+ updateMinimumUiTimeoutLocked(userState);
+ }
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index c612c88..8c8352f 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -378,7 +378,6 @@
if (newSession == null) {
return NO_SESSION;
}
- autofillId.setSessionId(newSession.id);
final String historyItem =
"id=" + newSession.id + " uid=" + uid + " a=" + shortComponentName
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a9257f6..c9eb2d2 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -23,7 +23,6 @@
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
-import static android.view.autofill.AutofillManager.NO_SESSION;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.Helper.getNumericValue;
@@ -76,6 +75,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -128,7 +128,7 @@
private final MetricsLogger mMetricsLogger = new MetricsLogger();
- private static AtomicInteger sIdCounter = new AtomicInteger(1);
+ private static AtomicInteger sIdCounter = new AtomicInteger();
/** ID of the session */
public final int id;
@@ -385,7 +385,6 @@
@Override
public AutofillValue findRawValueByAutofillId(AutofillId id) {
- if (id == null) return null;
synchronized (mLock) {
return findValueLocked(id);
}
@@ -398,50 +397,39 @@
@GuardedBy("mLock")
@Nullable
private AutofillValue findValueLocked(@NonNull AutofillId autofillId) {
- // TODO(b/113281366): rather than explicitly look for previous session, it might be
- // better to merge the sessions when created (see note on mergePreviousSessionLocked())
- final int requiredId = autofillId.getSessionId();
- Session rightSession = null;
- if (requiredId == NO_SESSION || requiredId == id) {
- rightSession = this;
- } else {
- final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this);
- if (previousSessions == null) {
- if (sVerbose) Slog.v(TAG, "findValue(): no previous sessions");
- return null;
- }
+ final AutofillValue value = findValueFromThisSessionOnlyLocked(autofillId);
+ if (value != null) return value;
+
+ // TODO(b/113281366): rather than explicitly look for previous session, it might be better
+ // to merge the sessions when created (see note on mergePreviousSessionLocked())
+ final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this);
+ if (previousSessions != null) {
if (sDebug) {
- Slog.d(TAG, "findValue(): looking on " + previousSessions.size()
+ Slog.d(TAG, "findValueLocked(): looking on " + previousSessions.size()
+ " previous sessions for autofillId " + autofillId);
}
for (int i = 0; i < previousSessions.size(); i++) {
final Session previousSession = previousSessions.get(i);
- if (previousSession.id == requiredId) {
- rightSession = previousSession;
- break;
+ final AutofillValue previousValue = previousSession
+ .findValueFromThisSessionOnlyLocked(autofillId);
+ if (previousValue != null) {
+ return previousValue;
}
}
}
- if (rightSession == null) {
- Slog.w(TAG, "findValue(): no session with id" + requiredId);
- return null;
- }
- return rightSession.findValueFromThisSessionOnlyLocked(autofillId);
+ return null;
}
- @GuardedBy("mLock")
@Nullable
private AutofillValue findValueFromThisSessionOnlyLocked(@NonNull AutofillId autofillId) {
final ViewState state = mViewStates.get(autofillId);
if (state == null) {
- if (sDebug) {
- Slog.d(TAG, "findValueLocked(): no view state for " + autofillId + " on " + id);
- }
+ if (sDebug) Slog.d(TAG, "findValueLocked(): no view state for " + autofillId);
return null;
}
AutofillValue value = state.getCurrentValue();
if (value == null) {
- Slog.d(TAG, "findValueLocked(): no current value for " + autofillId + " on " + id);
+ if (sDebug) Slog.d(TAG, "findValueLocked(): no current value for " + autofillId);
value = getValueFromContextsLocked(autofillId);
}
return value;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index c26ac17..1b97926 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -698,8 +698,6 @@
@GuardedBy("mQueueLock")
private ArrayList<FullBackupEntry> mFullBackupQueue;
- private BackupPolicyEnforcer mBackupPolicyEnforcer;
-
// Utility: build a new random integer token. The low bits are the ordinal of the
// operation for near-time uniqueness, and the upper bits are random for app-
// side unpredictability.
@@ -899,8 +897,6 @@
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
-
- mBackupPolicyEnforcer = new BackupPolicyEnforcer(context);
}
private void initPackageTracking() {
@@ -2827,10 +2823,6 @@
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
- if (!enable && mBackupPolicyEnforcer.getMandatoryBackupTransport() != null) {
- Slog.w(TAG, "Cannot disable backups when the mandatory backups policy is active.");
- return;
- }
Slog.i(TAG, "Backup enabled => " + enable);
@@ -3085,12 +3077,6 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "selectBackupTransport");
- if (!isAllowedByMandatoryBackupTransportPolicy(transportName)) {
- // Don't change the transport if it is not allowed.
- Slog.w(TAG, "Failed to select transport - disallowed by device owner policy.");
- return mTransportManager.getCurrentTransportName();
- }
-
final long oldId = Binder.clearCallingIdentity();
try {
String previousTransportName = mTransportManager.selectTransport(transportName);
@@ -3105,20 +3091,10 @@
@Override
public void selectBackupTransportAsync(
- ComponentName transportComponent, @Nullable ISelectBackupTransportCallback listener) {
+ ComponentName transportComponent, ISelectBackupTransportCallback listener) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
- if (!isAllowedByMandatoryBackupTransportPolicy(transportComponent)) {
- try {
- if (listener != null) {
- Slog.w(TAG, "Failed to select transport - disallowed by device owner policy.");
- listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
- }
- return;
- }
+
final long oldId = Binder.clearCallingIdentity();
try {
String transportString = transportComponent.flattenToShortString();
@@ -3140,12 +3116,10 @@
}
try {
- if (listener != null) {
- if (transportName != null) {
- listener.onSuccess(transportName);
- } else {
- listener.onFailure(result);
- }
+ if (transportName != null) {
+ listener.onSuccess(transportName);
+ } else {
+ listener.onFailure(result);
}
} catch (RemoteException e) {
Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
@@ -3156,38 +3130,6 @@
}
}
- /**
- * Returns if the specified transport can be set as the current transport without violating the
- * mandatory backup transport policy.
- */
- private boolean isAllowedByMandatoryBackupTransportPolicy(String transportName) {
- ComponentName mandatoryBackupTransport = mBackupPolicyEnforcer.getMandatoryBackupTransport();
- if (mandatoryBackupTransport == null) {
- return true;
- }
- final String mandatoryBackupTransportName;
- try {
- mandatoryBackupTransportName =
- mTransportManager.getTransportName(mandatoryBackupTransport);
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "mandatory backup transport not registered!");
- return false;
- }
- return TextUtils.equals(mandatoryBackupTransportName, transportName);
- }
-
- /**
- * Returns if the specified transport can be set as the current transport without violating the
- * mandatory backup transport policy.
- */
- private boolean isAllowedByMandatoryBackupTransportPolicy(ComponentName transport) {
- ComponentName mandatoryBackupTransport = mBackupPolicyEnforcer.getMandatoryBackupTransport();
- if (mandatoryBackupTransport == null) {
- return true;
- }
- return mandatoryBackupTransport.equals(transport);
- }
-
private void updateStateForTransport(String newTransportName) {
// Publish the name change
Settings.Secure.putString(mContext.getContentResolver(),
diff --git a/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java b/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java
deleted file mode 100644
index 158084a..0000000
--- a/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.android.server.backup;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * A helper class to decouple this service from {@link DevicePolicyManager} in order to improve
- * testability.
- */
-@VisibleForTesting
-public class BackupPolicyEnforcer {
- private DevicePolicyManager mDevicePolicyManager;
-
- public BackupPolicyEnforcer(Context context) {
- mDevicePolicyManager =
- (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
- }
-
- public ComponentName getMandatoryBackupTransport() {
- return mDevicePolicyManager.getMandatoryBackupTransport();
- }
-}
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 0000000..1ae598e
--- /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 499c03d..ad2f82c 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.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 @@
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 4016d29..e21a3d7 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_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.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 @@
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 01e8152..380f6a7 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -612,7 +612,7 @@
mSrvConfig
.getNetdInstance()
.ipSecDeleteSecurityAssociation(
- mResourceId,
+ uid,
mConfig.getSourceAddress(),
mConfig.getDestinationAddress(),
spi,
@@ -679,7 +679,7 @@
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 @@
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 @@
}
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 @@
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 @@
// 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 @@
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 @@
okey,
0xffffffff);
netd.ipSecAddSecurityPolicy(
- 0, // Use 0 for reqId
+ callerUid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
remoteAddr,
@@ -1532,7 +1534,7 @@
mSrvConfig
.getNetdInstance()
.ipSecAddSecurityAssociation(
- resourceId,
+ Binder.getCallingUid(),
c.getMode(),
c.getSourceAddress(),
c.getDestinationAddress(),
@@ -1623,13 +1625,14 @@
@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 @@
.getNetdInstance()
.ipSecApplyTransportModeTransform(
socket.getFileDescriptor(),
- resourceId,
+ callingUid,
direction,
c.getSourceAddress(),
c.getDestinationAddress(),
@@ -1675,7 +1678,8 @@
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 @@
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 daf870d..1d163ee 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1565,10 +1565,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index fd32b5a..a392b51 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1185,8 +1185,8 @@
final long accountId = accountEntry.getKey();
final Account account = accountEntry.getValue();
if (obsoleteAuthType.contains(account.type)) {
- Slog.w(TAG, "deleting account " + account.name + " because type "
- + account.type
+ Slog.w(TAG, "deleting account " + account.toSafeString()
+ + " because type " + account.type
+ "'s registered authenticator no longer exist.");
Map<String, Integer> packagesToVisibility =
getRequestingPackages(account, accounts);
@@ -1326,7 +1326,8 @@
Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
List<Account> accountsToRemove = accounts.accountsDb.findCeAccountsNotInDe();
if (!accountsToRemove.isEmpty()) {
- Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
+ Slog.i(TAG, accountsToRemove.size()
+ + " accounts were previously deleted while user "
+ accounts.userId + " was locked. Removing accounts from CE tables");
logRecord(accounts, AccountsDb.DEBUG_ACTION_SYNC_DE_CE_ACCOUNTS,
AccountsDb.TABLE_ACCOUNTS);
@@ -1641,7 +1642,7 @@
return;
}
- Slog.d(TAG, "Copying account " + account.name
+ Slog.d(TAG, "Copying account " + account.toSafeString()
+ " from user " + userFrom + " to user " + userTo);
long identityToken = clearCallingIdentity();
try {
@@ -1777,8 +1778,8 @@
return false;
}
if (!isLocalUnlockedUser(accounts.userId)) {
- Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
- + " is locked. callingUid=" + callingUid);
+ Log.w(TAG, "Account " + account.toSafeString() + " cannot be added - user "
+ + accounts.userId + " is locked. callingUid=" + callingUid);
return false;
}
synchronized (accounts.dbLock) {
@@ -1786,19 +1787,19 @@
accounts.accountsDb.beginTransaction();
try {
if (accounts.accountsDb.findCeAccountId(account) >= 0) {
- Log.w(TAG, "insertAccountIntoDatabase: " + account
+ Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+ ", skipping since the account already exists");
return false;
}
long accountId = accounts.accountsDb.insertCeAccount(account, password);
if (accountId < 0) {
- Log.w(TAG, "insertAccountIntoDatabase: " + account
+ Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+ ", skipping the DB insert failed");
return false;
}
// Insert into DE table
if (accounts.accountsDb.insertDeAccount(account, accountId) < 0) {
- Log.w(TAG, "insertAccountIntoDatabase: " + account
+ Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+ ", skipping the DB insert failed");
return false;
}
@@ -1806,7 +1807,8 @@
for (String key : extras.keySet()) {
final String value = extras.getString(key);
if (accounts.accountsDb.insertExtra(accountId, key, value) < 0) {
- Log.w(TAG, "insertAccountIntoDatabase: " + account
+ Log.w(TAG, "insertAccountIntoDatabase: "
+ + account.toSafeString()
+ ", skipping since insertExtra failed for key " + key);
return false;
}
@@ -2282,7 +2284,8 @@
boolean isChanged = false;
boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
if (!userUnlocked) {
- Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
+ Slog.i(TAG, "Removing account " + account.toSafeString()
+ + " while user " + accounts.userId
+ " is still locked. CE data will be removed later");
}
synchronized (accounts.dbLock) {
@@ -2907,7 +2910,7 @@
protected String toDebugString(long now) {
if (loginOptions != null) loginOptions.keySet();
return super.toDebugString(now) + ", getAuthToken"
- + ", " + account
+ + ", " + account.toSafeString()
+ ", authTokenType " + authTokenType
+ ", loginOptions " + loginOptions
+ ", notifyOnAuthFailure " + notifyOnAuthFailure;
@@ -3669,7 +3672,7 @@
@Override
protected String toDebugString(long now) {
return super.toDebugString(now) + ", confirmCredentials"
- + ", " + account;
+ + ", " + account.toSafeString();
}
}.bind();
} finally {
@@ -3707,7 +3710,7 @@
protected String toDebugString(long now) {
if (loginOptions != null) loginOptions.keySet();
return super.toDebugString(now) + ", updateCredentials"
- + ", " + account
+ + ", " + account.toSafeString()
+ ", authTokenType " + authTokenType
+ ", loginOptions " + loginOptions;
}
@@ -3771,7 +3774,7 @@
loginOptions.keySet();
return super.toDebugString(now)
+ ", startUpdateCredentialsSession"
- + ", " + account
+ + ", " + account.toSafeString()
+ ", authTokenType " + authTokenType
+ ", loginOptions " + loginOptions;
}
@@ -3812,7 +3815,7 @@
@Override
protected String toDebugString(long now) {
return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
- + ", " + account;
+ + ", " + account.toSafeString();
}
@Override
@@ -4369,7 +4372,7 @@
accounts.accountsDb.deleteSharedAccount(account);
long accountId = accounts.accountsDb.insertSharedAccount(account);
if (accountId < 0) {
- Log.w(TAG, "insertAccountIntoDatabase: " + account
+ Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+ ", skipping the DB insert failed");
return false;
}
@@ -5208,7 +5211,7 @@
Process.SYSTEM_UID, null /* packageName */, false);
fout.println("Accounts: " + accounts.length);
for (Account account : accounts) {
- fout.println(" " + account);
+ fout.println(" " + account.toSafeString());
}
// Add debug information.
@@ -5613,7 +5616,8 @@
if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
// TODO: Skip this check when running automated tests. Replace this
// with a more general solution.
- Log.d(TAG, "no credentials permission for usage of " + account + ", "
+ Log.d(TAG, "no credentials permission for usage of "
+ + account.toSafeString() + ", "
+ authTokenType + " by uid " + callerUid
+ " but ignoring since device is in test harness.");
return true;
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 7276222..1866420 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -296,7 +296,13 @@
<T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, int activityType,
boolean onTop) {
- final int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType);
+ // First preference is the windowing mode in the activity options if set.
+ int windowingMode = (options != null)
+ ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+ // Validate that our desired windowingMode will work under the current conditions.
+ // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
+ // it's display's windowing mode.
+ windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
return getOrCreateStack(windowingMode, activityType, onTop);
}
@@ -308,7 +314,7 @@
* Creates a stack matching the input windowing mode and activity type on this display.
* @param windowingMode The windowing mode the stack should be created in. If
* {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
- * be created in {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
+ * inherit it's parent's windowing mode.
* @param activityType The activityType the stack should be created in. If
* {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
* be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
@@ -589,7 +595,7 @@
if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
continue;
}
- otherStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN, false /* animate */,
+ otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED, false /* animate */,
false /* showRecents */, false /* enteringSplitScreenMode */,
true /* deferEnsuringVisibility */);
}
@@ -650,10 +656,14 @@
return false;
}
+ final int displayWindowingMode = getWindowingMode();
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
|| windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
return supportsSplitScreen
- && WindowConfiguration.supportSplitScreenWindowingMode(activityType);
+ && WindowConfiguration.supportSplitScreenWindowingMode(activityType)
+ // Freeform windows and split-screen windows don't mix well, so prevent
+ // split windowing modes on freeform displays.
+ && displayWindowingMode != WINDOWING_MODE_FREEFORM;
}
if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
@@ -666,6 +676,16 @@
return true;
}
+ /**
+ * Resolves the windowing mode that an {@link ActivityRecord} would be in if started on this
+ * display with the provided parameters.
+ *
+ * @param r The ActivityRecord in question.
+ * @param options Options to start with.
+ * @param task The task within-which the activity would start.
+ * @param activityType The type of activity to start.
+ * @return The resolved (not UNDEFINED) windowing-mode that the activity would be in.
+ */
int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
@Nullable TaskRecord task, int activityType) {
@@ -687,7 +707,9 @@
windowingMode = getWindowingMode();
}
}
- return validateWindowingMode(windowingMode, r, task, activityType);
+ windowingMode = validateWindowingMode(windowingMode, r, task, activityType);
+ return windowingMode != WINDOWING_MODE_UNDEFINED
+ ? windowingMode : WINDOWING_MODE_FULLSCREEN;
}
/**
@@ -724,23 +746,21 @@
final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
if (!inSplitScreenMode
&& windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
- // Switch to fullscreen windowing mode if we are not in split-screen mode and we are
+ // Switch to the display's windowing mode if we are not in split-screen mode and we are
// trying to launch in split-screen secondary.
- windowingMode = WINDOWING_MODE_FULLSCREEN;
- } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ } else if (inSplitScreenMode && (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_UNDEFINED)
&& supportsSplitScreen) {
windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
}
if (windowingMode != WINDOWING_MODE_UNDEFINED
&& isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
- supportsFreeform, supportsPip, activityType)) {
+ supportsFreeform, supportsPip, activityType)) {
return windowingMode;
}
- // Try to use the display's windowing mode otherwise fallback to fullscreen.
- windowingMode = getWindowingMode();
- return windowingMode != WINDOWING_MODE_UNDEFINED
- ? windowingMode : WINDOWING_MODE_FULLSCREEN;
+ return WINDOWING_MODE_UNDEFINED;
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2ee598f..b64e8b8 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 @@
}
}
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 @@
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/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index bc99827..aa4e68d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -347,6 +347,9 @@
/** The attached Display's unique identifier, or -1 if detached */
int mDisplayId;
+ /** Stores the override windowing-mode from before a transient mode change (eg. split) */
+ private int mRestoreOverrideWindowingMode = WINDOWING_MODE_UNDEFINED;
+
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
private final Rect mTmpRect2 = new Rect();
@@ -531,16 +534,48 @@
false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
}
+ /**
+ * A transient windowing mode is one which activities enter into temporarily. Examples of this
+ * are Split window modes and pip. Non-transient modes are modes that displays can adopt.
+ *
+ * @param windowingMode the windowingMode to test for transient-ness.
+ * @return {@code true} if the windowing mode is transient, {@code false} otherwise.
+ */
+ private static boolean isTransientWindowingMode(int windowingMode) {
+ // TODO(b/114842032): add PIP if/when it uses mode transitions instead of task reparenting
+ return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ /**
+ * Specialization of {@link #setWindowingMode(int)} for this subclass.
+ *
+ * @param preferredWindowingMode the preferred windowing mode. This may not be honored depending
+ * on the state of things. For example, WINDOWING_MODE_UNDEFINED will resolve to the
+ * previous non-transient mode if this stack is currently in a transient mode.
+ * @param animate Can be used to prevent animation.
+ * @param showRecents Controls whether recents is shown on the other side of a split while
+ * entering split mode.
+ * @param enteringSplitScreenMode {@code true} if entering split mode.
+ * @param deferEnsuringVisibility Whether visibility updates are deferred. This is set when
+ * many operations (which can effect visibility) are being performed in bulk.
+ */
void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents,
boolean enteringSplitScreenMode, boolean deferEnsuringVisibility) {
final boolean creating = mWindowContainerController == null;
final int currentMode = getWindowingMode();
+ final int currentOverrideMode = getOverrideWindowingMode();
final ActivityDisplay display = getDisplay();
final TaskRecord topTask = topTask();
final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
- mTmpOptions.setLaunchWindowingMode(preferredWindowingMode);
-
int windowingMode = preferredWindowingMode;
+ if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
+ && isTransientWindowingMode(currentMode)) {
+ // Leaving a transient mode. Interpret UNDEFINED as "restore"
+ windowingMode = mRestoreOverrideWindowingMode;
+ }
+ mTmpOptions.setLaunchWindowingMode(windowingMode);
+
// Need to make sure windowing mode is supported. If we in the process of creating the stack
// no need to resolve the windowing mode again as it is already resolved to the right mode.
if (!creating) {
@@ -550,8 +585,8 @@
if (splitScreenStack == this
&& windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
// Resolution to split-screen secondary for the primary split-screen stack means
- // we want to go fullscreen.
- windowingMode = WINDOWING_MODE_FULLSCREEN;
+ // we want to leave split-screen mode.
+ windowingMode = mRestoreOverrideWindowingMode;
}
final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack();
@@ -570,21 +605,33 @@
// doesn't support split-screen mode, go ahead an dismiss split-screen and display a
// warning toast about it.
mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack();
- display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN,
+ display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_UNDEFINED,
false /* animate */, false /* showRecents */,
false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */);
}
}
if (currentMode == windowingMode) {
- // You are already in the window mode silly...
+ // You are already in the window mode, so we can skip most of the work below. However,
+ // it's possible that we have inherited the current windowing mode from a parent. So,
+ // fulfill this method's contract by setting the override mode directly.
+ getOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
return;
}
final WindowManagerService wm = mService.mWindowManager;
final ActivityRecord topActivity = getTopActivity();
- if (sendNonResizeableNotification && windowingMode != WINDOWING_MODE_FULLSCREEN
+ // For now, assume that the Stack's windowing mode is what will actually be used
+ // by it's activities. In the future, there may be situations where this doesn't
+ // happen; so at that point, this message will need to handle that.
+ int likelyResolvedMode = windowingMode;
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ final ConfigurationContainer parent = getParent();
+ likelyResolvedMode = parent != null ? parent.getWindowingMode()
+ : WINDOWING_MODE_FULLSCREEN;
+ }
+ if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
&& topActivity != null && topActivity.isNonResizableOrForcedResizable()
&& !topActivity.noDisplay) {
// Inform the user that they are starting an app that may not work correctly in
@@ -625,6 +672,9 @@
+ " while there is already one isn't currently supported");
//return;
}
+ if (isTransientWindowingMode(windowingMode) && !isTransientWindowingMode(currentMode)) {
+ mRestoreOverrideWindowingMode = currentOverrideMode;
+ }
mTmpRect2.setEmpty();
if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
@@ -674,13 +724,6 @@
// display, so they should be considered compatible.
activityType = ACTIVITY_TYPE_STANDARD;
}
- final ActivityDisplay display = getDisplay();
- if (display != null && activityType == ACTIVITY_TYPE_STANDARD
- && windowingMode == WINDOWING_MODE_UNDEFINED) {
- // Standard activity types will mostly take on the windowing mode of the display if one
- // isn't specified, so look-up a compatible stack based on the display's windowing mode.
- windowingMode = display.getWindowingMode();
- }
return super.isCompatible(windowingMode, activityType);
}
@@ -1085,7 +1128,7 @@
* behind the home stack. Exit split screen in this case.
*/
if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ setWindowingMode(WINDOWING_MODE_UNDEFINED);
}
getDisplay().positionChildAtBottom(this);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index af2d3b0..310898e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2828,8 +2828,7 @@
if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
continue;
}
- resizeStackLocked(otherStack, null, null, null, PRESERVE_WINDOWS,
- true /* allowResizeInDockedMode */, DEFER_RESUME);
+ otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
}
// Also disable docked stack resizing since we have manually adjusted the
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 7eadcb3..212844a 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -3338,7 +3338,7 @@
}
}
- stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java
index 347a357..cd4d6a3 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 @@
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/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d16c277..a8f7259 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -68,6 +68,7 @@
import android.hardware.usb.UsbManager;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.ip.IpServer;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -112,10 +113,8 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.LocalServices;
-import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadController;
-import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
@@ -149,7 +148,7 @@
protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
private static final Class[] messageClasses = {
- Tethering.class, TetherMasterSM.class, TetherInterfaceStateMachine.class
+ Tethering.class, TetherMasterSM.class, IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
@@ -159,21 +158,21 @@
.getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
private static class TetherState {
- public final TetherInterfaceStateMachine stateMachine;
+ public final IpServer ipServer;
public int lastState;
public int lastError;
- public TetherState(TetherInterfaceStateMachine sm) {
- stateMachine = sm;
+ public TetherState(IpServer ipServer) {
+ this.ipServer = ipServer;
// Assume all state machines start out available and with no errors.
- lastState = IControlsTethering.STATE_AVAILABLE;
+ lastState = IpServer.STATE_AVAILABLE;
lastError = TETHER_ERROR_NO_ERROR;
}
public boolean isCurrentlyServing() {
switch (lastState) {
- case IControlsTethering.STATE_TETHERED:
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_TETHERED:
+ case IpServer.STATE_LOCAL_ONLY:
return true;
default:
return false;
@@ -198,7 +197,7 @@
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
// TODO: Figure out how to merge this and other downstream-tracking objects
// into a single coherent structure.
- private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams;
+ private final HashSet<IpServer> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
private final TetheringDependencies mDeps;
@@ -604,7 +603,7 @@
}
public int tether(String iface) {
- return tether(iface, IControlsTethering.STATE_TETHERED);
+ return tether(iface, IpServer.STATE_TETHERED);
}
private int tether(String iface, int requestedState) {
@@ -617,7 +616,7 @@
}
// Ignore the error status of the interface. If the interface is available,
// the errors are referring to past tethering attempts anyway.
- if (tetherState.lastState != IControlsTethering.STATE_AVAILABLE) {
+ if (tetherState.lastState != IpServer.STATE_AVAILABLE) {
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
return TETHER_ERROR_UNAVAIL_IFACE;
}
@@ -626,8 +625,7 @@
// return an error.
//
// TODO: reexamine the threading and messaging model.
- tetherState.stateMachine.sendMessage(
- TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, requestedState);
+ tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState);
return TETHER_ERROR_NO_ERROR;
}
}
@@ -644,8 +642,7 @@
Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
return TETHER_ERROR_UNAVAIL_IFACE;
}
- tetherState.stateMachine.sendMessage(
- TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+ tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED);
return TETHER_ERROR_NO_ERROR;
}
}
@@ -689,11 +686,11 @@
String iface = mTetherStates.keyAt(i);
if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
erroredList.add(iface);
- } else if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
+ } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
availableList.add(iface);
- } else if (tetherState.lastState == IControlsTethering.STATE_LOCAL_ONLY) {
+ } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
localOnlyList.add(iface);
- } else if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
+ } else if (tetherState.lastState == IpServer.STATE_TETHERED) {
if (cfg.isUsb(iface)) {
usbTethered = true;
} else if (cfg.isWifi(iface)) {
@@ -882,10 +879,10 @@
synchronized (Tethering.this.mPublicSync) {
if (!usbConnected && mRndisEnabled) {
// Turn off tethering if it was enabled and there is a disconnect.
- tetherMatchingInterfaces(IControlsTethering.STATE_AVAILABLE, TETHERING_USB);
+ tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
} else if (usbConfigured && rndisEnabled) {
// Tether if rndis is enabled and usb is configured.
- tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED, TETHERING_USB);
+ tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB);
}
mRndisEnabled = usbConfigured && rndisEnabled;
}
@@ -959,15 +956,15 @@
if (!TextUtils.isEmpty(ifname)) {
final TetherState ts = mTetherStates.get(ifname);
if (ts != null) {
- ts.stateMachine.unwanted();
+ ts.ipServer.unwanted();
return;
}
}
for (int i = 0; i < mTetherStates.size(); i++) {
- TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine;
- if (tism.interfaceType() == TETHERING_WIFI) {
- tism.unwanted();
+ final IpServer ipServer = mTetherStates.valueAt(i).ipServer;
+ if (ipServer.interfaceType() == TETHERING_WIFI) {
+ ipServer.unwanted();
return;
}
}
@@ -978,15 +975,15 @@
}
private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
- // Map wifiIpMode values to IControlsTethering serving states, inferring
+ // Map wifiIpMode values to IpServer.Callback serving states, inferring
// from mWifiTetherRequested as a final "best guess".
final int ipServingMode;
switch (wifiIpMode) {
case IFACE_IP_MODE_TETHERED:
- ipServingMode = IControlsTethering.STATE_TETHERED;
+ ipServingMode = IpServer.STATE_TETHERED;
break;
case IFACE_IP_MODE_LOCAL_ONLY:
- ipServingMode = IControlsTethering.STATE_LOCAL_ONLY;
+ ipServingMode = IpServer.STATE_LOCAL_ONLY;
break;
default:
mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode);
@@ -1041,12 +1038,12 @@
private void changeInterfaceState(String ifname, int requestedState) {
final int result;
switch (requestedState) {
- case IControlsTethering.STATE_UNAVAILABLE:
- case IControlsTethering.STATE_AVAILABLE:
+ case IpServer.STATE_UNAVAILABLE:
+ case IpServer.STATE_AVAILABLE:
result = untether(ifname);
break;
- case IControlsTethering.STATE_TETHERED:
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_TETHERED:
+ case IpServer.STATE_LOCAL_ONLY:
result = tether(ifname, requestedState);
break;
default:
@@ -1104,7 +1101,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
+ if (tetherState.lastState == IpServer.STATE_TETHERED) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -1117,7 +1114,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
+ if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -1177,7 +1174,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState != IControlsTethering.STATE_TETHERED) {
+ if (tetherState.lastState != IpServer.STATE_TETHERED) {
continue; // Skip interfaces that aren't tethered.
}
String iface = mTetherStates.keyAt(i);
@@ -1231,7 +1228,7 @@
// Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList
// so that the garbage collector does not clean up the state machine before it has a chance
// to tear itself down.
- private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+ private final ArrayList<IpServer> mNotifyList;
private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
private final OffloadWrapper mOffload;
@@ -1268,17 +1265,19 @@
public boolean processMessage(Message message) {
logMessage(this, message.what);
switch (message.what) {
- case EVENT_IFACE_SERVING_STATE_ACTIVE:
- TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+ case EVENT_IFACE_SERVING_STATE_ACTIVE: {
+ final IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
transitionTo(mTetherModeAliveState);
break;
- case EVENT_IFACE_SERVING_STATE_INACTIVE:
- who = (TetherInterfaceStateMachine) message.obj;
+ }
+ case EVENT_IFACE_SERVING_STATE_INACTIVE: {
+ final IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
handleInterfaceServingStateInactive(who);
break;
+ }
case EVENT_IFACE_UPDATE_LINKPROPERTIES:
// Silently ignore these for now.
break;
@@ -1410,8 +1409,8 @@
protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
mCurrentUpstreamIfaceSet = ifaces;
- for (TetherInterfaceStateMachine sm : mNotifyList) {
- sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces);
+ for (IpServer ipServer : mNotifyList) {
+ ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifaces);
}
}
@@ -1420,13 +1419,13 @@
mOffload.updateUpstreamNetworkState(ns);
}
- private void handleInterfaceServingStateActive(int mode, TetherInterfaceStateMachine who) {
+ private void handleInterfaceServingStateActive(int mode, IpServer who) {
if (mNotifyList.indexOf(who) < 0) {
mNotifyList.add(who);
mIPv6TetheringCoordinator.addActiveDownstream(who, mode);
}
- if (mode == IControlsTethering.STATE_TETHERED) {
+ if (mode == IpServer.STATE_TETHERED) {
// No need to notify OffloadController just yet as there are no
// "offload-able" prefixes to pass along. This will handled
// when the TISM informs Tethering of its LinkProperties.
@@ -1441,10 +1440,10 @@
final WifiManager mgr = getWifiManager();
final String iface = who.interfaceName();
switch (mode) {
- case IControlsTethering.STATE_TETHERED:
+ case IpServer.STATE_TETHERED:
mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED);
break;
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_LOCAL_ONLY:
mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY);
break;
default:
@@ -1454,7 +1453,7 @@
}
}
- private void handleInterfaceServingStateInactive(TetherInterfaceStateMachine who) {
+ private void handleInterfaceServingStateInactive(IpServer who) {
mNotifyList.remove(who);
mIPv6TetheringCoordinator.removeActiveDownstream(who);
mOffload.excludeDownstreamInterface(who.interfaceName());
@@ -1563,10 +1562,10 @@
boolean retValue = true;
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE: {
- TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+ IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
- who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
+ who.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
mCurrentUpstreamIfaceSet);
// If there has been a change and an upstream is now
// desired, kick off the selection process.
@@ -1577,7 +1576,7 @@
break;
}
case EVENT_IFACE_SERVING_STATE_INACTIVE: {
- TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+ IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
handleInterfaceServingStateInactive(who);
@@ -1591,7 +1590,7 @@
if (DBG) {
Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
" live requests:");
- for (TetherInterfaceStateMachine o : mNotifyList) {
+ for (IpServer o : mNotifyList) {
Log.d(TAG, " " + o);
}
}
@@ -1605,7 +1604,7 @@
}
case EVENT_IFACE_UPDATE_LINKPROPERTIES: {
final LinkProperties newLp = (LinkProperties) message.obj;
- if (message.arg1 == IControlsTethering.STATE_TETHERED) {
+ if (message.arg1 == IpServer.STATE_TETHERED) {
mOffload.updateDownstreamLinkProperties(newLp);
} else {
mOffload.excludeDownstreamInterface(newLp.getInterfaceName());
@@ -1650,7 +1649,7 @@
boolean retValue = true;
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE:
- TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+ IpServer who = (IpServer) message.obj;
who.sendMessage(mErrorNotification);
break;
case CMD_CLEAR_ERROR:
@@ -1665,8 +1664,8 @@
void notify(int msgType) {
mErrorNotification = msgType;
- for (TetherInterfaceStateMachine sm : mNotifyList) {
- sm.sendMessage(msgType);
+ for (IpServer ipServer : mNotifyList) {
+ ipServer.sendMessage(msgType);
}
}
@@ -1676,7 +1675,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingEnabled");
- notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR);
+ notify(IpServer.CMD_IP_FORWARDING_ENABLE_ERROR);
}
}
@@ -1684,7 +1683,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingDisabled");
- notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR);
+ notify(IpServer.CMD_IP_FORWARDING_DISABLE_ERROR);
}
}
@@ -1692,7 +1691,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in startTethering");
- notify(TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR);
+ notify(IpServer.CMD_START_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
@@ -1703,7 +1702,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in stopTethering");
- notify(TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR);
+ notify(IpServer.CMD_STOP_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
@@ -1714,7 +1713,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in setDnsForwarders");
- notify(TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR);
+ notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR);
try {
mNMService.stopTethering();
} catch (Exception e) {}
@@ -1771,15 +1770,15 @@
// Maybe add prefixes or addresses for downstreams, depending on
// the IP serving mode of each.
- for (TetherInterfaceStateMachine tism : mNotifyList) {
- final LinkProperties lp = tism.linkProperties();
+ for (IpServer ipServer : mNotifyList) {
+ final LinkProperties lp = ipServer.linkProperties();
- switch (tism.servingMode()) {
- case IControlsTethering.STATE_UNAVAILABLE:
- case IControlsTethering.STATE_AVAILABLE:
+ switch (ipServer.servingMode()) {
+ case IpServer.STATE_UNAVAILABLE:
+ case IpServer.STATE_AVAILABLE:
// No usable LinkProperties in these states.
continue;
- case IControlsTethering.STATE_TETHERED:
+ case IpServer.STATE_TETHERED:
// Only add IPv4 /32 and IPv6 /128 prefixes. The
// directly-connected prefixes will be sent as
// downstream "offload-able" prefixes.
@@ -1789,7 +1788,7 @@
localPrefixes.add(PrefixUtils.ipAddressAsPrefix(ip));
}
break;
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_LOCAL_ONLY:
// Add prefixes covering all local IPs.
localPrefixes.addAll(PrefixUtils.localPrefixesFrom(lp));
break;
@@ -1826,16 +1825,16 @@
pw.print(iface + " - ");
switch (tetherState.lastState) {
- case IControlsTethering.STATE_UNAVAILABLE:
+ case IpServer.STATE_UNAVAILABLE:
pw.print("UnavailableState");
break;
- case IControlsTethering.STATE_AVAILABLE:
+ case IpServer.STATE_AVAILABLE:
pw.print("AvailableState");
break;
- case IControlsTethering.STATE_TETHERED:
+ case IpServer.STATE_TETHERED:
pw.print("TetheredState");
break;
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_LOCAL_ONLY:
pw.print("LocalHotspotState");
break;
default:
@@ -1873,28 +1872,26 @@
return false;
}
- private IControlsTethering makeControlCallback(String ifname) {
- return new IControlsTethering() {
+ private IpServer.Callback makeControlCallback() {
+ return new IpServer.Callback() {
@Override
- public void updateInterfaceState(
- TetherInterfaceStateMachine who, int state, int lastError) {
- notifyInterfaceStateChange(ifname, who, state, lastError);
+ public void updateInterfaceState(IpServer who, int state, int lastError) {
+ notifyInterfaceStateChange(who, state, lastError);
}
@Override
- public void updateLinkProperties(
- TetherInterfaceStateMachine who, LinkProperties newLp) {
- notifyLinkPropertiesChanged(ifname, who, newLp);
+ public void updateLinkProperties(IpServer who, LinkProperties newLp) {
+ notifyLinkPropertiesChanged(who, newLp);
}
};
}
// TODO: Move into TetherMasterSM.
- private void notifyInterfaceStateChange(
- String iface, TetherInterfaceStateMachine who, int state, int error) {
+ private void notifyInterfaceStateChange(IpServer who, int state, int error) {
+ final String iface = who.interfaceName();
synchronized (mPublicSync) {
final TetherState tetherState = mTetherStates.get(iface);
- if (tetherState != null && tetherState.stateMachine.equals(who)) {
+ if (tetherState != null && tetherState.ipServer.equals(who)) {
tetherState.lastState = state;
tetherState.lastError = error;
} else {
@@ -1908,7 +1905,7 @@
// Notify that we're tethering (or not) this interface.
// This is how data saver for instance knows if the user explicitly
// turned on tethering (thus keeping us from being in data saver mode).
- mPolicyManager.onTetheringChanged(iface, state == IControlsTethering.STATE_TETHERED);
+ mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED);
} catch (RemoteException e) {
// Not really very much we can do here.
}
@@ -1921,12 +1918,12 @@
}
int which;
switch (state) {
- case IControlsTethering.STATE_UNAVAILABLE:
- case IControlsTethering.STATE_AVAILABLE:
+ case IpServer.STATE_UNAVAILABLE:
+ case IpServer.STATE_AVAILABLE:
which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_INACTIVE;
break;
- case IControlsTethering.STATE_TETHERED:
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_TETHERED:
+ case IpServer.STATE_LOCAL_ONLY:
which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_ACTIVE;
break;
default:
@@ -1937,12 +1934,12 @@
sendTetherStateChangedBroadcast();
}
- private void notifyLinkPropertiesChanged(String iface, TetherInterfaceStateMachine who,
- LinkProperties newLp) {
+ private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) {
+ final String iface = who.interfaceName();
final int state;
synchronized (mPublicSync) {
final TetherState tetherState = mTetherStates.get(iface);
- if (tetherState != null && tetherState.stateMachine.equals(who)) {
+ if (tetherState != null && tetherState.ipServer.equals(who)) {
state = tetherState.lastState;
} else {
mLog.log("got notification from stale iface " + iface);
@@ -1952,7 +1949,7 @@
mLog.log(String.format(
"OBSERVED LinkProperties update iface=%s state=%s lp=%s",
- iface, IControlsTethering.getStateString(state), newLp));
+ iface, IpServer.getStateString(state), newLp));
final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES;
mTetherMasterSM.sendMessage(which, state, 0, newLp);
}
@@ -1976,11 +1973,11 @@
mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
final TetherState tetherState = new TetherState(
- new TetherInterfaceStateMachine(
- iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
- makeControlCallback(iface), mConfig.enableLegacyDhcpServer, mDeps));
+ new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
+ makeControlCallback(), mConfig.enableLegacyDhcpServer,
+ mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
- tetherState.stateMachine.start();
+ tetherState.ipServer.start();
}
private void stopTrackingInterfaceLocked(final String iface) {
@@ -1989,36 +1986,11 @@
mLog.log("attempting to remove unknown iface (" + iface + "), ignoring");
return;
}
- tetherState.stateMachine.stop();
+ tetherState.ipServer.stop();
mLog.log("removing TetheringInterfaceStateMachine for: " + iface);
mTetherStates.remove(iface);
}
- private static String getIPv4DefaultRouteInterface(NetworkState ns) {
- if (ns == null) return null;
- return getInterfaceForDestination(ns.linkProperties, Inet4Address.ANY);
- }
-
- private static String getIPv6DefaultRouteInterface(NetworkState ns) {
- if (ns == null) return null;
- // An upstream network's IPv6 capability is currently only useful if it
- // can be 64share'd downstream (RFC 7278). For now, that means mobile
- // upstream networks only.
- if (ns.networkCapabilities == null ||
- !ns.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
- return null;
- }
-
- return getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY);
- }
-
- private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
- final RouteInfo ri = (lp != null)
- ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
- : null;
- return (ri != null) ? ri.getInterface() : null;
- }
-
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
deleted file mode 100644
index 2b81347..0000000
--- a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity.tethering;
-
-import android.net.LinkProperties;
-
-/**
- * @hide
- *
- * Interface with methods necessary to notify that a given interface is ready for tethering.
- *
- * Rename to something more representative, e.g. IpServingControlCallback.
- *
- * All methods MUST be called on the TetherMasterSM main Looper's thread.
- */
-public class IControlsTethering {
- public static final int STATE_UNAVAILABLE = 0;
- public static final int STATE_AVAILABLE = 1;
- public static final int STATE_TETHERED = 2;
- public static final int STATE_LOCAL_ONLY = 3;
-
- public static String getStateString(int state) {
- switch (state) {
- case STATE_UNAVAILABLE: return "UNAVAILABLE";
- case STATE_AVAILABLE: return "AVAILABLE";
- case STATE_TETHERED: return "TETHERED";
- case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
- }
- return "UNKNOWN: " + state;
- }
-
- /**
- * Notify that |who| has changed its tethering state.
- *
- * TODO: Remove the need for the |who| argument.
- *
- * @param who corresponding instance of a TetherInterfaceStateMachine
- * @param state one of IControlsTethering.STATE_*
- * @param lastError one of ConnectivityManager.TETHER_ERROR_*
- */
- public void updateInterfaceState(TetherInterfaceStateMachine who, int state, int lastError) {}
-
- /**
- * Notify that |who| has new LinkProperties.
- *
- * TODO: Remove the need for the |who| argument.
- *
- * @param who corresponding instance of a TetherInterfaceStateMachine
- * @param newLp the new LinkProperties to report
- */
- public void updateLinkProperties(TetherInterfaceStateMachine who, LinkProperties newLp) {}
-}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index ba67c94..1000148 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -17,6 +17,7 @@
package com.android.server.connectivity.tethering;
import android.net.ConnectivityManager;
+import android.net.ip.IpServer;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -50,19 +51,19 @@
private static final boolean VDBG = false;
private static class Downstream {
- public final TetherInterfaceStateMachine tism;
- public final int mode; // IControlsTethering.STATE_*
+ public final IpServer ipServer;
+ public final int mode; // IpServer.STATE_*
// Used to append to a ULA /48, constructing a ULA /64 for local use.
public final short subnetId;
- Downstream(TetherInterfaceStateMachine tism, int mode, short subnetId) {
- this.tism = tism;
+ Downstream(IpServer ipServer, int mode, short subnetId) {
+ this.ipServer = ipServer;
this.mode = mode;
this.subnetId = subnetId;
}
}
- private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+ private final ArrayList<IpServer> mNotifyList;
private final SharedLog mLog;
// NOTE: mActiveDownstreams is a list and not a hash data structure because
// we keep active downstreams in arrival order. This is done so /64s can
@@ -74,8 +75,7 @@
private short mNextSubnetId;
private NetworkState mUpstreamNetworkState;
- public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList,
- SharedLog log) {
+ public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
mNotifyList = notifyList;
mLog = log.forSubComponent(TAG);
mActiveDownstreams = new LinkedList<>();
@@ -83,7 +83,7 @@
mNextSubnetId = 0;
}
- public void addActiveDownstream(TetherInterfaceStateMachine downstream, int mode) {
+ public void addActiveDownstream(IpServer downstream, int mode) {
if (findDownstream(downstream) == null) {
// Adding a new downstream appends it to the list. Adding a
// downstream a second time without first removing it has no effect.
@@ -98,7 +98,7 @@
}
}
- public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
+ public void removeActiveDownstream(IpServer downstream) {
stopIPv6TetheringOn(downstream);
if (mActiveDownstreams.remove(findDownstream(downstream))) {
updateIPv6TetheringInterfaces();
@@ -133,8 +133,8 @@
}
private void stopIPv6TetheringOnAllInterfaces() {
- for (TetherInterfaceStateMachine sm : mNotifyList) {
- stopIPv6TetheringOn(sm);
+ for (IpServer ipServer : mNotifyList) {
+ stopIPv6TetheringOn(ipServer);
}
}
@@ -156,28 +156,28 @@
}
private void updateIPv6TetheringInterfaces() {
- for (TetherInterfaceStateMachine sm : mNotifyList) {
- final LinkProperties lp = getInterfaceIPv6LinkProperties(sm);
- sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
+ for (IpServer ipServer : mNotifyList) {
+ final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
+ ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
break;
}
}
- private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) {
- if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
+ private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
+ if (ipServer.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
// TODO: Figure out IPv6 support on PAN interfaces.
return null;
}
- final Downstream ds = findDownstream(sm);
+ final Downstream ds = findDownstream(ipServer);
if (ds == null) return null;
- if (ds.mode == IControlsTethering.STATE_LOCAL_ONLY) {
+ if (ds.mode == IpServer.STATE_LOCAL_ONLY) {
// Build a Unique Locally-assigned Prefix configuration.
return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
}
- // This downstream is in IControlsTethering.STATE_TETHERED mode.
+ // This downstream is in IpServer.STATE_TETHERED mode.
if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) {
return null;
}
@@ -188,7 +188,7 @@
// IPv6 toward the oldest (first requested) active downstream.
final Downstream currentActive = mActiveDownstreams.peek();
- if (currentActive != null && currentActive.tism == sm) {
+ if (currentActive != null && currentActive.ipServer == ipServer) {
final LinkProperties lp = getIPv6OnlyLinkProperties(
mUpstreamNetworkState.linkProperties);
if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
@@ -199,9 +199,9 @@
return null;
}
- Downstream findDownstream(TetherInterfaceStateMachine tism) {
+ Downstream findDownstream(IpServer ipServer) {
for (Downstream ds : mActiveDownstreams) {
- if (ds.tism == tism) return ds;
+ if (ds.ipServer == ipServer) return ds;
}
return null;
}
@@ -304,7 +304,7 @@
ns.linkProperties);
}
- private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
- sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+ private static void stopIPv6TetheringOn(IpServer ipServer) {
+ ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index caa867c..8b40069 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -21,6 +21,7 @@
import android.net.NetworkRequest;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
+import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetdService;
@@ -49,20 +50,12 @@
}
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
- ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+ ArrayList<IpServer> notifyList, SharedLog log) {
return new IPv6TetheringCoordinator(notifyList, log);
}
- public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
- return new RouterAdvertisementDaemon(ifParams);
- }
-
- public InterfaceParams getInterfaceParams(String ifName) {
- return InterfaceParams.getByName(ifName);
- }
-
- public INetd getNetdService() {
- return NetdService.getInstance();
+ public IpServer.Dependencies getIpServerDependencies() {
+ return new IpServer.Dependencies();
}
public boolean isTetheringSupported() {
@@ -72,9 +65,4 @@
public NetworkRequest getDefaultNetworkRequest() {
return null;
}
-
- public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, DhcpServingParams params,
- SharedLog log) {
- return new DhcpServer(looper, iface, params, log);
- }
}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 1b688a6..76c191d 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.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 @@
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/InputContentUriTokenHandler.java b/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java
similarity index 97%
rename from services/core/java/com/android/server/InputContentUriTokenHandler.java
rename to services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java
index 6338b2f..845fca1 100644
--- a/services/core/java/com/android/server/InputContentUriTokenHandler.java
+++ b/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java
@@ -14,11 +14,10 @@
** limitations under the License.
*/
-package com.android.server;
+package com.android.server.inputmethod;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.app.UriGrantsManager;
import android.content.Intent;
import android.net.Uri;
@@ -28,6 +27,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.server.LocalServices;
import com.android.server.uri.UriGrantsManagerInternal;
final class InputContentUriTokenHandler extends IInputContentUriToken.Stub {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
similarity index 98%
rename from services/core/java/com/android/server/InputMethodManagerService.java
rename to services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 93d023d..44fb9ea 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -13,9 +13,10 @@
* the License.
*/
-package com.android.server;
+package com.android.server.inputmethod;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -149,7 +150,9 @@
import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.InputBindResult;
import com.android.internal.view.InputMethodClient;
-import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.wm.WindowManagerInternal;
@@ -210,7 +213,6 @@
static final int MSG_BIND_CLIENT = 3010;
static final int MSG_SET_ACTIVE = 3020;
static final int MSG_SET_INTERACTIVE = 3030;
- static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -254,7 +256,7 @@
* the notification.
*/
private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
- "com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
+ "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
/**
* Debug flag for overriding runtime {@link SystemProperties}.
@@ -571,6 +573,11 @@
IBinder mCurToken;
/**
+ * The displayId of current active input method.
+ */
+ int mCurTokenDisplayId = INVALID_DISPLAY;
+
+ /**
* If non-null, this is the input method service we are currently connected
* to.
*/
@@ -598,8 +605,6 @@
*/
boolean mIsInteractive = true;
- int mCurUserActionNotificationSequenceNumber = 0;
-
int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
/**
@@ -1822,7 +1827,7 @@
}
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
- mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
+ mCurId, mCurSeq);
}
@GuardedBy("mMethodMap")
@@ -1867,7 +1872,9 @@
mCurAttribute = attribute;
// Check if the input method is changing.
- if (mCurId != null && mCurId.equals(mCurMethodId)) {
+ final int displayId = mWindowManagerInternal.getDisplayIdForWindow(
+ mCurFocusedWindow);
+ if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) {
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -1881,8 +1888,7 @@
requestClientSessionLocked(cs);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, mCurId, mCurSeq,
- mCurUserActionNotificationSequenceNumber);
+ null, null, mCurId, mCurSeq);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
// In this case we have connected to the service, but
@@ -1894,8 +1900,7 @@
// to see if we can get back in touch with the service.
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, mCurId, mCurSeq,
- mCurUserActionNotificationSequenceNumber);
+ null, null, mCurId, mCurSeq);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -1917,8 +1922,7 @@
// party code.
return new InputBindResult(
InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
- null, null, mCurMethodId, mCurSeq,
- mCurUserActionNotificationSequenceNumber);
+ null, null, mCurMethodId, mCurSeq);
}
InputMethodInfo info = mMethodMap.get(mCurMethodId);
@@ -1934,20 +1938,25 @@
com.android.internal.R.string.input_method_binding_label);
mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
+ final int displayId = mWindowManagerInternal.getDisplayIdForWindow(mCurFocusedWindow);
+ mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY;
+
if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
mLastBindTime = SystemClock.uptimeMillis();
mHaveConnection = true;
mCurId = info.getId();
mCurToken = new Binder();
try {
- if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken);
- mIWindowManager.addWindowToken(mCurToken, TYPE_INPUT_METHOD, DEFAULT_DISPLAY);
+ if (DEBUG) {
+ Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
+ + mCurTokenDisplayId);
+ }
+ mIWindowManager.addWindowToken(mCurToken, TYPE_INPUT_METHOD, mCurTokenDisplayId);
} catch (RemoteException e) {
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, mCurId, mCurSeq,
- mCurUserActionNotificationSequenceNumber);
+ null, null, mCurId, mCurSeq);
}
mCurIntent = null;
Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
@@ -1969,8 +1978,9 @@
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_INITIALIZE_IME, mCurMethod, mCurToken));
+ // Dispatch display id for InputMethodService to update context display.
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
+ MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken));
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
requestClientSessionLocked(mCurClient);
@@ -2016,15 +2026,19 @@
if (mCurToken != null) {
try {
- if (DEBUG) Slog.v(TAG, "Removing window token: " + mCurToken);
+ if (DEBUG) {
+ Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
+ + mCurTokenDisplayId);
+ }
if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) {
// The current IME is shown. Hence an IME switch (transition) is happening.
mWindowManagerInternal.saveLastInputMethodWindowForTransition();
}
- mIWindowManager.removeWindowToken(mCurToken, DEFAULT_DISPLAY);
+ mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
} catch (RemoteException e) {
}
mCurToken = null;
+ mCurTokenDisplayId = INVALID_DISPLAY;
}
mCurId = null;
@@ -2754,7 +2768,7 @@
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
- null, null, null, -1, -1);
+ null, null, null, -1);
}
mCurFocusedWindow = windowToken;
mCurFocusedWindowSoftInputMode = softInputMode;
@@ -2790,6 +2804,15 @@
// soft input window if it is shown.
if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
+
+ // If focused display changed, we should unbind current method
+ // to make app window in previous display relayout after Ime
+ // window token removed.
+ final int newFocusDisplayId =
+ mWindowManagerInternal.getDisplayIdForWindow(windowToken);
+ if (newFocusDisplayId != mCurTokenDisplayId) {
+ unbindCurrentMethodLocked(false);
+ }
}
} else if (isTextEditor && doAutoShow && (softInputMode &
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
@@ -3156,7 +3179,7 @@
*/
@Override
public int getInputMethodWindowVisibleHeight() {
- return mWindowManagerInternal.getInputMethodWindowVisibleHeight();
+ return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
}
@BinderThread
@@ -3172,17 +3195,16 @@
mWindowManagerInternal.clearLastInputMethodWindowForTransition();
}
- @Override
- public void notifyUserAction(int sequenceNumber) {
+ @BinderThread
+ private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
- Slog.d(TAG, "Got the notification of a user action. sequenceNumber:" + sequenceNumber);
+ Slog.d(TAG, "Got the notification of a user action.");
}
synchronized (mMethodMap) {
- if (mCurUserActionNotificationSequenceNumber != sequenceNumber) {
+ if (mCurToken != token) {
if (DEBUG) {
- Slog.d(TAG, "Ignoring the user action notification due to the sequence number "
- + "mismatch. expected:" + mCurUserActionNotificationSequenceNumber
- + " actual: " + sequenceNumber);
+ Slog.d(TAG, "Ignoring the user action notification from IMEs that are no longer"
+ + " active.");
}
return;
}
@@ -3358,9 +3380,12 @@
case MSG_INITIALIZE_IME:
args = (SomeArgs)msg.obj;
try {
- if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2);
+ if (DEBUG) {
+ Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
+ + msg.arg1);
+ }
final IBinder token = (IBinder) args.arg2;
- ((IInputMethod) args.arg1).initializeInternal(token,
+ ((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
new InputMethodPrivilegedOperationsImpl(this, token));
} catch (RemoteException e) {
}
@@ -3445,20 +3470,6 @@
case MSG_START_VR_INPUT:
startVrInputMethodNoCheck((ComponentName) msg.obj);
return true;
- case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
- final int sequenceNumber = msg.arg1;
- final ClientState clientState = (ClientState)msg.obj;
- try {
- clientState.client.setUserActionNotificationSequenceNumber(sequenceNumber);
- } catch (RemoteException e) {
- Slog.w(TAG, "Got RemoteException sending "
- + "setUserActionNotificationSequenceNumber("
- + sequenceNumber + ") notification to pid "
- + clientState.pid + " uid "
- + clientState.uid);
- }
- return true;
- }
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
final ClientState clientState = (ClientState)msg.obj;
@@ -3944,19 +3955,6 @@
mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
}
- mCurUserActionNotificationSequenceNumber =
- Math.max(mCurUserActionNotificationSequenceNumber + 1, 1);
- if (DEBUG) {
- Slog.d(TAG, "Bump mCurUserActionNotificationSequenceNumber:"
- + mCurUserActionNotificationSequenceNumber);
- }
-
- if (mCurClient != null && mCurClient.client != null) {
- executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
- MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
- mCurUserActionNotificationSequenceNumber, mCurClient));
- }
-
if (isVrInput) {
// Updates to InputMethod are transient in VR mode. Any changes to Settings are skipped.
return;
@@ -4549,6 +4547,7 @@
p.println(" mCurId=" + mCurId + " mHaveConnection=" + mHaveConnection
+ " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + mVisibleBound);
p.println(" mCurToken=" + mCurToken);
+ p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId);
p.println(" mCurIntent=" + mCurIntent);
method = mCurMethod;
p.println(" mCurMethod=" + mCurMethod);
@@ -4558,8 +4557,6 @@
+ " mShowForced=" + mShowForced
+ " mInputShown=" + mInputShown);
p.println(" mInFullscreenMode=" + mInFullscreenMode);
- p.println(" mCurUserActionNotificationSequenceNumber="
- + mCurUserActionNotificationSequenceNumber);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
p.println(" mSettingsObserver=" + mSettingsObserver);
p.println(" mSwitchingController:");
@@ -5057,5 +5054,11 @@
public boolean shouldOfferSwitchingToNextInputMethod() {
return mImms.shouldOfferSwitchingToNextInputMethod(mToken);
}
+
+ @BinderThread
+ @Override
+ public void notifyUserActionAsync() {
+ mImms.notifyUserAction(mToken);
+ }
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index b4bc7f5..452b699 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 @@
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 76f9695..48e09d7 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 @@
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 340ae0a..7751f5f 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 @@
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 593e7cd..13ff6e8 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 @@
}
// 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 @@
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
- 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);
+ // 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
}
}
- // 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);
+
+ 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);
}
- 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);
+ // 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.delegate = d;
+ // 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;
+ }
+
}
- }
-
- 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 @@
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 @@
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 @@
if (r.uid == UNKNOWN_UID) {
mRestoredWithoutUids.put(pkg, r);
} else {
- mPackagePreferencess.put(key, r);
+ mPackagePreferences.put(key, r);
}
}
return r;
@@ -364,10 +372,10 @@
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 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 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 @@
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 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 @@
} 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 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 @@
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 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 @@
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 @@
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 @@
}
// 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 c738701..f1b03d1 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 @@
@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 @@
@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 d305032..8f2833f 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.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.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 @@
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 @@
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 @@
@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 @@
}
};
- private void earlyBindToDefContainer() {
- mPm.earlyBindToDefContainer();
- }
-
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
@@ -413,19 +404,6 @@
}
}
}
-
- 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 cacdccb..adf95dc 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.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.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.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 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 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 @@
}
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 @@
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 @@
// 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 @@
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 @@
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);
- }
+ 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_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");
- }
- 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 @@
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 @@
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
processPendingInstall(args, ret);
- mHandler.sendEmptyMessage(MCS_UNBIND);
}
break;
}
@@ -1724,14 +1507,9 @@
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 @@
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
processPendingInstall(args, ret);
- mHandler.sendEmptyMessage(MCS_UNBIND);
}
break;
@@ -12430,14 +12207,6 @@
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 @@
}
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 @@
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 @@
* 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 @@
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 @@
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 @@
* 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 @@
@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 @@
this.installReason = installReason;
}
- abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
+ abstract int copyApk();
abstract int doPreInstall(int status);
/**
@@ -14547,16 +14276,16 @@
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 @@
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 @@
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 390c0cc..36948fc 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 @@
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.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.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 @@
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 5befc1f..846c7b7 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 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 @@
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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3e0429f..350d6b6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4732,8 +4732,8 @@
}
final WindowManager.LayoutParams attrs = win.getAttrs();
final boolean isDefaultDisplay = win.isDefaultDisplay();
- final boolean needsToOffsetInputMethodTarget = isDefaultDisplay &&
- (win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
+ final boolean needsToOffsetInputMethodTarget =
+ (win == mLastInputMethodTargetWindow) && (mLastInputMethodWindow != null);
if (needsToOffsetInputMethodTarget) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames);
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index f2ce63c..64553a8 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -29,6 +29,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
+
import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
@@ -295,6 +296,10 @@
return mFullConfiguration.windowConfiguration.getWindowingMode();
}
+ public int getOverrideWindowingMode() {
+ return mOverrideConfiguration.windowConfiguration.getWindowingMode();
+ }
+
/** Sets the windowing mode for the configuration container. */
public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
mTmpConfig.setTo(getOverrideConfiguration());
@@ -513,7 +518,8 @@
final String childPrefix = prefix + " ";
pw.println(getName()
+ " type=" + activityTypeToString(getActivityType())
- + " mode=" + windowingModeToString(getWindowingMode()));
+ + " mode=" + windowingModeToString(getWindowingMode())
+ + " override-mode=" + windowingModeToString(getOverrideWindowingMode()));
for (int i = getChildCount() - 1; i >= 0; --i) {
final E cc = getChildAt(i);
pw.print(childPrefix + "#" + i + " ");
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d3e534c..5d0101f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -409,6 +409,11 @@
private InputMonitor mInputMonitor;
+ /**
+ * The input method window for this display.
+ */
+ WindowState mInputMethodWindow;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final AppWindowToken atoken = w.mAppToken;
@@ -2107,18 +2112,16 @@
mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
}
}
- final WindowState inputMethod = mService.mInputMethodWindow;
- if (inputMethod != null && inputMethod.isVisibleLw()) {
+ if (mInputMethodWindow != null && mInputMethodWindow.isVisibleLw()) {
// If the input method is visible and the user is typing, we don't want these touch
// events to be intercepted and used to change focus. This would likely cause a
// disappearance of the input method.
- inputMethod.getTouchableRegion(mTmpRegion);
- if (inputMethod.getDisplayId() == mDisplayId) {
+ mInputMethodWindow.getTouchableRegion(mTmpRegion);
+ if (mInputMethodWindow.getDisplayId() == mDisplayId) {
mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
} else {
// IME is on a different display, so we need to update its tap detector.
- // TODO(multidisplay): Remove when IME will always appear on same display.
- inputMethod.getDisplayContent().setTouchExcludeRegion(null /* focusedTask */);
+ setTouchExcludeRegion(null /* focusedTask */);
}
}
for (int i = mTapExcludedWindows.size() - 1; i >= 0; i--) {
@@ -2257,7 +2260,7 @@
}
void adjustForImeIfNeeded() {
- final WindowState imeWin = mService.mInputMethodWindow;
+ final WindowState imeWin = mInputMethodWindow;
final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
&& !mDividerControllerLocked.isImeHideRequested();
final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
@@ -2640,12 +2643,21 @@
}
/**
+ * Set input method window for the display.
+ * @param win Set when window added or Null when destroyed.
+ */
+ void setInputMethodWindowLocked(WindowState win) {
+ mInputMethodWindow = win;
+ computeImeTarget(true /* updateImeTarget */);
+ }
+
+ /**
* Determine and return the window that should be the IME target.
* @param updateImeTarget If true the system IME target will be updated to match what we found.
* @return The window that should be used as the IME target or null if there isn't any.
*/
WindowState computeImeTarget(boolean updateImeTarget) {
- if (mService.mInputMethodWindow == null) {
+ if (mInputMethodWindow == null) {
// There isn't an IME so there shouldn't be a target...That was easy!
if (updateImeTarget) {
if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from "
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 86b14337..d92818a 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -681,10 +681,11 @@
i--;
WindowState win = mService.mDestroySurface.get(i);
win.mDestroying = false;
- if (mService.mInputMethodWindow == win) {
- mService.setInputMethodWindowLocked(null);
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent.mInputMethodWindow == win) {
+ displayContent.setInputMethodWindowLocked(null);
}
- if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
+ if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
wallpaperDestroyed = true;
}
win.destroySurfaceUnchecked();
@@ -1113,4 +1114,18 @@
callback.accept(mChildren.get(i));
}
}
+
+ /**
+ * Get current topmost focused IME window in system.
+ * Will look on all displays in current Z-order.
+ */
+ WindowState getCurrentInputMethodWindow() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mChildren.get(i);
+ if (displayContent.mInputMethodWindow != null) {
+ return displayContent.mInputMethodWindow;
+ }
+ }
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index df680f2..793ce60 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -335,9 +335,9 @@
public abstract void registerAppTransitionListener(AppTransitionListener listener);
/**
- * Retrieves a height of input method window.
+ * Retrieves a height of input method window for given display.
*/
- public abstract int getInputMethodWindowVisibleHeight();
+ public abstract int getInputMethodWindowVisibleHeight(int displayId);
/**
* Saves last input method window for transition.
@@ -356,8 +356,8 @@
/**
* Notifies WindowManagerService that the current IME window status is being changed.
*
- * <p>Only {@link com.android.server.InputMethodManagerService} is the expected and tested
- * caller of this method.</p>
+ * <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and
+ * tested caller of this method.</p>
*
* @param imeToken token to track the active input method. Corresponding IME windows can be
* identified by checking {@link android.view.WindowManager.LayoutParams#token}.
@@ -376,8 +376,8 @@
/**
* Notifies WindowManagerService that the current IME window status is being changed.
*
- * <p>Only {@link com.android.server.InputMethodManagerService} is the expected and tested
- * caller of this method.</p>
+ * <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and
+ * tested caller of this method.</p>
*
* @param imeToken token to track the active input method. Corresponding IME windows can be
* identified by checking {@link android.view.WindowManager.LayoutParams#token}.
@@ -447,4 +447,9 @@
* Returns {@code true} if a process that is identified by {@code client} has IME focus.
*/
public abstract boolean inputMethodClientHasFocus(IInputMethodClient client);
+
+ /**
+ * Return the display Id for given window.
+ */
+ public abstract int getDisplayIdForWindow(IBinder windowToken);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 679e0d8..2ed09ae 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -650,8 +650,6 @@
/** If true hold off on modifying the animation layer of mInputMethodTarget */
boolean mInputMethodTargetWaitingAnim;
- WindowState mInputMethodWindow = null;
-
boolean mHardKeyboardAvailable;
WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
SettingsObserver mSettingsObserver;
@@ -1414,7 +1412,7 @@
win.mToken.addWindow(win);
if (type == TYPE_INPUT_METHOD) {
win.mGivenInsetsPending = true;
- setInputMethodWindowLocked(win);
+ displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
displayContent.computeImeTarget(true /* updateImeTarget */);
@@ -1687,8 +1685,9 @@
mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Final remove of window: " + win);
- if (mInputMethodWindow == win) {
- setInputMethodWindowLocked(null);
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent.mInputMethodWindow == win) {
+ displayContent.setInputMethodWindowLocked(null);
}
final WindowToken token = win.mToken;
@@ -1733,13 +1732,6 @@
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
}
- void setInputMethodWindowLocked(WindowState win) {
- mInputMethodWindow = win;
- final DisplayContent dc = win != null
- ? win.getDisplayContent() : getDefaultDisplayContentLocked();
- dc.computeImeTarget(true /* updateImeTarget */);
- }
-
private void updateHiddenWhileSuspendedState(ArraySet<String> packages, boolean suspended) {
synchronized (mWindowMap) {
mRoot.updateHiddenWhileSuspendedState(packages, suspended);
@@ -2058,8 +2050,10 @@
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = isDefaultDisplay;
}
- if (win.mAttrs.type == TYPE_INPUT_METHOD && mInputMethodWindow == null) {
- setInputMethodWindowLocked(win);
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (win.mAttrs.type == TYPE_INPUT_METHOD
+ && displayContent.mInputMethodWindow == null) {
+ displayContent.setInputMethodWindowLocked(win);
imMayMove = true;
}
win.adjustStartingWindowFlags();
@@ -2222,8 +2216,9 @@
// of a transaction to avoid artifacts.
win.mAnimatingExit = true;
} else {
- if (mInputMethodWindow == win) {
- setInputMethodWindowLocked(null);
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent.mInputMethodWindow == win) {
+ displayContent.setInputMethodWindowLocked(null);
}
boolean stopped = win.mAppToken != null ? win.mAppToken.mAppStopped : true;
// We set mDestroying=true so AppWindowToken#notifyAppStopped in-to destroy surfaces
@@ -2828,7 +2823,7 @@
@Override
public WindowManagerPolicy.WindowState getInputMethodWindowLw() {
- return mInputMethodWindow;
+ return mRoot.getCurrentInputMethodWindow();
}
@Override
@@ -5575,10 +5570,10 @@
// change message pending.
mH.removeMessages(H.REPORT_FOCUS_CHANGE);
mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
- // TODO(multidisplay): Focused windows on default display only.
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final DisplayContent displayContent = (newFocus != null) ? newFocus.getDisplayContent()
+ : getDefaultDisplayContentLocked();
boolean imWindowChanged = false;
- if (mInputMethodWindow != null) {
+ if (displayContent.mInputMethodWindow != null) {
final WindowState prevTarget = mInputMethodTarget;
final WindowState newTarget =
@@ -5587,10 +5582,11 @@
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
&& mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
- final int prevImeAnimLayer = mInputMethodWindow.mWinAnimator.mAnimLayer;
+ final int prevImeAnimLayer =
+ displayContent.mInputMethodWindow.mWinAnimator.mAnimLayer;
displayContent.assignWindowLayers(false /* setLayoutNeeded */);
- imWindowChanged |=
- prevImeAnimLayer != mInputMethodWindow.mWinAnimator.mAnimLayer;
+ imWindowChanged |= prevImeAnimLayer
+ != displayContent.mInputMethodWindow.mWinAnimator.mAnimLayer;
}
}
@@ -5613,7 +5609,7 @@
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
- if (imWindowChanged && oldFocus != mInputMethodWindow) {
+ if (imWindowChanged && oldFocus != displayContent.mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
displayContent.performLayout(true /*initial*/, updateInputWindows);
@@ -6033,8 +6029,15 @@
}
synchronized (mWindowMap) {
final Region r = new Region();
- if (mInputMethodWindow != null) {
- mInputMethodWindow.getTouchableRegion(r);
+ // TODO(b/111080190): this method is only return the recent focused IME touch region,
+ // For Multi-Session IME, will need to add API for given display Id to
+ // get the right IME touch region.
+ for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mRoot.mChildren.get(i);
+ if (displayContent.mInputMethodWindow != null) {
+ displayContent.mInputMethodWindow.getTouchableRegion(r);
+ return r;
+ }
}
return r;
}
@@ -6217,8 +6220,9 @@
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
- if (mInputMethodWindow != null) {
- mInputMethodWindow.writeIdentifierToProto(proto, INPUT_METHOD_WINDOW);
+ final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
+ if (imeWindow != null) {
+ imeWindow.writeIdentifierToProto(proto, INPUT_METHOD_WINDOW);
}
proto.write(DISPLAY_FROZEN, mDisplayFrozen);
final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
@@ -6388,8 +6392,9 @@
pw.print(" mLastStatusBarVisibility=0x");
pw.println(Integer.toHexString(mLastStatusBarVisibility));
}
- if (mInputMethodWindow != null) {
- pw.print(" mInputMethodWindow="); pw.println(mInputMethodWindow);
+ final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
+ if (imeWindow != null) {
+ pw.print(" mInputMethodWindow="); pw.println(imeWindow);
}
mWindowPlacerLocked.dump(pw, " ");
mRoot.mWallpaperController.dump(pw, " ");
@@ -7287,10 +7292,9 @@
}
@Override
- public int getInputMethodWindowVisibleHeight() {
+ public int getInputMethodWindowVisibleHeight(int displayId) {
synchronized (mWindowMap) {
- // TODO(multi-display): Have caller pass in the display they are interested in.
- final DisplayContent dc = getDefaultDisplayContentLocked();
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
return dc.mDisplayFrames.getInputMethodWindowVisibleHeight();
}
}
@@ -7298,8 +7302,9 @@
@Override
public void saveLastInputMethodWindowForTransition() {
synchronized (mWindowMap) {
- if (mInputMethodWindow != null) {
- mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
+ final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
+ if (imeWindow != null) {
+ mPolicy.setLastInputMethodWindowLw(imeWindow, mInputMethodTarget);
}
}
}
@@ -7406,28 +7411,42 @@
}
}
- @Override
- public boolean inputMethodClientHasFocus(IInputMethodClient client) {
- synchronized (mWindowMap) {
- // TODO: multi-display
- if (getDefaultDisplayContentLocked().inputMethodClientHasFocus(client)) {
- return true;
- }
-
- // Okay, how about this... what is the current focus?
- // It seems in some cases we may not have moved the IM
- // target window, such as when it was in a pop-up window,
- // so let's also look at the current focus. (An example:
- // go to Gmail, start searching so the keyboard goes up,
- // press home. Sometimes the IME won't go down.)
- // Would be nice to fix this more correctly, but it's
- // way at the end of a release, and this should be good enough.
- if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null
- && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
+ public boolean inputMethodClientHasFocus(IInputMethodClient client) {
+ boolean hasFocus;
+ synchronized (mWindowMap) {
+ // Check all displays if any input method window has focus.
+ for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mRoot.mChildren.get(i);
+ if (displayContent.inputMethodClientHasFocus(client)) {
return true;
}
}
- return false;
+
+ // Okay, how about this... what is the current focus?
+ // It seems in some cases we may not have moved the IM
+ // target window, such as when it was in a pop-up window,
+ // so let's also look at the current focus. (An example:
+ // go to Gmail, start searching so the keyboard goes up,
+ // press home. Sometimes the IME won't go down.)
+ // Would be nice to fix this more correctly, but it's
+ // way at the end of a release, and this should be good enough.
+ if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null
+ && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int getDisplayIdForWindow(IBinder windowToken) {
+ synchronized (mWindowMap) {
+ final WindowState window = mWindowMap.get(windowToken);
+ if (window != null) {
+ return window.getDisplayContent().getDisplayId();
+ }
+ return Display.INVALID_DISPLAY;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 637c0ea..5272b66 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -845,7 +845,7 @@
mWindowFrames.mContainingFrame.bottom =
mWindowFrames.mContainingFrame.top + frozen.height();
}
- final WindowState imeWin = mService.mInputMethodWindow;
+ final WindowState imeWin = mService.mRoot.getCurrentInputMethodWindow();
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
@@ -3734,6 +3734,8 @@
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a1132d7..0ca0835 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -114,7 +114,6 @@
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.backup.IBackupManager;
-import android.app.backup.ISelectBackupTransportCallback;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -262,7 +261,6 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
@@ -895,7 +893,6 @@
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
- private static final String TAG_MANDATORY_BACKUP_TRANSPORT = "mandatory_backup_transport";
private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
private static final String TAG_METERED_DATA_DISABLED_PACKAGES
@@ -1016,10 +1013,6 @@
// Default title of confirm credentials screen
String organizationName = null;
- // The component name of the backup transport which has to be used if backups are mandatory
- // or null if backups are not mandatory.
- ComponentName mandatoryBackupTransport = null;
-
// Message for user switcher
String startUserSessionMessage = null;
String endUserSessionMessage = null;
@@ -1283,11 +1276,6 @@
out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutEnabled));
out.endTag(null, TAG_IS_LOGOUT_ENABLED);
}
- if (mandatoryBackupTransport != null) {
- out.startTag(null, TAG_MANDATORY_BACKUP_TRANSPORT);
- out.attribute(null, ATTR_VALUE, mandatoryBackupTransport.flattenToString());
- out.endTag(null, TAG_MANDATORY_BACKUP_TRANSPORT);
- }
if (startUserSessionMessage != null) {
out.startTag(null, TAG_START_USER_SESSION_MESSAGE);
out.text(startUserSessionMessage);
@@ -1476,9 +1464,6 @@
} else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
isLogoutEnabled = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
- } else if (TAG_MANDATORY_BACKUP_TRANSPORT.equals(tag)) {
- mandatoryBackupTransport = ComponentName.unflattenFromString(
- parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) {
type = parser.next();
if (type == XmlPullParser.TEXT) {
@@ -10538,8 +10523,7 @@
final int userId = UserHandle.getUserId(uid);
Intent intent = null;
if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
- DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction) ||
- DevicePolicyManager.POLICY_MANDATORY_BACKUPS.equals(restriction)) {
+ DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(userId);
final int N = policy.mAdminList.size();
@@ -10548,9 +10532,7 @@
if ((admin.disableCamera &&
DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
(admin.disableScreenCapture && DevicePolicyManager
- .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) ||
- (admin.mandatoryBackupTransport != null && DevicePolicyManager
- .POLICY_MANDATORY_BACKUPS.equals(restriction))) {
+ .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
intent = createShowAdminSupportIntent(admin.info.getComponent(), userId);
break;
}
@@ -11960,12 +11942,7 @@
}
Preconditions.checkNotNull(admin);
synchronized (getLockObject()) {
- ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(
- admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- if (!enabled) {
- activeAdmin.mandatoryBackupTransport = null;
- saveSettingsLocked(UserHandle.USER_SYSTEM);
- }
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
}
final long ident = mInjector.binderClearCallingIdentity();
@@ -12000,87 +11977,6 @@
}
@Override
- public boolean setMandatoryBackupTransport(
- ComponentName admin,
- ComponentName backupTransportComponent) {
- if (!mHasFeature) {
- return false;
- }
- Preconditions.checkNotNull(admin);
- enforceDeviceOwner(admin);
-
- final int callingUid = mInjector.binderGetCallingUid();
- final AtomicBoolean success = new AtomicBoolean(false);
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- final ISelectBackupTransportCallback selectBackupTransportCallbackInternal =
- new ISelectBackupTransportCallback.Stub() {
- public void onSuccess(String transportName) {
- saveMandatoryBackupTransport(admin, callingUid, backupTransportComponent);
- success.set(true);
- countDownLatch.countDown();
- }
-
- public void onFailure(int reason) {
- countDownLatch.countDown();
- }
- };
- final long identity = mInjector.binderClearCallingIdentity();
- try {
- IBackupManager ibm = mInjector.getIBackupManager();
- if (ibm != null && backupTransportComponent != null) {
- if (!ibm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
- ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- }
- ibm.selectBackupTransportAsync(
- backupTransportComponent, selectBackupTransportCallbackInternal);
- countDownLatch.await();
- if (success.get()) {
- ibm.setBackupEnabled(true);
- }
- } else if (backupTransportComponent == null) {
- saveMandatoryBackupTransport(admin, callingUid, backupTransportComponent);
- success.set(true);
- }
- } catch (RemoteException e) {
- throw new IllegalStateException("Failed to set mandatory backup transport.", e);
- } catch (InterruptedException e) {
- throw new IllegalStateException("Failed to set mandatory backup transport.", e);
- } finally {
- mInjector.binderRestoreCallingIdentity(identity);
- }
- return success.get();
- }
-
- private void saveMandatoryBackupTransport(
- ComponentName admin, int callingUid, ComponentName backupTransportComponent) {
- synchronized (getLockObject()) {
- ActiveAdmin activeAdmin =
- getActiveAdminWithPolicyForUidLocked(
- admin,
- DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
- callingUid);
- if (!Objects.equals(backupTransportComponent,
- activeAdmin.mandatoryBackupTransport)) {
- activeAdmin.mandatoryBackupTransport =
- backupTransportComponent;
- saveSettingsLocked(UserHandle.USER_SYSTEM);
- }
- }
- }
-
- @Override
- public ComponentName getMandatoryBackupTransport() {
- if (!mHasFeature) {
- return null;
- }
- synchronized (getLockObject()) {
- ActiveAdmin activeAdmin = getDeviceOwnerAdminLocked();
- return activeAdmin == null ? null : activeAdmin.mandatoryBackupTransport;
- }
- }
-
-
- @Override
public boolean bindDeviceAdminServiceAsUser(
@NonNull ComponentName admin, @NonNull IApplicationThread caller,
@Nullable IBinder activtiyToken, @NonNull Intent serviceIntent,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0b6a33f..c80b9d8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -82,6 +82,7 @@
import com.android.server.biometrics.fingerprint.FingerprintService;
import com.android.server.hdmi.HdmiControlService;
import com.android.server.input.InputManagerService;
+import com.android.server.inputmethod.InputMethodManagerService;
import com.android.server.job.JobSchedulerService;
import com.android.server.lights.LightsService;
import com.android.server.media.MediaResourceMonitorService;
diff --git a/services/net/java/android/net/dns/ResolvUtil.java b/services/net/java/android/net/dns/ResolvUtil.java
deleted file mode 100644
index d9d4b96..0000000
--- a/services/net/java/android/net/dns/ResolvUtil.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.net.dns;
-
-import static android.system.OsConstants.AI_ADDRCONFIG;
-
-import android.net.Network;
-import android.net.NetworkUtils;
-import android.system.GaiException;
-import android.system.OsConstants;
-import android.system.StructAddrinfo;
-
-import libcore.io.Libcore;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-
-/**
- * DNS resolution utility class.
- *
- * @hide
- */
-public class ResolvUtil {
- // Non-portable DNS resolution flag.
- private static final long NETID_USE_LOCAL_NAMESERVERS = 0x80000000L;
-
- private ResolvUtil() {}
-
- public static InetAddress[] blockingResolveAllLocally(Network network, String name)
- throws UnknownHostException {
- // Use AI_ADDRCONFIG by default
- return blockingResolveAllLocally(network, name, AI_ADDRCONFIG);
- }
-
- public static InetAddress[] blockingResolveAllLocally(
- Network network, String name, int aiFlags) throws UnknownHostException {
- final StructAddrinfo hints = new StructAddrinfo();
- hints.ai_flags = aiFlags;
- // Other hints identical to the default Inet6AddressImpl implementation
- hints.ai_family = OsConstants.AF_UNSPEC;
- hints.ai_socktype = OsConstants.SOCK_STREAM;
-
- final Network networkForResolv = getNetworkWithUseLocalNameserversFlag(network);
-
- try {
- return Libcore.os.android_getaddrinfo(name, hints, (int) networkForResolv.netId);
- } catch (GaiException gai) {
- gai.rethrowAsUnknownHostException(name + ": TLS-bypass resolution failed");
- return null; // keep compiler quiet
- }
- }
-
- public static Network getNetworkWithUseLocalNameserversFlag(Network network) {
- final long netidForResolv = NETID_USE_LOCAL_NAMESERVERS | (long) network.netId;
- return new Network((int) netidForResolv);
- }
-
- public static Network makeNetworkWithPrivateDnsBypass(Network network) {
- return new Network(network) {
- @Override
- public InetAddress[] getAllByName(String host) throws UnknownHostException {
- return blockingResolveAllLocally(network, host);
- }
- };
- }
-}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index ccc092d..0176dd4 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -228,6 +228,9 @@
// 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 @@
@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/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/net/java/android/net/ip/IpServer.java
similarity index 91%
rename from services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
rename to services/net/java/android/net/ip/IpServer.java
index 5accb45..33010a1 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity.tethering;
+package android.net.ip;
import static android.net.NetworkUtils.numericToInetAddress;
import static android.net.util.NetworkConstants.asByte;
@@ -31,11 +31,10 @@
import android.net.RouteInfo;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
-import android.net.ip.InterfaceController;
-import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
+import android.net.util.NetdService;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.Looper;
@@ -67,7 +66,22 @@
*
* @hide
*/
-public class TetherInterfaceStateMachine extends StateMachine {
+public class IpServer extends StateMachine {
+ public static final int STATE_UNAVAILABLE = 0;
+ public static final int STATE_AVAILABLE = 1;
+ public static final int STATE_TETHERED = 2;
+ public static final int STATE_LOCAL_ONLY = 3;
+
+ public static String getStateString(int state) {
+ switch (state) {
+ case STATE_UNAVAILABLE: return "UNAVAILABLE";
+ case STATE_AVAILABLE: return "AVAILABLE";
+ case STATE_TETHERED: return "TETHERED";
+ case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
+ }
+ return "UNKNOWN: " + state;
+ }
+
private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
private static final byte DOUG_ADAMS = (byte) 42;
@@ -83,15 +97,53 @@
// TODO: have this configurable
private static final int DHCP_LEASE_TIME_SECS = 3600;
- private final static String TAG = "TetherInterfaceSM";
+ private final static String TAG = "IpServer";
private final static boolean DBG = false;
private final static boolean VDBG = false;
private static final Class[] messageClasses = {
- TetherInterfaceStateMachine.class
+ IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
+ public static class Callback {
+ /**
+ * Notify that |who| has changed its tethering state.
+ *
+ * @param who the calling instance of IpServer
+ * @param state one of STATE_*
+ * @param lastError one of ConnectivityManager.TETHER_ERROR_*
+ */
+ public void updateInterfaceState(IpServer who, int state, int lastError) {}
+
+ /**
+ * Notify that |who| has new LinkProperties.
+ *
+ * @param who the calling instance of IpServer
+ * @param newLp the new LinkProperties to report
+ */
+ public void updateLinkProperties(IpServer who, LinkProperties newLp) {}
+ }
+
+ public static class Dependencies {
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+ return new RouterAdvertisementDaemon(ifParams);
+ }
+
+ public InterfaceParams getInterfaceParams(String ifName) {
+ return InterfaceParams.getByName(ifName);
+ }
+
+ public INetd getNetdService() {
+ return NetdService.getInstance();
+ }
+
+ public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+ DhcpServingParams params, SharedLog log) {
+ return new DhcpServer(looper, iface, params, log);
+ }
+ }
+
private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
// request from the user that it wants to tether
public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
@@ -123,7 +175,7 @@
private final INetworkManagementService mNMService;
private final INetd mNetd;
private final INetworkStatsService mStatsService;
- private final IControlsTethering mTetherController;
+ private final Callback mCallback;
private final InterfaceController mInterfaceCtrl;
private final String mIfaceName;
@@ -131,7 +183,7 @@
private final LinkProperties mLinkProperties;
private final boolean mUsingLegacyDhcp;
- private final TetheringDependencies mDeps;
+ private final Dependencies mDeps;
private int mLastError;
private int mServingMode;
@@ -148,17 +200,16 @@
private DhcpServer mDhcpServer;
private RaParams mLastRaParams;
- public TetherInterfaceStateMachine(
+ public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetworkManagementService nMService, INetworkStatsService statsService,
- IControlsTethering tetherController, boolean usingLegacyDhcp,
- TetheringDependencies deps) {
+ Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNMService = nMService;
mNetd = deps.getNetdService();
mStatsService = statsService;
- mTetherController = tetherController;
+ mCallback = callback;
mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
@@ -167,7 +218,7 @@
mDeps = deps;
resetLinkProperties();
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
- mServingMode = IControlsTethering.STATE_AVAILABLE;
+ mServingMode = STATE_AVAILABLE;
mInitialState = new InitialState();
mLocalHotspotState = new LocalHotspotState();
@@ -521,14 +572,12 @@
private void sendInterfaceState(int newInterfaceState) {
mServingMode = newInterfaceState;
- mTetherController.updateInterfaceState(
- TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
+ mCallback.updateInterfaceState(this, newInterfaceState, mLastError);
sendLinkProperties();
}
private void sendLinkProperties() {
- mTetherController.updateLinkProperties(
- TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
+ mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties));
}
private void resetLinkProperties() {
@@ -539,7 +588,7 @@
class InitialState extends State {
@Override
public void enter() {
- sendInterfaceState(IControlsTethering.STATE_AVAILABLE);
+ sendInterfaceState(STATE_AVAILABLE);
}
@Override
@@ -549,10 +598,10 @@
case CMD_TETHER_REQUESTED:
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
switch (message.arg1) {
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case STATE_LOCAL_ONLY:
transitionTo(mLocalHotspotState);
break;
- case IControlsTethering.STATE_TETHERED:
+ case STATE_TETHERED:
transitionTo(mTetheredState);
break;
default:
@@ -649,7 +698,7 @@
// problematic because transitioning during a multi-state jump yields
// a Log.wtf(). Ultimately, there should be only one ServingState,
// and forwarding and NAT rules should be handled by a coordinating
- // functional element outside of TetherInterfaceStateMachine.
+ // functional element outside of IpServer.
class LocalHotspotState extends BaseServingState {
@Override
public void enter() {
@@ -659,7 +708,7 @@
}
if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
- sendInterfaceState(IControlsTethering.STATE_LOCAL_ONLY);
+ sendInterfaceState(STATE_LOCAL_ONLY);
}
@Override
@@ -685,7 +734,7 @@
// problematic because transitioning during a multi-state jump yields
// a Log.wtf(). Ultimately, there should be only one ServingState,
// and forwarding and NAT rules should be handled by a coordinating
- // functional element outside of TetherInterfaceStateMachine.
+ // functional element outside of IpServer.
class TetheredState extends BaseServingState {
@Override
public void enter() {
@@ -695,7 +744,7 @@
}
if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
- sendInterfaceState(IControlsTethering.STATE_TETHERED);
+ sendInterfaceState(STATE_TETHERED);
}
@Override
@@ -817,7 +866,7 @@
@Override
public void enter() {
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
- sendInterfaceState(IControlsTethering.STATE_UNAVAILABLE);
+ sendInterfaceState(STATE_UNAVAILABLE);
}
}
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 2691701..8b59771 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -75,6 +75,7 @@
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/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 91a8857..57ebbfc 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -52,7 +52,6 @@
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderPackages;
import com.android.server.testing.shadows.ShadowAppBackupUtils;
-import com.android.server.testing.shadows.ShadowBackupPolicyEnforcer;
import com.android.server.testing.shadows.ShadowBinder;
import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
@@ -73,10 +72,7 @@
import org.robolectric.shadows.ShadowSettings;
@RunWith(FrameworkRobolectricTestRunner.class)
-@Config(
- manifest = Config.NONE,
- sdk = 26,
- shadows = {ShadowAppBackupUtils.class, ShadowBackupPolicyEnforcer.class})
+@Config(manifest = Config.NONE, sdk = 26, shadows = {ShadowAppBackupUtils.class})
@SystemLoaderPackages({"com.android.server.backup"})
@Presubmit
public class BackupManagerServiceTest {
@@ -118,15 +114,12 @@
mBaseStateDir = new File(cacheDir, "base_state");
// Corresponds to /cache/backup_stage
mDataDir = new File(cacheDir, "data");
-
- ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
}
@After
public void tearDown() throws Exception {
mBackupThread.quit();
ShadowAppBackupUtils.reset();
- ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
}
/* Tests for destination string */
@@ -252,7 +245,6 @@
private ComponentName mNewTransportComponent;
private TransportData mNewTransport;
private TransportMock mNewTransportMock;
- private ComponentName mOldTransportComponent;
private TransportData mOldTransport;
private TransportMock mOldTransportMock;
@@ -260,7 +252,6 @@
mNewTransport = backupTransport();
mNewTransportComponent = mNewTransport.getTransportComponent();
mOldTransport = d2dTransport();
- mOldTransportComponent = mOldTransport.getTransportComponent();
List<TransportMock> transportMocks =
setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
mNewTransportMock = transportMocks.get(0);
@@ -314,42 +305,6 @@
}
@Test
- public void testSelectBackupTransportAsync_whenMandatoryTransport() throws Exception {
- setUpForSelectTransport();
- ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mNewTransportComponent);
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
- verify(callback).onSuccess(eq(mNewTransport.transportName));
- verify(mTransportManager)
- .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
- }
-
- @Test
- public void testSelectBackupTransportAsync_whenOtherThanMandatoryTransport() throws Exception {
- setUpForSelectTransport();
- ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mOldTransportComponent);
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
- verify(callback).onFailure(eq(BackupManager.ERROR_BACKUP_NOT_ALLOWED));
- }
-
- @Test
public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
setUpForSelectTransport();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
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 0000000..3b6e038
--- /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/robotests/src/com/android/server/testing/shadows/ShadowBackupPolicyEnforcer.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupPolicyEnforcer.java
deleted file mode 100644
index e76b9d1..0000000
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupPolicyEnforcer.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.android.server.testing.shadows;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-
-import com.android.server.backup.BackupPolicyEnforcer;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-@Implements(BackupPolicyEnforcer.class)
-public class ShadowBackupPolicyEnforcer {
- @Nullable private static ComponentName sMandatoryBackupTransport;
-
- public static void setMandatoryBackupTransport(
- @Nullable ComponentName backupTransportComponent) {
- sMandatoryBackupTransport = backupTransportComponent;
- }
-
- @Implementation
- @Nullable
- public ComponentName getMandatoryBackupTransport() {
- return sMandatoryBackupTransport;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index eb28e1a..1eb88ba 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.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 @@
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/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 1023bc1..95f8fd1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -24,6 +24,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
@@ -73,7 +74,7 @@
mService = createActivityTaskManagerService();
mSupervisor = mService.mStackSupervisor;
mDefaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
- mStack = mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+ mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
}
@@ -140,7 +141,7 @@
}
@Test
- public void testPrimarySplitScreenToFullscreenWhenMovedToBack() throws Exception {
+ public void testPrimarySplitScreenRestoresWhenMovedToBack() throws Exception {
// Create primary splitscreen stack. This will create secondary stacks and places the
// existing fullscreen stack on the bottom.
final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
@@ -158,6 +159,60 @@
// Ensure no longer in splitscreen.
assertEquals(primarySplitScreen.getWindowingMode(), WINDOWING_MODE_FULLSCREEN);
+
+ // Ensure that the override mode is restored to undefined
+ assertEquals(primarySplitScreen.getOverrideWindowingMode(), WINDOWING_MODE_UNDEFINED);
+ }
+
+ @Test
+ public void testPrimarySplitScreenRestoresPreviousWhenMovedToBack() throws Exception {
+ // This time, start with a fullscreen activitystack
+ final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ primarySplitScreen.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+ // Assert windowing mode.
+ assertEquals(primarySplitScreen.getWindowingMode(), WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+ // Move primary to back.
+ primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
+ null /* task */);
+
+ // Assert that stack is at the bottom.
+ assertEquals(mDefaultDisplay.getIndexOf(primarySplitScreen), 0);
+
+ // Ensure that the override mode is restored to what it was (fullscreen)
+ assertEquals(primarySplitScreen.getOverrideWindowingMode(), WINDOWING_MODE_FULLSCREEN);
+ }
+
+ @Test
+ public void testStackInheritsDisplayWindowingMode() throws Exception {
+ final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
+ assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+
+ mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertEquals(WINDOWING_MODE_FREEFORM, primarySplitScreen.getWindowingMode());
+ assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+ }
+
+ @Test
+ public void testStackOverridesDisplayWindowingMode() throws Exception {
+ final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
+ assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+
+ primarySplitScreen.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ // setting windowing mode should still work even though resolved mode is already fullscreen
+ assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getOverrideWindowingMode());
+
+ mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index aef5537..bb8e5c5 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -520,6 +520,9 @@
private final ActivityStackSupervisor mSupervisor;
TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
super(supervisor, displayId);
+ // Normally this comes from display-properties as exposed by WM. Without that, just
+ // hard-code to FULLSCREEN for tests.
+ setWindowingMode(WINDOWING_MODE_FULLSCREEN);
mSupervisor = supervisor;
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index d94a5f345..16b127c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -57,7 +57,6 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.PasswordMetrics;
-import android.app.backup.ISelectBackupTransportCallback;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
@@ -2251,8 +2250,8 @@
assertEquals(UserManager.DISALLOW_ADJUST_VOLUME,
intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
- // Try with POLICY_DISABLE_CAMERA, POLICY_DISABLE_SCREEN_CAPTURE and
- // POLICY_MANDATORY_BACKUPS, which are not user restrictions
+ // Try with POLICY_DISABLE_CAMERA and POLICY_DISABLE_SCREEN_CAPTURE, which are not
+ // user restrictions
// Camera is not disabled
intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
@@ -2276,34 +2275,6 @@
assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE,
intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
- // Make the backup transport selection succeed
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- ISelectBackupTransportCallback callback =
- (ISelectBackupTransportCallback) invocation.getArguments()[1];
- if (callback != null) {
- callback.onSuccess("");
- }
- return null;
- }
- }).when(getServices().ibackupManager).selectBackupTransportAsync(
- any(ComponentName.class), any(ISelectBackupTransportCallback.class));
-
-
- // Backups are not mandatory
- intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
- assertNull(intent);
-
- // Backups are mandatory
- ComponentName transportComponent = ComponentName.unflattenFromString(
- "android/com.android.internal.backup.LocalTransport");
- dpm.setMandatoryBackupTransport(admin1, transportComponent);
- intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
- assertNotNull(intent);
- assertEquals(DevicePolicyManager.POLICY_MANDATORY_BACKUPS,
- intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
-
// Same checks for different user
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
// Camera should be disabled by device owner
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 e6ca03b..48dda01 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 @@
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 @@
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/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 5db0867..70e4ce4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -125,7 +125,7 @@
synchronized (sWm.mWindowMap) {
mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow");
mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow");
- sWm.mInputMethodWindow = mImeWindow;
+ mDisplayContent.mInputMethodWindow = mImeWindow;
mImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG,
"mImeDialogWindow");
mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow");
diff --git a/startop/OWNERS b/startop/OWNERS
new file mode 100644
index 0000000..bfe96d3
--- /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 0000000..d74d6f6
--- /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 0000000..780bb4e
--- /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 0000000..f96f802a
--- /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 0000000..bc4ec51
--- /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 0000000..4d5a53e
--- /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 0000000..1ff5fc6
--- /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/startop/scripts/app_startup/unlock_screen b/startop/scripts/app_startup/unlock_screen
new file mode 100755
index 0000000..478294c
--- /dev/null
+++ b/startop/scripts/app_startup/unlock_screen
@@ -0,0 +1,22 @@
+#!/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.
+
+# 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 383d10b..bb066ad 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -195,6 +195,8 @@
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 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 @@
// 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 @@
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 @@
out.writeTypedList(eventTimings);
writeBooleanAsByte(out, isVideoCall);
out.writeTypedList(videoEvents);
+ out.writeInt(callSource);
}
/** {@hide} */
@@ -385,6 +413,11 @@
this.videoEvents = videoEvents;
}
+ /** {@hide} */
+ public void setCallSource(int callSource) {
+ this.callSource = callSource;
+ }
+
public long getStartTimeMillis() {
return startTimeMillis;
}
@@ -443,6 +476,11 @@
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 48c1e24..4e22823 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -622,6 +622,18 @@
"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 1d9e605..8ebfec4 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1589,12 +1589,14 @@
}
/**
- * @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/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/android/net/ip/IpServerTest.java
similarity index 71%
rename from tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
rename to tests/net/java/android/net/ip/IpServerTest.java
index 5934653..cff0b54 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity.tethering;
+package android.net.ip;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -37,9 +37,9 @@
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static com.android.server.connectivity.tethering.IControlsTethering.STATE_AVAILABLE;
-import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
-import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
+import static android.net.ip.IpServer.STATE_AVAILABLE;
+import static android.net.ip.IpServer.STATE_TETHERED;
+import static android.net.ip.IpServer.STATE_UNAVAILABLE;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
@@ -50,7 +50,6 @@
import android.net.RouteInfo;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
-import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
@@ -74,7 +73,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class TetherInterfaceStateMachineTest {
+public class IpServerTest {
private static final String IFACE_NAME = "testnet1";
private static final String UPSTREAM_IFACE = "upstream0";
private static final String UPSTREAM_IFACE2 = "upstream1";
@@ -85,39 +84,38 @@
@Mock private INetworkManagementService mNMService;
@Mock private INetworkStatsService mStatsService;
- @Mock private IControlsTethering mTetherHelper;
+ @Mock private IpServer.Callback mCallback;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@Mock private SharedLog mSharedLog;
@Mock private DhcpServer mDhcpServer;
@Mock private RouterAdvertisementDaemon mRaDaemon;
- @Mock private TetheringDependencies mTetheringDependencies;
+ @Mock private IpServer.Dependencies mDependencies;
@Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor;
private final TestLooper mLooper = new TestLooper();
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
ArgumentCaptor.forClass(LinkProperties.class);
- private TetherInterfaceStateMachine mTestedSm;
+ private IpServer mIpServer;
private void initStateMachine(int interfaceType) throws Exception {
initStateMachine(interfaceType, false /* usingLegacyDhcp */);
}
private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
- mTestedSm = new TetherInterfaceStateMachine(
+ mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
- mNMService, mStatsService, mTetherHelper, usingLegacyDhcp,
- mTetheringDependencies);
- mTestedSm.start();
+ mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
+ mIpServer.start();
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
- reset(mNMService, mStatsService, mTetherHelper);
+ reset(mNMService, mStatsService, mCallback);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
- when(mTetheringDependencies.makeDhcpServer(
+ when(mDependencies.makeDhcpServer(
any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer);
- when(mTetheringDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
- when(mTetheringDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+ when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
+ when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
when(mRaDaemon.start()).thenReturn(true);
}
@@ -130,11 +128,11 @@
private void initTetheredStateMachine(int interfaceType, String upstreamIface,
boolean usingLegacyDhcp) throws Exception {
initStateMachine(interfaceType, usingLegacyDhcp);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
if (upstreamIface != null) {
dispatchTetherConnectionChanged(upstreamIface);
}
- reset(mNMService, mStatsService, mTetherHelper);
+ reset(mNMService, mStatsService, mCallback);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
}
@@ -145,34 +143,34 @@
@Test
public void startsOutAvailable() {
- mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
- TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
- false /* usingLegacyDhcp */, mTetheringDependencies);
- mTestedSm.start();
+ mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(),
+ TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback,
+ false /* usingLegacyDhcp */, mDependencies);
+ mIpServer.start();
mLooper.dispatchAll();
- verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
- verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService);
+ verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+ verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mCallback, mNMService, mStatsService);
}
@Test
public void shouldDoNothingUntilRequested() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
final int [] NOOP_COMMANDS = {
- TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED,
- TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR,
- TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR,
- TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR,
- TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR,
- TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR,
- TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED
+ IpServer.CMD_TETHER_UNREQUESTED,
+ IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
+ IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
+ IpServer.CMD_START_TETHERING_ERROR,
+ IpServer.CMD_STOP_TETHERING_ERROR,
+ IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
+ IpServer.CMD_TETHER_CONNECTION_CHANGED
};
for (int command : NOOP_COMMANDS) {
// None of these commands should trigger us to request action from
// the rest of the system.
dispatchCommand(command);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
}
@@ -180,57 +178,57 @@
public void handlesImmediateInterfaceDown() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
- dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
- verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
- verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
+ verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
+ verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
public void canBeTethered() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder inOrder = inOrder(mTetherHelper, mNMService);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+ InOrder inOrder = inOrder(mCallback, mNMService);
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
- inOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
public void canUnrequestTethering() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
+ dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
+ InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
- inOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
public void canBeTetheredAsUsb() throws Exception {
initStateMachine(TETHERING_USB);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder inOrder = inOrder(mTetherHelper, mNMService);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+ InOrder inOrder = inOrder(mCallback, mNMService);
inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
- inOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -243,7 +241,7 @@
InOrder inOrder = inOrder(mNMService);
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -257,7 +255,7 @@
inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -300,18 +298,18 @@
public void canUnrequestTetheringWithUpstream() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
+ dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
+ InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
inOrder.verify(mStatsService).forceUpdate();
inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
- inOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -322,15 +320,15 @@
if (shouldThrow) {
doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
}
- dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
- InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+ dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
+ InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
IFACE_NAME, mInterfaceConfiguration);
- usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
- usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ usbTeardownOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
+ usbTeardownOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
}
@@ -340,15 +338,15 @@
initStateMachine(TETHERING_USB);
doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+ InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
IFACE_NAME, mInterfaceConfiguration);
- usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
- usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ usbTeardownOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
+ usbTeardownOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@@ -358,13 +356,13 @@
doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+ InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
- usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
- usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ usbTeardownOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
+ usbTeardownOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@@ -372,11 +370,11 @@
public void ignoresDuplicateUpstreamNotifications() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
for (int i = 0; i < 5; i++) {
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
}
@@ -401,11 +399,11 @@
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- verify(mTetheringDependencies, never()).makeDhcpServer(any(), any(), any(), any());
+ verify(mDependencies, never()).makeDhcpServer(any(), any(), any(), any());
}
private void assertDhcpStarted(IpPrefix expectedPrefix) {
- verify(mTetheringDependencies, times(1)).makeDhcpServer(
+ verify(mDependencies, times(1)).makeDhcpServer(
eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog));
verify(mDhcpServer, times(1)).start();
final DhcpServingParams params = mDhcpParamsCaptor.getValue();
@@ -422,21 +420,21 @@
/**
* Send a command to the state machine under test, and run the event loop to idle.
*
- * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
+ * @param command One of the IpServer.CMD_* constants.
* @param arg1 An additional argument to pass.
*/
private void dispatchCommand(int command, int arg1) {
- mTestedSm.sendMessage(command, arg1);
+ mIpServer.sendMessage(command, arg1);
mLooper.dispatchAll();
}
/**
* Send a command to the state machine under test, and run the event loop to idle.
*
- * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
+ * @param command One of the IpServer.CMD_* constants.
*/
private void dispatchCommand(int command) {
- mTestedSm.sendMessage(command);
+ mIpServer.sendMessage(command);
mLooper.dispatchAll();
}
@@ -447,7 +445,7 @@
* @param upstreamIface String name of upstream interface (or null)
*/
private void dispatchTetherConnectionChanged(String upstreamIface) {
- mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
+ mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
new InterfaceSet(upstreamIface));
mLooper.dispatchAll();
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 102cb7c..99a5a69 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.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 @@
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 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(spiResp.resourceId),
+ eq(mUid),
anyString(),
anyString(),
eq(TEST_SPI),
@@ -189,8 +190,7 @@
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 @@
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 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(spiResp.resourceId),
+ eq(mUid),
anyString(),
anyString(),
eq(TEST_SPI),
@@ -270,7 +269,7 @@
verify(mMockNetd)
.ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
+ eq(mUid),
anyInt(),
anyString(),
anyString(),
@@ -305,7 +304,7 @@
verify(mMockNetd)
.ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
+ eq(mUid),
anyInt(),
anyString(),
anyString(),
@@ -361,13 +360,12 @@
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 @@
verify(mMockNetd, times(1))
.ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
+ eq(mUid),
anyString(),
anyString(),
eq(TEST_SPI),
@@ -397,8 +395,7 @@
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 @@
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 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
+ eq(mUid),
anyString(),
anyString(),
eq(TEST_SPI),
@@ -477,7 +473,7 @@
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
eq(pfd.getFileDescriptor()),
- eq(resourceId),
+ eq(mUid),
eq(IpSecManager.DIRECTION_OUT),
anyString(),
anyString(),
@@ -509,8 +505,7 @@
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 @@
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 @@
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/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 0d3b8e4..40d5544 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -75,6 +75,7 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetworkConstants;
@@ -99,10 +100,8 @@
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadHardwareInterface;
-import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
@@ -190,7 +189,7 @@
public class MockTetheringDependencies extends TetheringDependencies {
StateMachine upstreamNetworkMonitorMasterSM;
- ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+ ArrayList<IpServer> ipv6CoordinatorNotifyList;
int isTetheringSupportedCalls;
public void reset() {
@@ -213,29 +212,35 @@
@Override
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
- ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+ ArrayList<IpServer> notifyList, SharedLog log) {
ipv6CoordinatorNotifyList = notifyList;
return mIPv6TetheringCoordinator;
}
@Override
- public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
- return mRouterAdvertisementDaemon;
- }
+ public IpServer.Dependencies getIpServerDependencies() {
+ return new IpServer.Dependencies() {
+ @Override
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
+ InterfaceParams ifParams) {
+ return mRouterAdvertisementDaemon;
+ }
- @Override
- public INetd getNetdService() {
- return mNetd;
- }
+ @Override
+ public InterfaceParams getInterfaceParams(String ifName) {
+ final String[] ifaces = new String[] {
+ TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
+ final int index = ArrayUtils.indexOf(ifaces, ifName);
+ assertTrue("Non-mocked interface: " + ifName, index >= 0);
+ return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
+ MacAddress.ALL_ZEROS_ADDRESS);
+ }
- @Override
- public InterfaceParams getInterfaceParams(String ifName) {
- final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME,
- TEST_MOBILE_IFNAME };
- final int index = ArrayUtils.indexOf(ifaces, ifName);
- assertTrue("Non-mocked interface: " + ifName, index >= 0);
- return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
- MacAddress.ALL_ZEROS_ADDRESS);
+ @Override
+ public INetd getNetdService() {
+ return mNetd;
+ }
+ };
}
@Override
@@ -458,9 +463,9 @@
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
- // If, and only if, Tethering received an interface status changed
- // then it creates a TetherInterfaceStateMachine and sends out a
- // broadcast indicating that the interface is "available".
+ // If, and only if, Tethering received an interface status changed then
+ // it creates a IpServer and sends out a broadcast indicating that the
+ // interface is "available".
if (emulateInterfaceStatusChanged) {
assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
@@ -557,18 +562,18 @@
}
/**
- * Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator.
+ * Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator.
*/
private void sendIPv6TetherUpdates(NetworkState upstreamState) {
// IPv6TetheringCoordinator must have been notified of downstream
verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
- eq(IControlsTethering.STATE_TETHERED));
+ eq(IpServer.STATE_TETHERED));
- for (TetherInterfaceStateMachine tism :
+ for (IpServer ipSrv :
mTetheringDependencies.ipv6CoordinatorNotifyList) {
NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
- tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
+ ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
upstreamState.linkProperties.isIPv6Provisioned()
? ipv6OnlyState.linkProperties
: null);
@@ -812,7 +817,7 @@
// We verify get/set called thrice here: once for setup and twice during
// teardown because all events happen over the course of the single
- // dispatchAll() above. Note that once the TISM IPv4 address config
+ // dispatchAll() above. Note that once the IpServer IPv4 address config
// code is refactored the two calls during shutdown will revert to one.
verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(3))
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 29bf74c..e50c70d 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
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index f294728..4245700 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -195,9 +195,11 @@
print_error(field, "Unkown type for field: %s\n", field->name().c_str());
errorCount++;
continue;
- } else if (javaType == JAVA_TYPE_OBJECT) {
+ } else if (javaType == JAVA_TYPE_OBJECT &&
+ atomDecl->code < PULL_ATOM_START_ID) {
// Allow attribution chain, but only at position 1.
- print_error(field, "Message type not allowed for field: %s\n",
+ print_error(field,
+ "Message type not allowed for field in pushed atoms: %s\n",
field->name().c_str());
errorCount++;
continue;
@@ -233,12 +235,19 @@
java_type_t javaType = java_type(field);
AtomField atField(field->name(), javaType);
+ // Generate signature for pushed atoms
+ if (atomDecl->code < PULL_ATOM_START_ID) {
+ if (javaType == JAVA_TYPE_ENUM) {
+ // All enums are treated as ints when it comes to function signatures.
+ signature->push_back(JAVA_TYPE_INT);
+ collate_enums(*field->enum_type(), &atField);
+ } else {
+ signature->push_back(javaType);
+ }
+ }
if (javaType == JAVA_TYPE_ENUM) {
// All enums are treated as ints when it comes to function signatures.
- signature->push_back(JAVA_TYPE_INT);
collate_enums(*field->enum_type(), &atField);
- } else {
- signature->push_back(javaType);
}
atomDecl->fields.push_back(atField);
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index ccdd145..31b8b07 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -34,6 +34,8 @@
using google::protobuf::Descriptor;
using google::protobuf::FieldDescriptor;
+const int PULL_ATOM_START_ID = 10000;
+
/**
* The types for atom parameters.
*/
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 83a6631..9915479 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -18,8 +18,6 @@
namespace android {
namespace stats_log_api_gen {
-const int PULL_ATOM_START_ID = 1000;
-
int maxPushedAtomId = 2;
using android::os::statsd::Atom;