diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-12 18:53:04 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2022-05-12 18:53:04 +0000 |
commit | fc1458f4f4aa816a11d71dc67c5f1e83e4cf8abb (patch) | |
tree | 41f6366d428821fb33bf87e6068ddd0c94140a01 | |
parent | 2c50a3a7900919d5488db63b9eb8af67afdb4438 (diff) | |
parent | 780946834d26003c63c67116ffce0f1272450b84 (diff) | |
download | base-main-cg-testing-release.tar.gz |
Merge "Snap for 8580505 from 821acc6177c2c6459d223e0a6ecef7f3d24d8876 to main-cg-testing-release" into main-cg-testing-releasemain-cg-testing-release
47 files changed, 1358 insertions, 249 deletions
diff --git a/ApiDocs.bp b/ApiDocs.bp index 39f055b578ab..a542c5cf245c 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -134,11 +134,7 @@ droidstubs { defaults: ["android-non-updatable-doc-stubs-defaults"], srcs: [":all-modules-public-stubs-source"], args: metalava_framework_docs_args, - api_levels_annotations_enabled: true, - api_levels_annotations_dirs: [ - "sdk-dir", - "api-versions-jars-dir", - ], + api_levels_module: "api_versions_public", aidl: { include_dirs: [ "packages/modules/Connectivity/framework/aidl-export", diff --git a/api/api.go b/api/api.go index 9cd0132e2c6c..93f1354ce9e4 100644 --- a/api/api.go +++ b/api/api.go @@ -179,7 +179,7 @@ func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) { // Note: order matters: first parameter is the full api-versions.xml // after that the stubs files in any order // stubs files are all modules that export API surfaces EXCEPT ART - props.Srcs = append([]string{":framework-doc-stubs{.api_versions.xml}"}, createSrcs(modules, ".stubs{.jar}")...) + props.Srcs = append([]string{":api_versions_public{.api_versions.xml}"}, createSrcs(modules, ".stubs{.jar}")...) props.Dists = []android.Dist{{Targets: []string{"sdk"}}} ctx.CreateModule(genrule.GenRuleFactory, &props) } diff --git a/core/java/android/app/time/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java index a7c0e5c79607..a7828ab4c9dc 100644 --- a/core/java/android/app/time/ExternalTimeSuggestion.java +++ b/core/java/android/app/time/ExternalTimeSuggestion.java @@ -19,15 +19,14 @@ package android.app.time; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; +import android.app.timedetector.TimeSuggestionHelper; import android.os.Parcel; import android.os.Parcelable; +import android.os.ShellCommand; import android.os.TimestampedValue; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; +import java.io.PrintWriter; import java.util.List; import java.util.Objects; @@ -75,7 +74,9 @@ public final class ExternalTimeSuggestion implements Parcelable { public static final @NonNull Creator<ExternalTimeSuggestion> CREATOR = new Creator<ExternalTimeSuggestion>() { public ExternalTimeSuggestion createFromParcel(Parcel in) { - return ExternalTimeSuggestion.createFromParcel(in); + TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel( + ExternalTimeSuggestion.class, in); + return new ExternalTimeSuggestion(helper); } public ExternalTimeSuggestion[] newArray(int size) { @@ -83,10 +84,7 @@ public final class ExternalTimeSuggestion implements Parcelable { } }; - @NonNull - private final TimestampedValue<Long> mUnixEpochTime; - @Nullable - private ArrayList<String> mDebugInfo; + @NonNull private final TimeSuggestionHelper mTimeSuggestionHelper; /** * Creates a time suggestion cross-referenced to the elapsed realtime clock. See {@link @@ -98,17 +96,12 @@ public final class ExternalTimeSuggestion implements Parcelable { */ public ExternalTimeSuggestion(@ElapsedRealtimeLong long elapsedRealtimeMillis, @CurrentTimeMillisLong long suggestionMillis) { - mUnixEpochTime = new TimestampedValue(elapsedRealtimeMillis, suggestionMillis); + mTimeSuggestionHelper = new TimeSuggestionHelper(ExternalTimeSuggestion.class, + new TimestampedValue<>(elapsedRealtimeMillis, suggestionMillis)); } - private static ExternalTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); - ExternalTimeSuggestion suggestion = - new ExternalTimeSuggestion(utcTime.getReferenceTimeMillis(), utcTime.getValue()); - @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); - suggestion.mDebugInfo = debugInfo; - return suggestion; + private ExternalTimeSuggestion(@NonNull TimeSuggestionHelper helper) { + mTimeSuggestionHelper = Objects.requireNonNull(helper); } @Override @@ -118,8 +111,7 @@ public final class ExternalTimeSuggestion implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mUnixEpochTime, 0); - dest.writeList(mDebugInfo); + mTimeSuggestionHelper.handleWriteToParcel(dest, flags); } /** @@ -127,7 +119,7 @@ public final class ExternalTimeSuggestion implements Parcelable { */ @NonNull public TimestampedValue<Long> getUnixEpochTime() { - return mUnixEpochTime; + return mTimeSuggestionHelper.getUnixEpochTime(); } /** @@ -135,9 +127,7 @@ public final class ExternalTimeSuggestion implements Parcelable { */ @NonNull public List<String> getDebugInfo() { - return mDebugInfo == null - ? Collections.emptyList() - : Collections.unmodifiableList(mDebugInfo); + return mTimeSuggestionHelper.getDebugInfo(); } /** @@ -146,10 +136,7 @@ public final class ExternalTimeSuggestion implements Parcelable { * #equals(Object)} and {@link #hashCode()}. */ public void addDebugInfo(@NonNull String... debugInfos) { - if (mDebugInfo == null) { - mDebugInfo = new ArrayList<>(); - } - mDebugInfo.addAll(Arrays.asList(debugInfos)); + mTimeSuggestionHelper.addDebugInfo(debugInfos); } @Override @@ -161,18 +148,29 @@ public final class ExternalTimeSuggestion implements Parcelable { return false; } ExternalTimeSuggestion that = (ExternalTimeSuggestion) o; - return Objects.equals(mUnixEpochTime, that.mUnixEpochTime); + return mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper); } @Override public int hashCode() { - return Objects.hash(mUnixEpochTime); + return mTimeSuggestionHelper.hashCode(); } @Override public String toString() { - return "ExternalTimeSuggestion{" + "mUnixEpochTime=" + mUnixEpochTime - + ", mDebugInfo=" + mDebugInfo - + '}'; + return mTimeSuggestionHelper.handleToString(); + } + + /** @hide */ + public static ExternalTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) + throws IllegalArgumentException { + return new ExternalTimeSuggestion( + TimeSuggestionHelper.handleParseCommandLineArg(ExternalTimeSuggestion.class, cmd)); + } + + /** @hide */ + public static void printCommandLineOpts(PrintWriter pw) { + TimeSuggestionHelper.handlePrintCommandLineOpts( + pw, "External", ExternalTimeSuggestion.class); } } diff --git a/core/java/android/app/timedetector/GnssTimeSuggestion.java b/core/java/android/app/timedetector/GnssTimeSuggestion.java index 34f4565fb410..3531b19d0f65 100644 --- a/core/java/android/app/timedetector/GnssTimeSuggestion.java +++ b/core/java/android/app/timedetector/GnssTimeSuggestion.java @@ -17,30 +17,19 @@ package android.app.timedetector; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.os.ShellCommand; import android.os.TimestampedValue; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; +import java.io.PrintWriter; import java.util.List; import java.util.Objects; /** * A time signal from a GNSS source. * - * <p>{@code unixEpochTime} is the suggested time. The {@code unixEpochTime.value} is the number of - * milliseconds elapsed since 1/1/1970 00:00:00 UTC according to the Unix time system. The {@code - * unixEpochTime.referenceTimeMillis} is the value of the elapsed realtime clock when the {@code - * unixEpochTime.value} was established. Note that the elapsed realtime clock is considered accurate - * but it is volatile, so time suggestions cannot be persisted across device resets. - * - * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to - * record why the suggestion exists and how it was entered. This information exists only to aid in - * debugging and therefore is used by {@link #toString()}, but it is not for use in detection - * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}. + * <p>See {@link TimeSuggestionHelper} for property information. * * @hide */ @@ -49,7 +38,9 @@ public final class GnssTimeSuggestion implements Parcelable { public static final @NonNull Creator<GnssTimeSuggestion> CREATOR = new Creator<GnssTimeSuggestion>() { public GnssTimeSuggestion createFromParcel(Parcel in) { - return GnssTimeSuggestion.createFromParcel(in); + TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel( + GnssTimeSuggestion.class, in); + return new GnssTimeSuggestion(helper); } public GnssTimeSuggestion[] newArray(int size) { @@ -57,21 +48,14 @@ public final class GnssTimeSuggestion implements Parcelable { } }; - @NonNull private final TimestampedValue<Long> mUnixEpochTime; - @Nullable private ArrayList<String> mDebugInfo; + @NonNull private final TimeSuggestionHelper mTimeSuggestionHelper; public GnssTimeSuggestion(@NonNull TimestampedValue<Long> unixEpochTime) { - mUnixEpochTime = Objects.requireNonNull(unixEpochTime); - Objects.requireNonNull(unixEpochTime.getValue()); + mTimeSuggestionHelper = new TimeSuggestionHelper(GnssTimeSuggestion.class, unixEpochTime); } - private static GnssTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */); - GnssTimeSuggestion suggestion = new GnssTimeSuggestion(unixEpochTime); - @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); - suggestion.mDebugInfo = debugInfo; - return suggestion; + private GnssTimeSuggestion(@NonNull TimeSuggestionHelper helper) { + mTimeSuggestionHelper = Objects.requireNonNull(helper); } @Override @@ -81,19 +65,17 @@ public final class GnssTimeSuggestion implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mUnixEpochTime, 0); - dest.writeList(mDebugInfo); + mTimeSuggestionHelper.handleWriteToParcel(dest, flags); } @NonNull public TimestampedValue<Long> getUnixEpochTime() { - return mUnixEpochTime; + return mTimeSuggestionHelper.getUnixEpochTime(); } @NonNull public List<String> getDebugInfo() { - return mDebugInfo == null - ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo); + return mTimeSuggestionHelper.getDebugInfo(); } /** @@ -102,10 +84,7 @@ public final class GnssTimeSuggestion implements Parcelable { * {@link #equals(Object)} and {@link #hashCode()}. */ public void addDebugInfo(String... debugInfos) { - if (mDebugInfo == null) { - mDebugInfo = new ArrayList<>(); - } - mDebugInfo.addAll(Arrays.asList(debugInfos)); + mTimeSuggestionHelper.addDebugInfo(debugInfos); } @Override @@ -117,19 +96,29 @@ public final class GnssTimeSuggestion implements Parcelable { return false; } GnssTimeSuggestion that = (GnssTimeSuggestion) o; - return Objects.equals(mUnixEpochTime, that.mUnixEpochTime); + return mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper); } @Override public int hashCode() { - return Objects.hash(mUnixEpochTime); + return mTimeSuggestionHelper.hashCode(); } @Override public String toString() { - return "GnssTimeSuggestion{" - + "mUnixEpochTime=" + mUnixEpochTime - + ", mDebugInfo=" + mDebugInfo - + '}'; + return mTimeSuggestionHelper.handleToString(); + } + + /** Parses command line args to create a {@link GnssTimeSuggestion}. */ + public static GnssTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) + throws IllegalArgumentException { + TimeSuggestionHelper suggestionHelper = + TimeSuggestionHelper.handleParseCommandLineArg(GnssTimeSuggestion.class, cmd); + return new GnssTimeSuggestion(suggestionHelper); + } + + /** Prints the command line args needed to create a {@link GnssTimeSuggestion}. */ + public static void printCommandLineOpts(PrintWriter pw) { + TimeSuggestionHelper.handlePrintCommandLineOpts(pw, "GNSS", GnssTimeSuggestion.class); } } diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java index 76db33b1c32b..b447799eb84c 100644 --- a/core/java/android/app/timedetector/ManualTimeSuggestion.java +++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java @@ -20,27 +20,17 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.os.ShellCommand; import android.os.TimestampedValue; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; +import java.io.PrintWriter; import java.util.List; import java.util.Objects; /** * A time signal from a manual (user provided) source. * - * <p>{@code unixEpochTime} is the suggested time. The {@code unixEpochTime.value} is the number of - * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code unixEpochTime.referenceTimeMillis} - * is the value of the elapsed realtime clock when the {@code unixEpochTime.value} was established. - * Note that the elapsed realtime clock is considered accurate but it is volatile, so time - * suggestions cannot be persisted across device resets. - * - * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to - * record why the suggestion exists and how it was entered. This information exists only to aid in - * debugging and therefore is used by {@link #toString()}, but it is not for use in detection - * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}. + * <p>See {@link TimeSuggestionHelper} for property information. * * @hide */ @@ -49,7 +39,9 @@ public final class ManualTimeSuggestion implements Parcelable { public static final @NonNull Creator<ManualTimeSuggestion> CREATOR = new Creator<ManualTimeSuggestion>() { public ManualTimeSuggestion createFromParcel(Parcel in) { - return ManualTimeSuggestion.createFromParcel(in); + TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel( + ManualTimeSuggestion.class, in); + return new ManualTimeSuggestion(helper); } public ManualTimeSuggestion[] newArray(int size) { @@ -57,21 +49,14 @@ public final class ManualTimeSuggestion implements Parcelable { } }; - @NonNull private final TimestampedValue<Long> mUnixEpochTime; - @Nullable private ArrayList<String> mDebugInfo; + @NonNull private final TimeSuggestionHelper mTimeSuggestionHelper; public ManualTimeSuggestion(@NonNull TimestampedValue<Long> unixEpochTime) { - mUnixEpochTime = Objects.requireNonNull(unixEpochTime); - Objects.requireNonNull(unixEpochTime.getValue()); + mTimeSuggestionHelper = new TimeSuggestionHelper(ManualTimeSuggestion.class, unixEpochTime); } - private static ManualTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */); - ManualTimeSuggestion suggestion = new ManualTimeSuggestion(unixEpochTime); - @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); - suggestion.mDebugInfo = debugInfo; - return suggestion; + private ManualTimeSuggestion(@NonNull TimeSuggestionHelper helper) { + mTimeSuggestionHelper = Objects.requireNonNull(helper); } @Override @@ -81,19 +66,17 @@ public final class ManualTimeSuggestion implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mUnixEpochTime, 0); - dest.writeList(mDebugInfo); + mTimeSuggestionHelper.handleWriteToParcel(dest, flags); } @NonNull public TimestampedValue<Long> getUnixEpochTime() { - return mUnixEpochTime; + return mTimeSuggestionHelper.getUnixEpochTime(); } @NonNull public List<String> getDebugInfo() { - return mDebugInfo == null - ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo); + return mTimeSuggestionHelper.getDebugInfo(); } /** @@ -102,10 +85,7 @@ public final class ManualTimeSuggestion implements Parcelable { * {@link #equals(Object)} and {@link #hashCode()}. */ public void addDebugInfo(String... debugInfos) { - if (mDebugInfo == null) { - mDebugInfo = new ArrayList<>(); - } - mDebugInfo.addAll(Arrays.asList(debugInfos)); + mTimeSuggestionHelper.addDebugInfo(debugInfos); } @Override @@ -117,19 +97,28 @@ public final class ManualTimeSuggestion implements Parcelable { return false; } ManualTimeSuggestion that = (ManualTimeSuggestion) o; - return Objects.equals(mUnixEpochTime, that.mUnixEpochTime); + return mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper); } @Override public int hashCode() { - return Objects.hash(mUnixEpochTime); + return mTimeSuggestionHelper.hashCode(); } @Override public String toString() { - return "ManualTimeSuggestion{" - + "mUnixEpochTime=" + mUnixEpochTime - + ", mDebugInfo=" + mDebugInfo - + '}'; + return mTimeSuggestionHelper.handleToString(); + } + + /** @hide */ + public static ManualTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) + throws IllegalArgumentException { + return new ManualTimeSuggestion( + TimeSuggestionHelper.handleParseCommandLineArg(ManualTimeSuggestion.class, cmd)); + } + + /** @hide */ + public static void printCommandLineOpts(PrintWriter pw) { + TimeSuggestionHelper.handlePrintCommandLineOpts(pw, "Manual", ManualTimeSuggestion.class); } } diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java index e22f1d6ea8be..e93c75cb0a0c 100644 --- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java +++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java @@ -17,31 +17,19 @@ package android.app.timedetector; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.os.ShellCommand; import android.os.TimestampedValue; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; +import java.io.PrintWriter; import java.util.List; import java.util.Objects; /** * A time signal from a network time source like NTP. * - * <p>{@code unixEpochTime} contains the suggested time. The {@code unixEpochTime.value} is the - * number of milliseconds elapsed since 1/1/1970 00:00:00 UTC according to the Unix time system. - * The {@code unixEpochTime.referenceTimeMillis} is the value of the elapsed realtime clock when - * the {@code unixEpochTime.value} was established. Note that the elapsed realtime clock is - * considered accurate but it is volatile, so time suggestions cannot be persisted across device - * resets. - * - * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to - * record why the suggestion exists and how it was determined. This information exists only to aid - * in debugging and therefore is used by {@link #toString()}, but it is not for use in detection - * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}. + * <p>See {@link TimeSuggestionHelper} for property information. * * @hide */ @@ -50,7 +38,9 @@ public final class NetworkTimeSuggestion implements Parcelable { public static final @NonNull Creator<NetworkTimeSuggestion> CREATOR = new Creator<NetworkTimeSuggestion>() { public NetworkTimeSuggestion createFromParcel(Parcel in) { - return NetworkTimeSuggestion.createFromParcel(in); + TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel( + NetworkTimeSuggestion.class, in); + return new NetworkTimeSuggestion(helper); } public NetworkTimeSuggestion[] newArray(int size) { @@ -58,21 +48,15 @@ public final class NetworkTimeSuggestion implements Parcelable { } }; - @NonNull private final TimestampedValue<Long> mUnixEpochTime; - @Nullable private ArrayList<String> mDebugInfo; + @NonNull private final TimeSuggestionHelper mTimeSuggestionHelper; public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> unixEpochTime) { - mUnixEpochTime = Objects.requireNonNull(unixEpochTime); - Objects.requireNonNull(unixEpochTime.getValue()); + mTimeSuggestionHelper = new TimeSuggestionHelper( + NetworkTimeSuggestion.class, unixEpochTime); } - private static NetworkTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */); - NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(unixEpochTime); - @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); - suggestion.mDebugInfo = debugInfo; - return suggestion; + private NetworkTimeSuggestion(@NonNull TimeSuggestionHelper helper) { + mTimeSuggestionHelper = Objects.requireNonNull(helper); } @Override @@ -82,35 +66,30 @@ public final class NetworkTimeSuggestion implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mUnixEpochTime, 0); - dest.writeList(mDebugInfo); + mTimeSuggestionHelper.handleWriteToParcel(dest, flags); } @NonNull public TimestampedValue<Long> getUnixEpochTime() { - return mUnixEpochTime; + return mTimeSuggestionHelper.getUnixEpochTime(); } @NonNull public List<String> getDebugInfo() { - return mDebugInfo == null - ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo); + return mTimeSuggestionHelper.getDebugInfo(); } /** * Associates information with the instance that can be useful for debugging / logging. The - * information is present in {@link #toString()} but is not considered for - * {@link #equals(Object)} and {@link #hashCode()}. + * information is present in {@link #toString()} but is not considered for {@link + * #equals(Object)} and {@link #hashCode()}. */ public void addDebugInfo(String... debugInfos) { - if (mDebugInfo == null) { - mDebugInfo = new ArrayList<>(); - } - mDebugInfo.addAll(Arrays.asList(debugInfos)); + mTimeSuggestionHelper.addDebugInfo(debugInfos); } @Override - public boolean equals(@Nullable Object o) { + public boolean equals(Object o) { if (this == o) { return true; } @@ -118,19 +97,28 @@ public final class NetworkTimeSuggestion implements Parcelable { return false; } NetworkTimeSuggestion that = (NetworkTimeSuggestion) o; - return Objects.equals(mUnixEpochTime, that.mUnixEpochTime); + return mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper); } @Override public int hashCode() { - return Objects.hash(mUnixEpochTime); + return mTimeSuggestionHelper.hashCode(); } @Override public String toString() { - return "NetworkTimeSuggestion{" - + "mUnixEpochTime=" + mUnixEpochTime - + ", mDebugInfo=" + mDebugInfo - + '}'; + return mTimeSuggestionHelper.handleToString(); + } + + /** @hide */ + public static NetworkTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) + throws IllegalArgumentException { + return new NetworkTimeSuggestion( + TimeSuggestionHelper.handleParseCommandLineArg(NetworkTimeSuggestion.class, cmd)); + } + + /** @hide */ + public static void printCommandLineOpts(PrintWriter pw) { + TimeSuggestionHelper.handlePrintCommandLineOpts(pw, "Network", NetworkTimeSuggestion.class); } } diff --git a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java index 4ff75174ab57..6f204d661004 100644 --- a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java +++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java @@ -20,8 +20,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.os.ShellCommand; import android.os.TimestampedValue; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -88,6 +90,61 @@ public final class TelephonyTimeSuggestion implements Parcelable { return suggestion; } + /** @hide */ + public static TelephonyTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) + throws IllegalArgumentException { + Integer slotIndex = null; + Long referenceTimeMillis = null; + Long unixEpochTimeMillis = null; + String opt; + while ((opt = cmd.getNextArg()) != null) { + switch (opt) { + case "--slot_index": { + slotIndex = Integer.parseInt(cmd.getNextArgRequired()); + break; + } + case "--reference_time": { + referenceTimeMillis = Long.parseLong(cmd.getNextArgRequired()); + break; + } + case "--unix_epoch_time": { + unixEpochTimeMillis = Long.parseLong(cmd.getNextArgRequired()); + break; + } + default: { + throw new IllegalArgumentException("Unknown option: " + opt); + } + } + } + + if (slotIndex == null) { + throw new IllegalArgumentException("No slotIndex specified."); + } + if (referenceTimeMillis == null) { + throw new IllegalArgumentException("No referenceTimeMillis specified."); + } + if (unixEpochTimeMillis == null) { + throw new IllegalArgumentException("No unixEpochTimeMillis specified."); + } + + TimestampedValue<Long> timeSignal = + new TimestampedValue<>(referenceTimeMillis, unixEpochTimeMillis); + Builder builder = new Builder(slotIndex) + .setUnixEpochTime(timeSignal) + .addDebugInfo("Command line injection"); + return builder.build(); + } + + /** @hide */ + public static void printCommandLineOpts(PrintWriter pw) { + pw.println("Telephony suggestion options:"); + pw.println(" --slot_index <number>"); + pw.println(" --reference_time <elapsed realtime millis>"); + pw.println(" --unix_epoch_time <Unix epoch time millis>"); + pw.println(); + pw.println("See " + TelephonyTimeSuggestion.class.getName() + " for more information"); + } + @Override public int describeContents() { return 0; diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index a3562301cde7..f0d777611620 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -44,6 +44,36 @@ public interface TimeDetector { String SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED = "is_auto_detection_enabled"; /** + * A shell command that injects a manual time suggestion. + * @hide + */ + String SHELL_COMMAND_SUGGEST_MANUAL_TIME = "suggest_manual_time"; + + /** + * A shell command that injects a telephony time suggestion. + * @hide + */ + String SHELL_COMMAND_SUGGEST_TELEPHONY_TIME = "suggest_telephony_time"; + + /** + * A shell command that injects a network time suggestion. + * @hide + */ + String SHELL_COMMAND_SUGGEST_NETWORK_TIME = "suggest_network_time"; + + /** + * A shell command that injects a GNSS time suggestion. + * @hide + */ + String SHELL_COMMAND_SUGGEST_GNSS_TIME = "suggest_gnss_time"; + + /** + * A shell command that injects a external time suggestion. + * @hide + */ + String SHELL_COMMAND_SUGGEST_EXTERNAL_TIME = "suggest_external_time"; + + /** * A shared utility method to create a {@link ManualTimeSuggestion}. * * @hide diff --git a/core/java/android/app/timedetector/TimeSuggestionHelper.java b/core/java/android/app/timedetector/TimeSuggestionHelper.java new file mode 100644 index 000000000000..9b99be61b3c8 --- /dev/null +++ b/core/java/android/app/timedetector/TimeSuggestionHelper.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2022 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.app.timedetector; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.ShellCommand; +import android.os.TimestampedValue; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * A delegate class to support time suggestion classes that could diverge in the future. This class + * exists purely for code re-use and provides support methods. It avoids class inheritance + * deliberately to allow each suggestion to evolve in different directions later without affecting + * SDK APIs. + * + * <p>{@code unixEpochTime} is the suggested time. The {@code unixEpochTime.value} is the number of + * milliseconds elapsed since 1/1/1970 00:00:00 UTC according to the Unix time system. The {@code + * unixEpochTime.referenceTimeMillis} is the value of the elapsed realtime clock when the {@code + * unixEpochTime.value} was established. Note that the elapsed realtime clock is considered accurate + * but it is volatile, so time suggestions cannot be persisted across device resets. + * + * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to + * record why the suggestion exists and how it was entered. This information exists only to aid in + * debugging and therefore is used by {@link #toString()}, but it is not for use in detection + * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}. + * + * @hide + */ +public final class TimeSuggestionHelper { + + @NonNull private final Class<?> mHelpedClass; + @NonNull private final TimestampedValue<Long> mUnixEpochTime; + @Nullable private ArrayList<String> mDebugInfo; + + /** Creates a helper for the specified class, containing the supplied properties. */ + public TimeSuggestionHelper(@NonNull Class<?> helpedClass, + @NonNull TimestampedValue<Long> unixEpochTime) { + mHelpedClass = Objects.requireNonNull(helpedClass); + mUnixEpochTime = Objects.requireNonNull(unixEpochTime); + Objects.requireNonNull(unixEpochTime.getValue()); + } + + /** See {@link TimeSuggestionHelper} for property details. */ + @NonNull + public TimestampedValue<Long> getUnixEpochTime() { + return mUnixEpochTime; + } + + /** See {@link TimeSuggestionHelper} for information about {@code debugInfo}. */ + @NonNull + public List<String> getDebugInfo() { + return mDebugInfo == null + ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo); + } + + /** + * Associates information with the instance that can be useful for debugging / logging. + * + * <p>See {@link TimeSuggestionHelper} for more information about {@code debugInfo}. + */ + public void addDebugInfo(@NonNull String debugInfo) { + if (mDebugInfo == null) { + mDebugInfo = new ArrayList<>(); + } + mDebugInfo.add(debugInfo); + } + + /** + * Associates information with the instance that can be useful for debugging / logging. The + * information is present in {@link #toString()} but is not considered for + * {@link #equals(Object)} and {@link #hashCode()}. + */ + public void addDebugInfo(String... debugInfos) { + addDebugInfo(Arrays.asList(debugInfos)); + } + + /** + * Associates information with the instance that can be useful for debugging / logging. + * + * <p>See {@link TimeSuggestionHelper} for more information about {@code debugInfo}. + */ + public void addDebugInfo(@NonNull List<String> debugInfo) { + if (mDebugInfo == null) { + mDebugInfo = new ArrayList<>(debugInfo.size()); + } + mDebugInfo.addAll(debugInfo); + } + + /** + * Implemented in case users call this insteam of {@link #handleEquals(TimeSuggestionHelper)}. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TimeSuggestionHelper that = (TimeSuggestionHelper) o; + return handleEquals(that); + } + + /** Used to implement {@link Object#equals(Object)}. */ + public boolean handleEquals(TimeSuggestionHelper o) { + return Objects.equals(mHelpedClass, o.mHelpedClass) + && Objects.equals(mUnixEpochTime, o.mUnixEpochTime); + } + + @Override + public int hashCode() { + return Objects.hash(mUnixEpochTime); + } + + /** Used to implement {@link Object#toString()}. */ + public String handleToString() { + return mHelpedClass.getSimpleName() + "{" + + "mUnixEpochTime=" + mUnixEpochTime + + ", mDebugInfo=" + mDebugInfo + + '}'; + } + + /** Constructs a helper with suggestion state from a Parcel. */ + public static TimeSuggestionHelper handleCreateFromParcel(@NonNull Class<?> helpedClass, + @NonNull Parcel in) { + @SuppressWarnings("unchecked") + TimestampedValue<Long> unixEpochTime = in.readParcelable( + null /* classLoader */, TimestampedValue.class); + TimeSuggestionHelper suggestionHelper = + new TimeSuggestionHelper(helpedClass, unixEpochTime); + suggestionHelper.mDebugInfo = in.readArrayList(null /* classLoader */, String.class); + return suggestionHelper; + } + + /** Writes the helper suggestion state to a Parcel. */ + public void handleWriteToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mUnixEpochTime, 0); + dest.writeList(mDebugInfo); + } + + /** Parses command line args to create a {@link TimeSuggestionHelper}. */ + public static TimeSuggestionHelper handleParseCommandLineArg( + @NonNull Class<?> helpedClass, @NonNull ShellCommand cmd) + throws IllegalArgumentException { + Long referenceTimeMillis = null; + Long unixEpochTimeMillis = null; + String opt; + while ((opt = cmd.getNextArg()) != null) { + switch (opt) { + case "--reference_time": { + referenceTimeMillis = Long.parseLong(cmd.getNextArgRequired()); + break; + } + case "--unix_epoch_time": { + unixEpochTimeMillis = Long.parseLong(cmd.getNextArgRequired()); + break; + } + default: { + throw new IllegalArgumentException("Unknown option: " + opt); + } + } + } + + if (referenceTimeMillis == null) { + throw new IllegalArgumentException("No referenceTimeMillis specified."); + } + if (unixEpochTimeMillis == null) { + throw new IllegalArgumentException("No unixEpochTimeMillis specified."); + } + + TimestampedValue<Long> timeSignal = + new TimestampedValue<>(referenceTimeMillis, unixEpochTimeMillis); + TimeSuggestionHelper suggestionHelper = new TimeSuggestionHelper(helpedClass, timeSignal); + suggestionHelper.addDebugInfo("Command line injection"); + return suggestionHelper; + } + + /** Prints the command line args needed to create a {@link TimeSuggestionHelper}. */ + public static void handlePrintCommandLineOpts( + @NonNull PrintWriter pw, @NonNull String typeName, @NonNull Class<?> clazz) { + pw.printf("%s suggestion options:\n", typeName); + pw.println(" --reference_time <elapsed realtime millis>"); + pw.println(" --unix_epoch_time <Unix epoch time millis>"); + pw.println(); + pw.println("See " + clazz.getName() + " for more information"); + } +} diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index f4f9f9437eb0..1122c20dda22 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1153,8 +1153,8 @@ static void relabelDir(const char* path, const char* context, fail_fn_t fail_fn) } } -// Relabel all directories under a path non-recursively. -static void relabelAllDirs(const char* path, const char* context, fail_fn_t fail_fn) { +// Relabel the subdirectories and symlinks in the given directory, non-recursively. +static void relabelSubdirs(const char* path, const char* context, fail_fn_t fail_fn) { DIR* dir = opendir(path); if (dir == nullptr) { fail_fn(CREATE_ERROR("Failed to opendir %s", path)); @@ -1231,11 +1231,19 @@ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_d snprintf(internalDePath, PATH_MAX, "/data/user_de"); snprintf(externalPrivateMountPath, PATH_MAX, "/mnt/expand"); - char* dataDataContext = nullptr; - if (getfilecon(internalDePath, &dataDataContext) < 0) { - fail_fn(CREATE_ERROR("Unable to getfilecon on %s %s", internalDePath, + // Get the "u:object_r:system_userdir_file:s0" security context. This can be + // gotten from several different places; we use /data/user. + char* dataUserdirContext = nullptr; + if (getfilecon(internalCePath, &dataUserdirContext) < 0) { + fail_fn(CREATE_ERROR("Unable to getfilecon on %s %s", internalCePath, strerror(errno))); } + // Get the "u:object_r:system_data_file:s0" security context. This can be + // gotten from several different places; we use /data/misc. + char* dataFileContext = nullptr; + if (getfilecon("/data/misc", &dataFileContext) < 0) { + fail_fn(CREATE_ERROR("Unable to getfilecon on /data/misc %s", strerror(errno))); + } MountAppDataTmpFs(internalLegacyCePath, fail_fn); MountAppDataTmpFs(internalCePath, fail_fn); @@ -1330,19 +1338,19 @@ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_d // the file operations on tmpfs. If we set the label when we mount // tmpfs, SELinux will not happy as we are changing system_data_files. // Relabel dir under /data/user, including /data/user/0 - relabelAllDirs(internalCePath, dataDataContext, fail_fn); + relabelSubdirs(internalCePath, dataFileContext, fail_fn); // Relabel /data/user - relabelDir(internalCePath, dataDataContext, fail_fn); + relabelDir(internalCePath, dataUserdirContext, fail_fn); // Relabel /data/data - relabelDir(internalLegacyCePath, dataDataContext, fail_fn); + relabelDir(internalLegacyCePath, dataFileContext, fail_fn); - // Relabel dir under /data/user_de - relabelAllDirs(internalDePath, dataDataContext, fail_fn); + // Relabel subdirectories of /data/user_de + relabelSubdirs(internalDePath, dataFileContext, fail_fn); // Relabel /data/user_de - relabelDir(internalDePath, dataDataContext, fail_fn); + relabelDir(internalDePath, dataUserdirContext, fail_fn); // Relabel CE and DE dirs under /mnt/expand dir = opendir(externalPrivateMountPath); @@ -1355,14 +1363,15 @@ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_d auto cePath = StringPrintf("%s/user", volPath.c_str()); auto dePath = StringPrintf("%s/user_de", volPath.c_str()); - relabelAllDirs(cePath.c_str(), dataDataContext, fail_fn); - relabelDir(cePath.c_str(), dataDataContext, fail_fn); - relabelAllDirs(dePath.c_str(), dataDataContext, fail_fn); - relabelDir(dePath.c_str(), dataDataContext, fail_fn); + relabelSubdirs(cePath.c_str(), dataFileContext, fail_fn); + relabelDir(cePath.c_str(), dataUserdirContext, fail_fn); + relabelSubdirs(dePath.c_str(), dataFileContext, fail_fn); + relabelDir(dePath.c_str(), dataUserdirContext, fail_fn); } closedir(dir); - freecon(dataDataContext); + freecon(dataUserdirContext); + freecon(dataFileContext); } static void insertPackagesToMergedList(JNIEnv* env, diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index b3851a5ffbf2..d24a95db3cd9 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -35,7 +35,7 @@ rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max If this is configured as an empty string, the system default will be applied. --> - <string name="config_tcp_buffers" translatable="false"></string> + <string name="config_tcp_buffers" translatable="false">2097152,6291456,16777216,512000,2097152,8388608</string> <java-symbol type="string" name="config_tcp_buffers" /> <!-- What source to use to estimate link upstream and downstream bandwidth capacities. @@ -109,4 +109,12 @@ <!-- Telephony qualified networks service class name to bind to by default. --> <string name="config_qualified_networks_service_class" translatable="false"></string> <java-symbol type="string" name="config_qualified_networks_service_class" /> + + <!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks + will not perform handover if the target transport is out of service, or VoPS not + supported. The network will be torn down on the source transport, and will be + re-established on the target transport when condition is allowed for bringing up a + new network. --> + <bool name="config_enhanced_iwlan_handover_check">true</bool> + <java-symbol type="bool" name="config_enhanced_iwlan_handover_check" /> </resources> diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp index de2a013df999..6f2366e32574 100644 --- a/core/tests/PackageInstallerSessions/Android.bp +++ b/core/tests/PackageInstallerSessions/Android.bp @@ -50,6 +50,6 @@ android_test { ":PackageManagerTestAppVersion1", ], - platform_apis: true, + sdk_version: "core_platform", test_suites: ["device-tests"], } diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp index 43a96793447d..2b34ee2646f3 100644 --- a/core/tests/bugreports/Android.bp +++ b/core/tests/bugreports/Android.bp @@ -35,7 +35,7 @@ android_test { "truth-prebuilt", ], test_suites: ["general-tests"], - platform_apis: true, + sdk_version: "test_current", } filegroup { diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index b52118425a2d..f971af1466c3 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -72,7 +72,7 @@ android_test { "libpowermanagertest_jni", ], - platform_apis: true, + sdk_version: "core_platform", test_suites: ["device-tests"], certificate: "platform", diff --git a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java new file mode 100644 index 000000000000..90b33058d4e8 --- /dev/null +++ b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2022 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.app.time; + +import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions; + +import static org.junit.Assert.assertEquals; + +import android.os.ShellCommand; + +import org.junit.Test; + +/** + * Tests for non-SDK methods on {@link ExternalTimeSuggestion}. + * Also see {@link android.app.time.cts.ExternalTimeSuggestionTest} + */ +public class ExternalTimeSuggestionTest { + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noReferenceTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--unix_epoch_time 12345"); + ExternalTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noUnixEpochTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321"); + ExternalTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test + public void testParseCommandLineArg_validSuggestion() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345"); + ExternalTimeSuggestion expectedSuggestion = new ExternalTimeSuggestion(54321L, 12345L); + ExternalTimeSuggestion actualSuggestion = + ExternalTimeSuggestion.parseCommandLineArg(testShellCommand); + assertEquals(expectedSuggestion, actualSuggestion); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_unknownArgument() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345 --bad_arg 0"); + ExternalTimeSuggestion.parseCommandLineArg(testShellCommand); + } +} diff --git a/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java index e248010319e1..af403a20ae7b 100644 --- a/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/timedetector/GnssTimeSuggestionTest.java @@ -18,10 +18,12 @@ package android.app.timedetector; import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable; +import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import android.os.ShellCommand; import android.os.TimestampedValue; import org.junit.Test; @@ -63,4 +65,36 @@ public class GnssTimeSuggestionTest { GnssTimeSuggestion rtSuggestion = roundTripParcelable(suggestion); assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo()); } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noReferenceTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--unix_epoch_time 12345"); + GnssTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noUnixEpochTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321"); + GnssTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test + public void testParseCommandLineArg_validSuggestion() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345"); + TimestampedValue<Long> timeSignal = new TimestampedValue<>(54321L, 12345L); + GnssTimeSuggestion expectedSuggestion = new GnssTimeSuggestion(timeSignal); + GnssTimeSuggestion actualSuggestion = + GnssTimeSuggestion.parseCommandLineArg(testShellCommand); + assertEquals(expectedSuggestion, actualSuggestion); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_unknownArgument() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345 --bad_arg 0"); + GnssTimeSuggestion.parseCommandLineArg(testShellCommand); + } } diff --git a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java index 750ffa1c9a54..94218cdea0ed 100644 --- a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java @@ -18,10 +18,12 @@ package android.app.timedetector; import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable; +import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import android.os.ShellCommand; import android.os.TimestampedValue; import org.junit.Test; @@ -63,4 +65,36 @@ public class ManualTimeSuggestionTest { ManualTimeSuggestion rtSuggestion = roundTripParcelable(suggestion); assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo()); } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noReferenceTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--unix_epoch_time 12345"); + ManualTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noUnixEpochTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321"); + ManualTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test + public void testParseCommandLineArg_validSuggestion() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345"); + TimestampedValue<Long> timeSignal = new TimestampedValue<>(54321L, 12345L); + ManualTimeSuggestion expectedSuggestion = new ManualTimeSuggestion(timeSignal); + ManualTimeSuggestion actualSuggestion = + ManualTimeSuggestion.parseCommandLineArg(testShellCommand); + assertEquals(expectedSuggestion, actualSuggestion); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_unknownArgument() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345 --bad_arg 0"); + ManualTimeSuggestion.parseCommandLineArg(testShellCommand); + } } diff --git a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java index b88c36f20bc6..0e09dd3e9aab 100644 --- a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java @@ -18,10 +18,12 @@ package android.app.timedetector; import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable; +import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import android.os.ShellCommand; import android.os.TimestampedValue; import org.junit.Test; @@ -63,4 +65,36 @@ public class NetworkTimeSuggestionTest { NetworkTimeSuggestion rtSuggestion = roundTripParcelable(suggestion); assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo()); } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noReferenceTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--unix_epoch_time 12345"); + NetworkTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noUnixEpochTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321"); + NetworkTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test + public void testParseCommandLineArg_validSuggestion() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345"); + TimestampedValue<Long> timeSignal = new TimestampedValue<>(54321L, 12345L); + NetworkTimeSuggestion expectedSuggestion = new NetworkTimeSuggestion(timeSignal); + NetworkTimeSuggestion actualSuggestion = + NetworkTimeSuggestion.parseCommandLineArg(testShellCommand); + assertEquals(expectedSuggestion, actualSuggestion); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_unknownArgument() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345 --bad_arg 0"); + NetworkTimeSuggestion.parseCommandLineArg(testShellCommand); + } } diff --git a/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java index cc7557977e80..bb995a852637 100644 --- a/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java @@ -18,10 +18,12 @@ package android.app.timedetector; import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable; +import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import android.os.ShellCommand; import android.os.TimestampedValue; import org.junit.Test; @@ -95,4 +97,45 @@ public class TelephonyTimeSuggestionTest { assertEquals(suggestion1.getDebugInfo(), rtSuggestion1.getDebugInfo()); } } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noSlotIndex() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345"); + TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noReferenceTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--slot_index 0 --unix_epoch_time 12345"); + TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noUnixEpochTime() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--slot_index 0 --reference_time 54321"); + TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test + public void testParseCommandLineArg_validSuggestion() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--slot_index 0 --reference_time 54321 --unix_epoch_time 12345"); + TelephonyTimeSuggestion expectedSuggestion = + new TelephonyTimeSuggestion.Builder(0) + .setUnixEpochTime(new TimestampedValue<>(54321L, 12345L)) + .build(); + TelephonyTimeSuggestion actualSuggestion = + TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand); + assertEquals(expectedSuggestion, actualSuggestion); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_unknownArgument() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--slot_index 0 --reference_time 54321 --unix_epoch_time 12345 --bad_arg 0"); + TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand); + } } diff --git a/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java b/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java index 8d8290c7bdc9..4efaed11168e 100644 --- a/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java +++ b/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java @@ -26,14 +26,14 @@ import java.util.Arrays; import java.util.List; /** Utility methods related to {@link ShellCommand} objects used in several tests. */ -final class ShellCommandTestSupport { +public final class ShellCommandTestSupport { private ShellCommandTestSupport() {} - static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) { + public static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) { return createShellCommandWithArgsAndOptions(Arrays.asList(argsWithSpaces.split(" "))); } - static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) { + public static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) { ShellCommand command = mock(ShellCommand.class); class ArgProvider { private int mCount; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java index 9ad6f3adbd33..6fff52a20062 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java @@ -206,6 +206,8 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider { putSignatureImpl("NONEwithECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$NONE"); + putSignatureImpl("Ed25519", + PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$Ed25519"); putSignatureImpl("SHA1withECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1"); put("Alg.Alias.Signature.ECDSA", "SHA1withECDSA"); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java index 8289671de212..5216a908826b 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java @@ -29,7 +29,10 @@ import libcore.util.EmptyArray; import java.io.ByteArrayOutputStream; import java.security.InvalidKeyException; import java.security.SignatureSpi; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; import java.util.List; +import java.util.Set; /** * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures. @@ -37,6 +40,10 @@ import java.util.List; * @hide */ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase { + private static final Set<String> ACCEPTED_SIGNING_SCHEMES = Set.of( + KeyProperties.KEY_ALGORITHM_EC.toLowerCase(), + NamedParameterSpec.ED25519.getName().toLowerCase(), + "eddsa"); public final static class NONE extends AndroidKeyStoreECDSASignatureSpi { public NONE() { @@ -114,6 +121,18 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature } } + public static final class Ed25519 extends AndroidKeyStoreECDSASignatureSpi { + public Ed25519() { + // Ed25519 uses an internal digest system. + super(KeymasterDefs.KM_DIGEST_NONE); + } + + @Override + protected String getAlgorithm() { + return NamedParameterSpec.ED25519.getName(); + } + } + public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi { public SHA1() { super(KeymasterDefs.KM_DIGEST_SHA1); @@ -174,9 +193,10 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature @Override protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException { - if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) { + if (!ACCEPTED_SIGNING_SCHEMES.contains(key.getAlgorithm().toLowerCase())) { throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm() - + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported"); + + ". Only" + Arrays.toString(ACCEPTED_SIGNING_SCHEMES.stream().toArray()) + + " supported"); } long keySizeBits = -1; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java new file mode 100644 index 000000000000..4855ad0f7293 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 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.security.keystore2; + +import android.annotation.NonNull; +import android.security.KeyStoreSecurityLevel; +import android.system.keystore2.Authorization; +import android.system.keystore2.KeyDescriptor; + +import java.security.PrivateKey; +import java.security.interfaces.EdECKey; +import java.security.spec.NamedParameterSpec; + +/** + * EdEC private key (instance of {@link PrivateKey} and {@link EdECKey}) backed by keystore. + * + * @hide + */ +public class AndroidKeyStoreEdECPrivateKey extends AndroidKeyStorePrivateKey implements EdECKey { + public AndroidKeyStoreEdECPrivateKey( + @NonNull KeyDescriptor descriptor, long keyId, + @NonNull Authorization[] authorizations, + @NonNull String algorithm, + @NonNull KeyStoreSecurityLevel securityLevel) { + super(descriptor, keyId, authorizations, algorithm, securityLevel); + } + + @Override + public NamedParameterSpec getParams() { + return NamedParameterSpec.ED25519; + } +} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java new file mode 100644 index 000000000000..642e08813291 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2022 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.security.keystore2; + +import android.annotation.NonNull; +import android.security.KeyStoreSecurityLevel; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyMetadata; + +import java.math.BigInteger; +import java.security.interfaces.EdECPublicKey; +import java.security.spec.EdECPoint; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; +import java.util.Objects; + +/** + * {@link EdECPublicKey} backed by keystore. + * + * @hide + */ +public class AndroidKeyStoreEdECPublicKey extends AndroidKeyStorePublicKey + implements EdECPublicKey { + /** + * DER sequence, as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-4 and + * https://datatracker.ietf.org/doc/html/rfc5280#section-4.1. + * SEQUENCE (2 elem) + * SEQUENCE (1 elem) + * OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm) + * as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-3 + * BIT STRING (256 bit) as defined in + * https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2 + */ + private static final byte[] DER_KEY_PREFIX = new byte[] { + 0x30, + 0x2a, + 0x30, + 0x05, + 0x06, + 0x03, + 0x2b, + 0x65, + 0x70, + 0x03, + 0x21, + 0x00, + }; + private static final int ED25519_KEY_SIZE_BYTES = 32; + + private byte[] mEncodedKey; + private EdECPoint mPoint; + + public AndroidKeyStoreEdECPublicKey( + @NonNull KeyDescriptor descriptor, + @NonNull KeyMetadata metadata, + @NonNull String algorithm, + @NonNull KeyStoreSecurityLevel iSecurityLevel, + @NonNull byte[] encodedKey) { + super(descriptor, metadata, encodedKey, algorithm, iSecurityLevel); + mEncodedKey = encodedKey; + + int preambleLength = matchesPreamble(DER_KEY_PREFIX, encodedKey); + if (preambleLength == 0) { + throw new IllegalArgumentException("Key size is not correct size"); + } + + mPoint = pointFromKeyByteArray( + Arrays.copyOfRange(encodedKey, preambleLength, encodedKey.length)); + } + + @Override + AndroidKeyStorePrivateKey getPrivateKey() { + return new AndroidKeyStoreEdECPrivateKey( + getUserKeyDescriptor(), + getKeyIdDescriptor().nspace, + getAuthorizations(), + "EdDSA", + getSecurityLevel()); + } + + @Override + public NamedParameterSpec getParams() { + return NamedParameterSpec.ED25519; + } + + @Override + public EdECPoint getPoint() { + return mPoint; + } + + private static int matchesPreamble(byte[] preamble, byte[] encoded) { + if (encoded.length != (preamble.length + ED25519_KEY_SIZE_BYTES)) { + return 0; + } + if (Arrays.compare(preamble, Arrays.copyOf(encoded, preamble.length)) != 0) { + return 0; + } + return preamble.length; + } + + private static EdECPoint pointFromKeyByteArray(byte[] coordinates) { + Objects.requireNonNull(coordinates); + + // Oddity of the key is the most-significant bit of the last byte. + boolean isOdd = (0x80 & coordinates[coordinates.length - 1]) != 0; + // Zero out the oddity bit. + coordinates[coordinates.length - 1] &= (byte) 0x7f; + // Representation of Y is in little-endian, according to rfc8032 section-3.1. + reverse(coordinates); + // The integer representing Y starts from the first bit in the coordinates array. + BigInteger y = new BigInteger(1, coordinates); + return new EdECPoint(isOdd, y); + } + + private static void reverse(byte[] coordinateArray) { + int start = 0; + int end = coordinateArray.length - 1; + while (start < end) { + byte tmp = coordinateArray[start]; + coordinateArray[start] = coordinateArray[end]; + coordinateArray[end] = tmp; + start++; + end--; + } + } + + @Override + public byte[] getEncoded() { + return mEncodedKey.clone(); + } +} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index d31499e8b36d..0355628b8135 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -224,7 +224,6 @@ public class AndroidKeyStoreProvider extends Provider { String jcaKeyAlgorithm = publicKey.getAlgorithm(); - KeyStoreSecurityLevel securityLevel = iSecurityLevel; if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) { return new AndroidKeyStoreECPublicKey(descriptor, metadata, iSecurityLevel, (ECPublicKey) publicKey); @@ -232,8 +231,9 @@ public class AndroidKeyStoreProvider extends Provider { return new AndroidKeyStoreRSAPublicKey(descriptor, metadata, iSecurityLevel, (RSAPublicKey) publicKey); } else if (ED25519_OID.equalsIgnoreCase(jcaKeyAlgorithm)) { - //TODO(b/214203951) missing classes in conscrypt - throw new ProviderException("Curve " + ED25519_OID + " not supported yet"); + final byte[] publicKeyEncoded = publicKey.getEncoded(); + return new AndroidKeyStoreEdECPublicKey(descriptor, metadata, ED25519_OID, + iSecurityLevel, publicKeyEncoded); } else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) { //TODO(b/214203951) missing classes in conscrypt throw new ProviderException("Curve " + X25519_ALIAS + " not supported yet"); diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java new file mode 100644 index 000000000000..5bd5797859c9 --- /dev/null +++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022 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.security.keystore2; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import android.security.KeyStoreSecurityLevel; +import android.system.keystore2.Authorization; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyMetadata; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.math.BigInteger; +import java.util.Base64; + +@RunWith(AndroidJUnit4.class) +public class AndroidKeyStoreEdECPublicKeyTest { + private static KeyDescriptor descriptor() { + final KeyDescriptor keyDescriptor = new KeyDescriptor(); + keyDescriptor.alias = "key"; + keyDescriptor.blob = null; + keyDescriptor.domain = Domain.APP; + keyDescriptor.nspace = -1; + return keyDescriptor; + } + + private static KeyMetadata metadata(byte[] cert, byte[] certChain) { + KeyMetadata metadata = new KeyMetadata(); + metadata.authorizations = new Authorization[0]; + metadata.certificate = cert; + metadata.certificateChain = certChain; + metadata.key = descriptor(); + metadata.modificationTimeMs = 0; + metadata.keySecurityLevel = 1; + return metadata; + } + + @Mock + private KeyStoreSecurityLevel mKeystoreSecurityLevel; + + private static class EdECTestVector { + public final byte[] encodedKeyBytes; + public final boolean isOdd; + public final BigInteger yValue; + + EdECTestVector(String b64KeyBytes, boolean isOdd, String yValue) { + this.encodedKeyBytes = Base64.getDecoder().decode(b64KeyBytes); + this.isOdd = isOdd; + this.yValue = new BigInteger(yValue); + } + } + + private static final EdECTestVector[] ED_EC_TEST_VECTORS = new EdECTestVector[]{ + new EdECTestVector("MCowBQYDK2VwAyEADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSo=", + false, + "19147682157189290216699341180089409126316261024914226007941553249095116672780" + ), + new EdECTestVector("MCowBQYDK2VwAyEA/0E1IRNzGj85Ot/TPeXqifkqTkdk4voleH0hIq59D9w=", + true, + "41640152188550647350742178040529506688513911269563908889464821205156322689535" + ), + new EdECTestVector("MCowBQYDK2VwAyEAunOvGuenetl9GQSXGVo5L3RIr4OOIpFIv/Zre8qTc/8=", + true, + "57647939198144376128225770417635248407428273266444593100194116168980378907578" + ), + new EdECTestVector("MCowBQYDK2VwAyEA2hHqaZ5IolswN1Yd58Y4hzhmUMCCqc4PW5A/SFLmTX8=", + false, + "57581368614046789120409806291852629847774713088410311752049592044694364885466" + ), + }; + + @Test + public void testParsingOfValidKeys() { + for (EdECTestVector testVector : ED_EC_TEST_VECTORS) { + AndroidKeyStoreEdECPublicKey pkey = new AndroidKeyStoreEdECPublicKey(descriptor(), + metadata(null, null), "EdDSA", mKeystoreSecurityLevel, + testVector.encodedKeyBytes); + + assertEquals(pkey.getPoint().isXOdd(), testVector.isOdd); + assertEquals(pkey.getPoint().getY(), testVector.yValue); + } + } + + @Test + public void testFailedParsingOfKeysWithDifferentOid() { + final byte[] testVectorWithIncorrectOid = Base64.getDecoder().decode( + "MCowBQYDLGVwAyEADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSo="); + assertThrows("OID should be unrecognized", IllegalArgumentException.class, + () -> new AndroidKeyStoreEdECPublicKey(descriptor(), metadata(null, null), "EdDSA", + mKeystoreSecurityLevel, testVectorWithIncorrectOid)); + } + + @Test + public void testFailedParsingOfKeysWithWrongSize() { + final byte[] testVectorWithIncorrectKeySize = Base64.getDecoder().decode( + "MCwwBQYDK2VwAyMADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSrOzg=="); + assertThrows("Key length should be invalid", IllegalArgumentException.class, + () -> new AndroidKeyStoreEdECPublicKey(descriptor(), metadata(null, null), "EdDSA", + mKeystoreSecurityLevel, testVectorWithIncorrectKeySize)); + } +} + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index ff4c2c63f8b4..c4d4261ba080 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -478,6 +478,12 @@ <uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" /> <uses-permission android:name="android.permission.SUGGEST_EXTERNAL_TIME" /> + <!-- Permissions used for manual testing of time detection behavior. --> + <uses-permission android:name="android.permission.SUGGEST_MANUAL_TIME" /> + <uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME" /> + <uses-permission android:name="android.permission.SUGGEST_NETWORK_TIME" /> + <uses-permission android:name="android.permission.SUGGEST_GNSS_TIME" /> + <!-- Permission required for CTS test - android.server.biometrics --> <uses-permission android:name="android.permission.USE_BIOMETRIC" /> diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 9676a57b2df9..91c7a24afaf7 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -376,7 +376,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon Log.w(TAG, String.format("onActiveChanged(%d,%d,%s,%s,%d,%d)", code, uid, packageName, Boolean.toString(active), attributionChainId, attributionFlags)); } - if (attributionChainId != AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE + if (active && attributionChainId != AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE && attributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE && (attributionFlags & AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR) == 0 && (attributionFlags & AppOpsManager.ATTRIBUTION_FLAG_TRUSTED) == 0) { diff --git a/services/core/OWNERS b/services/core/OWNERS deleted file mode 100644 index 88d0b61a2ab6..000000000000 --- a/services/core/OWNERS +++ /dev/null @@ -1 +0,0 @@ -per-file Android.bp = file:platform/build/soong:/OWNERS diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 406ff9b00686..a81699fa82c3 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1202,8 +1202,11 @@ import java.util.concurrent.atomic.AtomicBoolean; break; case MSG_L_SET_BT_ACTIVE_DEVICE: synchronized (mDeviceStateLock) { - mDeviceInventory.onSetBtActiveDevice((BtDeviceInfo) msg.obj, - mAudioService.getBluetoothContextualVolumeStream()); + BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj; + mDeviceInventory.onSetBtActiveDevice(btInfo, + (btInfo.mProfile != BluetoothProfile.LE_AUDIO || btInfo.mIsLeOutput) + ? mAudioService.getBluetoothContextualVolumeStream() + : AudioSystem.STREAM_DEFAULT); } break; case MSG_BT_HEADSET_CNCT_FAILED: diff --git a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java index 28c7cad3b184..44822a3f7429 100644 --- a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java +++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java @@ -18,6 +18,7 @@ package com.android.server.clipboard; import android.annotation.Nullable; import android.content.ClipData; +import android.os.PersistableBundle; import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; @@ -25,6 +26,7 @@ import android.system.OsConstants; import android.system.VmSocketAddress; import android.util.Slog; +import java.io.EOFException; import java.io.FileDescriptor; import java.io.InterruptedIOException; import java.net.SocketException; @@ -58,11 +60,11 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> { return mPipe; } - private synchronized boolean openPipe() { - if (mPipe != null) { - return true; - } + private synchronized void setPipeFD(final FileDescriptor fd) { + mPipe = fd; + } + private static FileDescriptor openPipeImpl() { try { final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0); @@ -70,16 +72,33 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> { Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST)); final byte[] handshake = createOpenHandshake(); - Os.write(fd, handshake, 0, handshake.length); - mPipe = fd; - return true; + writeFully(fd, handshake, 0, handshake.length); + return fd; } catch (ErrnoException | SocketException | InterruptedIOException e) { Os.close(fd); } } catch (ErrnoException e) { } - return false; + return null; + } + + private void openPipe() throws InterruptedException { + FileDescriptor fd = getPipeFD(); + + if (fd == null) { + fd = openPipeImpl(); + + // There's no guarantee that QEMU pipes will be ready at the moment + // this method is invoked. We simply try to get the pipe open and + // retry on failure indefinitely. + while (fd == null) { + Thread.sleep(100); + fd = openPipeImpl(); + } + } + + setPipeFD(fd); } private synchronized void closePipe() { @@ -93,16 +112,16 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> { } } - private byte[] receiveMessage() throws ErrnoException, InterruptedIOException { + private byte[] receiveMessage() throws ErrnoException, InterruptedIOException, EOFException { final byte[] lengthBits = new byte[4]; - Os.read(mPipe, lengthBits, 0, lengthBits.length); + readFully(mPipe, lengthBits, 0, lengthBits.length); final ByteBuffer bb = ByteBuffer.wrap(lengthBits); bb.order(ByteOrder.LITTLE_ENDIAN); final int msgLen = bb.getInt(); final byte[] msg = new byte[msgLen]; - Os.read(mPipe, msg, 0, msg.length); + readFully(mPipe, msg, 0, msg.length); return msg; } @@ -115,20 +134,15 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> { bb.order(ByteOrder.LITTLE_ENDIAN); bb.putInt(msg.length); - Os.write(fd, lengthBits, 0, lengthBits.length); - Os.write(fd, msg, 0, msg.length); + writeFully(fd, lengthBits, 0, lengthBits.length); + writeFully(fd, msg, 0, msg.length); } EmulatorClipboardMonitor(final Consumer<ClipData> setAndroidClipboard) { this.mHostMonitorThread = new Thread(() -> { while (!Thread.interrupted()) { try { - // There's no guarantee that QEMU pipes will be ready at the moment - // this method is invoked. We simply try to get the pipe open and - // retry on failure indefinitely. - while (!openPipe()) { - Thread.sleep(100); - } + openPipe(); final byte[] receivedData = receiveMessage(); @@ -136,12 +150,15 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> { final ClipData clip = new ClipData("host clipboard", new String[]{"text/plain"}, new ClipData.Item(str)); + final PersistableBundle bundle = new PersistableBundle(); + bundle.putBoolean("com.android.systemui.SUPPRESS_CLIPBOARD_OVERLAY", true); + clip.getDescription().setExtras(bundle); if (LOG_CLIBOARD_ACCESS) { Slog.i(TAG, "Setting the guest clipboard to '" + str + "'"); } setAndroidClipboard.accept(clip); - } catch (ErrnoException | InterruptedIOException e) { + } catch (ErrnoException | EOFException | InterruptedIOException e) { closePipe(); } catch (InterruptedException | IllegalArgumentException e) { } @@ -182,4 +199,32 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> { t.start(); } } + + private static void readFully(final FileDescriptor fd, + final byte[] buf, int offset, int size) + throws ErrnoException, InterruptedIOException, EOFException { + while (size > 0) { + final int r = Os.read(fd, buf, offset, size); + if (r > 0) { + offset += r; + size -= r; + } else { + throw new EOFException(); + } + } + } + + private static void writeFully(final FileDescriptor fd, + final byte[] buf, int offset, int size) + throws ErrnoException, InterruptedIOException { + while (size > 0) { + final int r = Os.write(fd, buf, offset, size); + if (r > 0) { + offset += r; + size -= r; + } else { + throw new ErrnoException("write", OsConstants.EIO); + } + } + } } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index d07f151f25bd..18efe5375942 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1621,11 +1621,17 @@ public class Vpn { } // Note: Return type guarantees results are deduped and sorted, which callers require. + // This method also adds the SDK sandbox UIDs corresponding to the applications by default, + // since apps are generally not aware of them, yet they should follow the VPN configuration + // of the app they belong to. private SortedSet<Integer> getAppsUids(List<String> packageNames, int userId) { SortedSet<Integer> uids = new TreeSet<>(); for (String app : packageNames) { int uid = getAppUid(app, userId); if (uid != -1) uids.add(uid); + if (Process.isApplicationUid(uid)) { + uids.add(Process.toSdkSandboxUid(uid)); + } } return uids; } diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 2c2a2bf24cfd..7d8a22a9b563 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -505,12 +505,36 @@ public class BrightnessTracker { } } + // Return the path to the given file, either the new path + // /data/system/$filename, or the old path /data/system_de/$filename if the + // file exists there but not at the new path. Only use this for EVENTS_FILE + // and AMBIENT_BRIGHTNESS_STATS_FILE. + // + // Explanation: this service previously incorrectly stored these two files + // directly in /data/system_de, instead of in /data/system where they should + // have been. As system_server no longer has write access to + // /data/system_de itself, these files were moved to /data/system. To + // lazily migrate the files, we simply read from the old path if it exists + // and the new one doesn't, and always write to the new path. Note that + // system_server doesn't have permission to delete the old files. + private AtomicFile getFileWithLegacyFallback(String filename) { + AtomicFile file = mInjector.getFile(filename); + if (file != null && !file.exists()) { + AtomicFile legacyFile = mInjector.getLegacyFile(filename); + if (legacyFile != null && legacyFile.exists()) { + Slog.i(TAG, "Reading " + filename + " from old location"); + return legacyFile; + } + } + return file; + } + private void readEvents() { synchronized (mEventsLock) { // Read might prune events so mark as dirty. mEventsDirty = true; mEvents.clear(); - final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE); + final AtomicFile readFrom = getFileWithLegacyFallback(EVENTS_FILE); if (readFrom != null && readFrom.exists()) { FileInputStream input = null; try { @@ -528,7 +552,7 @@ public class BrightnessTracker { private void readAmbientBrightnessStats() { mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null); - final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE); + final AtomicFile readFrom = getFileWithLegacyFallback(AMBIENT_BRIGHTNESS_STATS_FILE); if (readFrom != null && readFrom.exists()) { FileInputStream input = null; try { @@ -1095,6 +1119,10 @@ public class BrightnessTracker { } public AtomicFile getFile(String filename) { + return new AtomicFile(new File(Environment.getDataSystemDirectory(), filename)); + } + + public AtomicFile getLegacyFile(String filename) { return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename)); } diff --git a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java index 7649958fe6c9..9d263516db3a 100644 --- a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java +++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java @@ -22,6 +22,7 @@ import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; +import android.os.Build; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; @@ -166,6 +167,11 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { mWakeLock.release(); } + @Override + public boolean deviceHasY2038Issue() { + return Build.SUPPORTED_32_BIT_ABIS.length > 0; + } + private void checkWakeLockHeld() { if (!mWakeLock.isHeld()) { Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " not held"); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java index 721986bc6e93..cc5e6fe433fa 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java @@ -17,14 +17,26 @@ package com.android.server.timedetector; import static android.app.timedetector.TimeDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED; import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SERVICE_NAME; +import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_EXTERNAL_TIME; +import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_GNSS_TIME; +import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_MANUAL_TIME; +import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_NETWORK_TIME; +import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_TELEPHONY_TIME; import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME; import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE; import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE; +import android.app.time.ExternalTimeSuggestion; +import android.app.timedetector.GnssTimeSuggestion; +import android.app.timedetector.ManualTimeSuggestion; +import android.app.timedetector.NetworkTimeSuggestion; +import android.app.timedetector.TelephonyTimeSuggestion; import android.os.ShellCommand; import java.io.PrintWriter; +import java.util.function.Consumer; +import java.util.function.Supplier; /** Implements the shell command interface for {@link TimeDetectorService}. */ class TimeDetectorShellCommand extends ShellCommand { @@ -44,6 +56,16 @@ class TimeDetectorShellCommand extends ShellCommand { switch (cmd) { case SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED: return runIsAutoDetectionEnabled(); + case SHELL_COMMAND_SUGGEST_MANUAL_TIME: + return runSuggestManualTime(); + case SHELL_COMMAND_SUGGEST_TELEPHONY_TIME: + return runSuggestTelephonyTime(); + case SHELL_COMMAND_SUGGEST_NETWORK_TIME: + return runSuggestNetworkTime(); + case SHELL_COMMAND_SUGGEST_GNSS_TIME: + return runSuggestGnssTime(); + case SHELL_COMMAND_SUGGEST_EXTERNAL_TIME: + return runSuggestExternalTime(); default: { return handleDefaultCommands(cmd); } @@ -59,6 +81,53 @@ class TimeDetectorShellCommand extends ShellCommand { return 0; } + private int runSuggestManualTime() { + return runSuggestTime( + () -> ManualTimeSuggestion.parseCommandLineArg(this), + mInterface::suggestManualTime); + } + + private int runSuggestTelephonyTime() { + return runSuggestTime( + () -> TelephonyTimeSuggestion.parseCommandLineArg(this), + mInterface::suggestTelephonyTime); + } + + private int runSuggestNetworkTime() { + return runSuggestTime( + () -> NetworkTimeSuggestion.parseCommandLineArg(this), + mInterface::suggestNetworkTime); + } + + private int runSuggestGnssTime() { + return runSuggestTime( + () -> GnssTimeSuggestion.parseCommandLineArg(this), + mInterface::suggestGnssTime); + } + + private int runSuggestExternalTime() { + return runSuggestTime( + () -> ExternalTimeSuggestion.parseCommandLineArg(this), + mInterface::suggestExternalTime); + } + + private <T> int runSuggestTime(Supplier<T> suggestionParser, Consumer<T> invoker) { + final PrintWriter pw = getOutPrintWriter(); + try { + T suggestion = suggestionParser.get(); + if (suggestion == null) { + pw.println("Error: suggestion not specified"); + return 1; + } + invoker.accept(suggestion); + pw.println("Suggestion " + suggestion + " injected."); + return 0; + } catch (RuntimeException e) { + pw.println(e); + return 1; + } + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -68,6 +137,22 @@ class TimeDetectorShellCommand extends ShellCommand { pw.printf(" %s\n", SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED); pw.printf(" Prints true/false according to the automatic time detection setting.\n"); pw.println(); + pw.printf(" %s <manual suggestion opts>\n", SHELL_COMMAND_SUGGEST_MANUAL_TIME); + pw.printf(" %s <telephony suggestion opts>\n", SHELL_COMMAND_SUGGEST_TELEPHONY_TIME); + pw.printf(" %s <network suggestion opts>\n", SHELL_COMMAND_SUGGEST_NETWORK_TIME); + pw.printf(" %s <gnss suggestion opts>\n", SHELL_COMMAND_SUGGEST_GNSS_TIME); + pw.printf(" %s <external suggestion opts>\n", SHELL_COMMAND_SUGGEST_EXTERNAL_TIME); + pw.println(); + ManualTimeSuggestion.printCommandLineOpts(pw); + pw.println(); + TelephonyTimeSuggestion.printCommandLineOpts(pw); + pw.println(); + NetworkTimeSuggestion.printCommandLineOpts(pw); + pw.println(); + GnssTimeSuggestion.printCommandLineOpts(pw); + pw.println(); + ExternalTimeSuggestion.printCommandLineOpts(pw); + pw.println(); pw.printf("This service is also affected by the following device_config flags in the" + " %s namespace:\n", NAMESPACE_SYSTEM_TIME); pw.printf(" %s\n", KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index ae4d46c387b9..33ab1047c763 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -85,6 +85,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { */ private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10; + /** The value in Unix epoch milliseconds of the Y2038 issue. */ + private static final long Y2038_LIMIT_IN_MILLIS = 1000L * Integer.MAX_VALUE; + /** * A log that records the decisions / decision metadata that affected the device's system clock * time. This is logged in bug reports to assist with debugging issues with detection. @@ -185,6 +188,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */ void releaseWakeLock(); + + /** + * Returns {@code true} if the device may be at risk of time_t overflow (because bionic + * defines time_t as a 32-bit signed integer for 32-bit processes). + */ + boolean deviceHasY2038Issue(); } static TimeDetectorStrategy create( @@ -333,6 +342,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { .mapToObj(TimeDetectorStrategy::originToString) .collect(joining(",", "[", "]")); ipw.println("mEnvironment.autoOriginPriorities()=" + priorities); + ipw.println("mEnvironment.deviceHasY2038Issue()=" + mEnvironment.deviceHasY2038Issue()); ipw.println("Time change log:"); ipw.increaseIndent(); // level 2 @@ -413,6 +423,16 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { + ", suggestion=" + suggestion); return false; } + + if (newUnixEpochTime.getValue() > Y2038_LIMIT_IN_MILLIS + && mEnvironment.deviceHasY2038Issue()) { + // This check won't prevent a device's system clock exceeding Integer.MAX_VALUE Unix + // seconds through the normal passage of time, but it will stop it jumping above 2038 + // because of a "bad" suggestion. b/204193177 + Slog.w(LOG_TAG, "Suggested value is above max time supported by this device." + + " suggestion=" + suggestion); + return false; + } return true; } diff --git a/services/proguard.flags b/services/proguard.flags index 425da6c11177..bad02b47031c 100644 --- a/services/proguard.flags +++ b/services/proguard.flags @@ -33,6 +33,11 @@ public <init>(...); } +# Accessed from com.android.compos APEX +-keep,allowoptimization,allowaccessmodification class com.android.internal.art.ArtStatsLog { + public static void write(...); +} + # Binder interfaces -keep,allowoptimization,allowaccessmodification class * extends android.os.IInterface -keep,allowoptimization,allowaccessmodification class * extends android.os.IHwInterface diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp index 0a033380e6c9..2617f4d09ee7 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp @@ -25,6 +25,7 @@ package { android_test_helper_app { name: "PackageManagerServiceDeviceSideTests", + sdk_version: "test_current", srcs: ["src/**/*.kt"], libs: [ "android.test.base", @@ -37,5 +38,4 @@ android_test_helper_app { "androidx.test.rules", "truth-prebuilt", ], - platform_apis: true, } diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java index bdf94f3a2882..3151552fc4c9 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java @@ -1037,6 +1037,12 @@ public class BrightnessTrackerTest { } @Override + public AtomicFile getLegacyFile(String filename) { + // Don't have the test write / read from anywhere. + return null; + } + + @Override public long currentTimeMillis() { return mCurrentTimeMillis; } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index a227cd3c6f5c..035249e32d74 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -70,6 +70,7 @@ import com.google.common.collect.ImmutableMap; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -516,6 +517,7 @@ public class RecoverableKeyStoreManagerTest { } } + @Ignore("Causing breakages so ignoring to resolve, b/231667368") @Test public void initRecoveryService_alwaysUpdatesCertsWhenTestRootCertIsUsed() throws Exception { int uid = Binder.getCallingUid(); @@ -539,6 +541,7 @@ public class RecoverableKeyStoreManagerTest { testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint2()); } + @Ignore("Causing breakages so ignoring to resolve, b/231667368") @Test public void initRecoveryService_updatesCertsIndependentlyForDifferentRoots() throws Exception { int uid = Binder.getCallingUid(); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index 2d9903f9cf60..2248ddb9e69a 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -1131,6 +1131,49 @@ public class TimeDetectorStrategyImplTest { .verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli()); } + @Test + public void manualY2038SuggestionsAreRejectedOnAffectedDevices() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(false) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY) + .pokeDeviceHasY2038Issues(true); + + Instant y2038IssueTime = Instant.ofEpochMilli((1L + Integer.MAX_VALUE) * 1000L); + ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(y2038IssueTime); + mScript.simulateManualTimeSuggestion(timeSuggestion, false /* expectedResult */) + .verifySystemClockWasNotSetAndResetCallTracking(); + } + + @Test + public void telephonyY2038SuggestionsAreRejectedOnAffectedDevices() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY) + .pokeDeviceHasY2038Issues(true); + + final int slotIndex = 0; + Instant y2038IssueTime = Instant.ofEpochMilli((1L + Integer.MAX_VALUE) * 1000L); + TelephonyTimeSuggestion timeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex, y2038IssueTime); + mScript.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockWasNotSetAndResetCallTracking(); + } + + @Test + public void telephonyY2038SuggestionsAreNotRejectedOnUnaffectedDevices() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY) + .pokeDeviceHasY2038Issues(false); + + final int slotIndex = 0; + Instant y2038IssueTime = Instant.ofEpochMilli((1L + Integer.MAX_VALUE) * 1000L); + TelephonyTimeSuggestion timeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex, y2038IssueTime); + mScript.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockWasSetAndResetCallTracking(y2038IssueTime.toEpochMilli()); + } + /** * A fake implementation of {@link TimeDetectorStrategyImpl.Environment}. Besides tracking * changes and behaving like the real thing should, it also asserts preconditions. @@ -1143,6 +1186,7 @@ public class TimeDetectorStrategyImplTest { private int mSystemClockUpdateThresholdMillis = 2000; private int[] mAutoOriginPriorities = PROVIDERS_PRIORITY; private ConfigurationChangeListener mConfigChangeListener; + private boolean mDeviceHas2038Issues = false; // Tracking operations. private boolean mSystemClockWasSet; @@ -1208,6 +1252,15 @@ public class TimeDetectorStrategyImplTest { mWakeLockAcquired = false; } + public void setDeviceHas2038Issues(boolean hasIssues) { + mDeviceHas2038Issues = hasIssues; + } + + @Override + public boolean deviceHasY2038Issue() { + return mDeviceHas2038Issues; + } + // Methods below are for managing the fake's behavior. void pokeSystemClockUpdateThreshold(int thresholdMillis) { @@ -1304,6 +1357,11 @@ public class TimeDetectorStrategyImplTest { return this; } + Script pokeDeviceHasY2038Issues(boolean hasIssues) { + mFakeEnvironment.setDeviceHas2038Issues(hasIssues); + return this; + } + long peekElapsedRealtimeMillis() { return mFakeEnvironment.peekElapsedRealtimeMillis(); } diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java index f47cf3384791..e7d95e4f53b3 100644 --- a/telephony/java/android/telephony/AnomalyReporter.java +++ b/telephony/java/android/telephony/AnomalyReporter.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; + import static com.android.internal.telephony.TelephonyStatsLog.TELEPHONY_ANOMALY_DETECTED; import android.annotation.NonNull; @@ -73,6 +75,7 @@ public final class AnomalyReporter { * * This method sends the {@link TelephonyManager#ACTION_ANOMALY_REPORTED} broadcast, which is * system protected. Invoking this method unless you are the system will result in an error. + * Carrier Id will be set as UNKNOWN_CARRIER_ID. * * @param eventId a fixed event ID that will be sent for each instance of the same event. This * ID should be generated randomly. @@ -81,6 +84,23 @@ public final class AnomalyReporter { * static and must not contain any sensitive information (especially PII). */ public static void reportAnomaly(@NonNull UUID eventId, String description) { + reportAnomaly(eventId, description, UNKNOWN_CARRIER_ID); + } + + /** + * If enabled, build and send an intent to a Debug Service for logging. + * + * This method sends the {@link TelephonyManager#ACTION_ANOMALY_REPORTED} broadcast, which is + * system protected. Invoking this method unless you are the system will result in an error. + * + * @param eventId a fixed event ID that will be sent for each instance of the same event. This + * ID should be generated randomly. + * @param description an optional description, that if included will be used as the subject for + * identification and discussion of this event. This description should ideally be + * static and must not contain any sensitive information (especially PII). + * @param carrierId the carrier of the id associated with this event. + */ + public static void reportAnomaly(@NonNull UUID eventId, String description, int carrierId) { if (sContext == null) { Rlog.w(TAG, "AnomalyReporter not yet initialized, dropping event=" + eventId); return; @@ -88,7 +108,7 @@ public final class AnomalyReporter { TelephonyStatsLog.write( TELEPHONY_ANOMALY_DETECTED, - 0, // TODO: carrier id needs to be populated + carrierId, eventId.getLeastSignificantBits(), eventId.getMostSignificantBits()); diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index 3a3b3632dc9b..ac1f376cf6b5 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -1661,8 +1661,8 @@ public final class DataFailCause { /** @hide */ public static String toString(@DataFailureCause int dataFailCause) { - int cause = getFailCause(dataFailCause); - return (cause == UNKNOWN) ? "UNKNOWN(" + dataFailCause + ")" : sFailCauseMap.get(cause); + return sFailCauseMap.getOrDefault(dataFailCause, "UNKNOWN") + "(0x" + + Integer.toHexString(dataFailCause) + ")"; } /** @hide */ @@ -1673,4 +1673,9 @@ public final class DataFailCause { return UNKNOWN; } } + + /** @hide */ + public static boolean isFailCauseExisting(@DataFailureCause int failCause) { + return sFailCauseMap.containsKey(failCause); + } } diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index d91134e33ef3..d978f5701eca 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -23,16 +23,12 @@ import android.os.Parcel; import android.os.Parcelable; import android.telephony.Annotation.NetworkType; -import com.android.telephony.Rlog; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; public final class PhysicalChannelConfig implements Parcelable { - static final String TAG = "PhysicalChannelConfig"; - // TODO(b/72993578) consolidate these enums in a central location. /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -571,21 +567,19 @@ public final class PhysicalChannelConfig implements Parcelable { public @NonNull Builder setNetworkType(@NetworkType int networkType) { if (!TelephonyManager.isNetworkTypeValid(networkType)) { - Rlog.e(TAG, "Builder.setNetworkType: Network type " + networkType + " is invalid."); - } else { - mNetworkType = networkType; + throw new IllegalArgumentException("Network type " + networkType + " is invalid."); } + mNetworkType = networkType; return this; } public @NonNull Builder setFrequencyRange(int frequencyRange) { if (!ServiceState.isFrequencyRangeValid(frequencyRange) && frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) { - Rlog.e(TAG, "Builder.setFrequencyRange: Frequency range " + frequencyRange + throw new IllegalArgumentException("Frequency range " + frequencyRange + " is invalid."); - } else { - mFrequencyRange = frequencyRange; } + mFrequencyRange = frequencyRange; return this; } @@ -601,21 +595,19 @@ public final class PhysicalChannelConfig implements Parcelable { public @NonNull Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) { if (cellBandwidthDownlinkKhz < CELL_BANDWIDTH_UNKNOWN) { - Rlog.e(TAG, "Builder.setCellBandwidthDownlinkKhz: Cell downlink bandwidth(kHz) " + throw new IllegalArgumentException("Cell downlink bandwidth(kHz) " + cellBandwidthDownlinkKhz + " is invalid."); - } else { - mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; } + mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; return this; } public @NonNull Builder setCellBandwidthUplinkKhz(int cellBandwidthUplinkKhz) { if (cellBandwidthUplinkKhz < CELL_BANDWIDTH_UNKNOWN) { - Rlog.e(TAG, "Builder.setCellBandwidthUplinkKhz: Cell uplink bandwidth(kHz) " + throw new IllegalArgumentException("Cell uplink bandwidth(kHz) " + cellBandwidthUplinkKhz + " is invalid."); - } else { - mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz; } + mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz; return this; } @@ -632,20 +624,18 @@ public final class PhysicalChannelConfig implements Parcelable { public @NonNull Builder setPhysicalCellId(int physicalCellId) { if (physicalCellId > PHYSICAL_CELL_ID_MAXIMUM_VALUE) { - Rlog.e(TAG, "Builder.setPhysicalCellId: Physical cell ID " + physicalCellId + throw new IllegalArgumentException("Physical cell ID " + physicalCellId + " is over limit."); - } else { - mPhysicalCellId = physicalCellId; } + mPhysicalCellId = physicalCellId; return this; } public @NonNull Builder setBand(int band) { if (band <= BAND_UNKNOWN) { - Rlog.e(TAG, "Builder.setBand: Band " + band + " is invalid."); - } else { - mBand = band; + throw new IllegalArgumentException("Band " + band + " is invalid."); } + mBand = band; return this; } } diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index b0ddf2c28dff..a710e38538ce 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1290,8 +1290,8 @@ public class ApnSetting implements Parcelable { && Objects.equals(this.mOperatorNumeric, other.mOperatorNumeric) && Objects.equals(this.mProtocol, other.mProtocol) && Objects.equals(this.mRoamingProtocol, other.mRoamingProtocol) - && xorEqualsInt(this.mMtuV4, other.mMtuV4) - && xorEqualsInt(this.mMtuV6, other.mMtuV6) + && mtuUnsetOrEquals(this.mMtuV4, other.mMtuV4) + && mtuUnsetOrEquals(this.mMtuV6, other.mMtuV6) && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled) && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask) && Objects.equals(this.mLingeringNetworkTypeBitmask, @@ -1319,7 +1319,12 @@ public class ApnSetting implements Parcelable { // Equal or one is not specified. private boolean xorEqualsInt(int first, int second) { return first == UNSPECIFIED_INT || second == UNSPECIFIED_INT - || Objects.equals(first, second); + || first == second; + } + + // Equal or one is not specified. Specific to MTU where <= 0 indicates unset. + private boolean mtuUnsetOrEquals(int first, int second) { + return first <= 0 || second <= 0 || first == second; } private String nullToEmpty(String stringValue) { diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index ef02589abaf8..4afaf537f7c3 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -428,7 +428,7 @@ public final class DataCallResponse implements Parcelable { public String toString() { StringBuffer sb = new StringBuffer(); sb.append("DataCallResponse: {") - .append(" cause=").append(mCause) + .append(" cause=").append(DataFailCause.toString(mCause)) .append(" retry=").append(mSuggestedRetryTime) .append(" cid=").append(mId) .append(" linkStatus=").append(mLinkStatus) diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml index 55704eda905e..3c8e1ed99604 100644 --- a/tests/ApkVerityTest/AndroidTest.xml +++ b/tests/ApkVerityTest/AndroidTest.xml @@ -35,6 +35,8 @@ <option name="push" value="ApkVerityTestCert.der->/data/local/tmp/ApkVerityTestCert.der" /> </target_preparer> + <!-- Skip on HWASan. TODO(b/232288278): Re-enable --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.SkipHWASanModuleController" /> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > <option name="jar" value="ApkVerityTest.jar" /> </test> |