Merge "make CallerInfo as SystemAPI"
diff --git a/Android.bp b/Android.bp
index 77f024b..a913324 100644
--- a/Android.bp
+++ b/Android.bp
@@ -194,6 +194,9 @@
         ":PacProcessor-aidl-sources",
         ":ProxyHandler-aidl-sources",
 
+        // AIDL from frameworks/base/native/
+        ":platform-compat-native-aidl",
+
         // AIDL sources from external directories
         ":dumpstate_aidl",
         ":framework_native_aidl",
@@ -508,6 +511,12 @@
     ],
 }
 
+filegroup {
+    name: "framework-tethering-shared-srcs",
+    srcs: [
+        "core/java/android/util/LocalLog.java",
+    ],
+}
 // Build ext.jar
 // ============================================================
 java_library {
@@ -1019,6 +1028,7 @@
         "core/res/AndroidManifest.xml",
     ],
     args: metalava_framework_docs_args,
+    write_sdk_values: true,
 }
 
 droidstubs {
@@ -1028,6 +1038,7 @@
         "core/res/AndroidManifest.xml",
     ],
     args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi ",
+    write_sdk_values: true,
 }
 
 droiddoc {
@@ -1051,7 +1062,6 @@
     ],
     proofread_file: "offline-sdk-docs-proofrerad.txt",
     args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"",
-    write_sdk_values: true,
     static_doc_index_redirect: "docs/docs-preview-index.html",
 }
 
@@ -1069,7 +1079,6 @@
     ],
     proofread_file: "offline-sdk-referenceonly-docs-proofrerad.txt",
     args: framework_docs_only_args + " -offlinemode -title \"Android SDK\" -referenceonly",
-    write_sdk_values: true,
     static_doc_index_redirect: "docs/docs-documentation-redirect.html",
     static_doc_properties: "docs/source.properties",
 }
@@ -1089,7 +1098,6 @@
     proofread_file: "offline-system-sdk-referenceonly-docs-proofrerad.txt",
     args: framework_docs_only_args + " -hide 101 -hide 104 -hide 108" +
     " -offlinemode -title \"Android System SDK\" -referenceonly",
-    write_sdk_values: true,
     static_doc_index_redirect: "docs/docs-documentation-redirect.html",
     static_doc_properties: "docs/source.properties",
 }
@@ -1531,3 +1539,15 @@
         targets: ["droidcore"],
     },
 }
+
+// Avoid including Parcelable classes as we don't want to have two copies of
+// Parcelable cross the process.
+filegroup {
+    name: "framework-cellbroadcast-shared-srcs",
+    srcs: [
+        "core/java/android/util/LocalLog.java",
+        "core/java/android/util/Slog.java",
+        "core/java/com/android/internal/util/State.java",
+        "core/java/com/android/internal/util/StateMachine.java",
+    ],
+}
diff --git a/Android.mk b/Android.mk
index 9bda2dc..815a169 100644
--- a/Android.mk
+++ b/Android.mk
@@ -54,6 +54,24 @@
 .PHONY: docs offline-sdk-docs
 docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp
 
+SDK_METADATA_DIR :=$= $(call intermediates-dir-for,PACKAGING,framework-doc-stubs-metadata,,COMMON)
+SDK_METADATA_FILES :=$= $(addprefix $(SDK_METADATA_DIR)/,\
+    activity_actions.txt \
+    broadcast_actions.txt \
+    categories.txt \
+    features.txt \
+    service_actions.txt \
+    widgets.txt)
+SDK_METADATA :=$= $(firstword $(SDK_METADATA_FILES))
+$(SDK_METADATA): .KATI_IMPLICIT_OUTPUTS := $(filter-out $(SDK_METADATA),$(SDK_METADATA_FILES))
+$(SDK_METADATA): $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/framework-doc-stubs-metadata.zip
+	rm -rf $(SDK_METADATA_DIR)
+	mkdir -p $(SDK_METADATA_DIR)
+	unzip -qo $< -d $(SDK_METADATA_DIR)
+
+.PHONY: framework-doc-stubs
+framework-doc-stubs: $(SDK_METADATA)
+
 # Run this for checkbuild
 checkbuild: doc-comment-check-docs
 
diff --git a/api/current.txt b/api/current.txt
index 402fe89..0c60fc1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -45055,6 +45055,7 @@
     method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
     method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
     method public boolean setLine1NumberForDisplay(String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(String, boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2ecddd8..a9fd0cd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5157,6 +5157,7 @@
     field public static final String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
     field public static final String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
     field public static final String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
+    field public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
     field public static final String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
     field public static final String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
     field public static final String ACTION_UPDATE_NETWORK_WATCHLIST = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 1572114..f476fcf 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -546,16 +546,16 @@
             ;
             android_log_list_element elem;
 
-            lastTimestamp.tv_sec = msg.entry_v1.sec;
-            lastTimestamp.tv_nsec = msg.entry_v1.nsec;
+            lastTimestamp.tv_sec = msg.entry.sec;
+            lastTimestamp.tv_nsec = msg.entry.nsec;
 
             // format a BinaryLogEntry
             uint64_t token = proto.start(LogProto::BINARY_LOGS);
-            proto.write(BinaryLogEntry::SEC, msg.entry_v1.sec);
-            proto.write(BinaryLogEntry::NANOSEC, msg.entry_v1.nsec);
-            proto.write(BinaryLogEntry::UID, (int)msg.entry_v4.uid);
-            proto.write(BinaryLogEntry::PID, msg.entry_v1.pid);
-            proto.write(BinaryLogEntry::TID, msg.entry_v1.tid);
+            proto.write(BinaryLogEntry::SEC, (int32_t)msg.entry.sec);
+            proto.write(BinaryLogEntry::NANOSEC, (int32_t)msg.entry.nsec);
+            proto.write(BinaryLogEntry::UID, (int)msg.entry.uid);
+            proto.write(BinaryLogEntry::PID, msg.entry.pid);
+            proto.write(BinaryLogEntry::TID, (int32_t)msg.entry.tid);
             proto.write(BinaryLogEntry::TAG_INDEX,
                         get4LE(reinterpret_cast<uint8_t const*>(msg.msg())));
             do {
@@ -603,7 +603,7 @@
             }
         } else {
             AndroidLogEntry entry;
-            err = android_log_processLogBuffer(&msg.entry_v1, &entry);
+            err = android_log_processLogBuffer(&msg.entry, &entry);
             if (err != NO_ERROR) {
                 ALOGW("[%s] fails to process to an entry.\n", this->name.string());
                 break;
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index 43addc2..2603469 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -54,9 +54,9 @@
     write4Bytes(99 /* a value to log*/, &buffer);
     buffer.push_back(EVENT_TYPE_LIST_STOP);
 
-    msg->entry_v1.len = buffer.size();
+    msg->entry.len = buffer.size();
     msg->entry.hdr_size = kLogMsgHeaderSize;
-    msg->entry_v1.sec = time(nullptr);
+    msg->entry.sec = time(nullptr);
     std::copy(buffer.begin(), buffer.end(), msg->buf + kLogMsgHeaderSize);
 }
 
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 0ade531..838561e 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -38,8 +38,8 @@
 LogEvent::LogEvent(log_msg& msg) {
     mContext =
             create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
-    mLogdTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
-    mLogUid = msg.entry_v4.uid;
+    mLogdTimestampNs = msg.entry.sec * NS_PER_SEC + msg.entry.nsec;
+    mLogUid = msg.entry.uid;
     init(mContext);
     if (mContext) {
         // android_log_destroy will set mContext to NULL
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
index a454df5..204d71d 100644
--- a/core/java/android/annotation/UnsupportedAppUsage.java
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -83,8 +83,9 @@
      * <p>Possible values are:
      * <ul>
      *     <li>
-     *         {@link android.os.Build.VERSION_CODES#O} or {@link android.os.Build.VERSION_CODES#P},
-     *         to limit access to apps targeting these SDKs (or earlier).
+     *         An API level like {@link android.os.Build.VERSION_CODES#O} - in which case the API is
+     *         available up to and including the specified release. Or, in other words, the API is
+     *         blacklisted (unavailable) from the next API level from the one specified.
      *     </li>
      *     <li>
      *         absent (default value) - All apps can access this API, but doing so may result in
@@ -94,10 +95,6 @@
      *
      * </ul>
      *
-     * Note, if this is set to {@link android.os.Build.VERSION_CODES#O}, apps targeting O
-     * maintenance releases will also be allowed to use the API, and similarly for any future
-     * maintenance releases of P.
-     *
      * @return The maximum value for an apps targetSdkVersion in order to access this API.
      */
     int maxTargetSdk() default Integer.MAX_VALUE;
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index f624446..ddc4932 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -16,10 +16,10 @@
 
 package android.app.timedetector;
 
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
 
 /**
- * System private API to comunicate with time detector service.
+ * System private API to communicate with time detector service.
  *
  * <p>Used by parts of the Android system with signals associated with the device's time to provide
  * information to the Time Detector Service.
@@ -32,5 +32,5 @@
  * {@hide}
  */
 interface ITimeDetectorService {
-  void suggestTime(in TimeSignal timeSignal);
+  void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
 }
diff --git a/core/java/android/app/timedetector/TimeSignal.aidl b/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl
similarity index 94%
rename from core/java/android/app/timedetector/TimeSignal.aidl
rename to core/java/android/app/timedetector/PhoneTimeSuggestion.aidl
index d2ec357..f5e2405 100644
--- a/core/java/android/app/timedetector/TimeSignal.aidl
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl
@@ -16,4 +16,4 @@
 
 package android.app.timedetector;
 
-parcelable TimeSignal;
\ No newline at end of file
+parcelable PhoneTimeSuggestion;
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
new file mode 100644
index 0000000..475a4aa
--- /dev/null
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2019 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.Parcelable;
+import android.util.TimestampedValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A time signal from a telephony source. The value consists of the number of milliseconds elapsed
+ * since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime clock when that number
+ * was established. The elapsed realtime clock is considered accurate but volatile, so time signals
+ * must not be persisted across device resets.
+ *
+ * @hide
+ */
+public final class PhoneTimeSuggestion implements Parcelable {
+
+    public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR =
+            new Parcelable.Creator<PhoneTimeSuggestion>() {
+                public PhoneTimeSuggestion createFromParcel(Parcel in) {
+                    return PhoneTimeSuggestion.createFromParcel(in);
+                }
+
+                public PhoneTimeSuggestion[] newArray(int size) {
+                    return new PhoneTimeSuggestion[size];
+                }
+            };
+
+    private final int mPhoneId;
+    @NonNull
+    private final TimestampedValue<Long> mUtcTime;
+    @Nullable
+    private ArrayList<String> mDebugInfo;
+
+    public PhoneTimeSuggestion(int phoneId, @NonNull TimestampedValue<Long> utcTime) {
+        mPhoneId = phoneId;
+        mUtcTime = Objects.requireNonNull(utcTime);
+    }
+
+    private static PhoneTimeSuggestion createFromParcel(Parcel in) {
+        int phoneId = in.readInt();
+        TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+        PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion(phoneId, utcTime);
+        @SuppressWarnings("unchecked")
+        ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+        suggestion.mDebugInfo = debugInfo;
+        return suggestion;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPhoneId);
+        dest.writeParcelable(mUtcTime, 0);
+        dest.writeList(mDebugInfo);
+    }
+
+    public int getPhoneId() {
+        return mPhoneId;
+    }
+
+    @NonNull
+    public TimestampedValue<Long> getUtcTime() {
+        return mUtcTime;
+    }
+
+    @NonNull
+    public List<String> getDebugInfo() {
+        return Collections.unmodifiableList(mDebugInfo);
+    }
+
+    /**
+     * 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) {
+        if (mDebugInfo == null) {
+            mDebugInfo = new ArrayList<>();
+        }
+        mDebugInfo.addAll(Arrays.asList(debugInfos));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PhoneTimeSuggestion that = (PhoneTimeSuggestion) o;
+        return mPhoneId == that.mPhoneId
+                && Objects.equals(mUtcTime, that.mUtcTime);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPhoneId, mUtcTime);
+    }
+
+    @Override
+    public String toString() {
+        return "PhoneTimeSuggestion{"
+                + "mPhoneId='" + mPhoneId + '\''
+                + ", mUtcTime=" + mUtcTime
+                + ", mDebugInfo=" + mDebugInfo
+                + '}';
+    }
+}
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 052050d..334e958 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -45,12 +45,12 @@
      * signals are available such as those that come from more reliable sources or were
      * determined more recently.
      */
-    public void suggestTime(@NonNull TimeSignal timeSignal) {
+    public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
         if (DEBUG) {
-            Log.d(TAG, "suggestTime called: " + timeSignal);
+            Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion);
         }
         try {
-            mITimeDetectorService.suggestTime(timeSignal);
+            mITimeDetectorService.suggestPhoneTime(timeSuggestion);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/timedetector/TimeSignal.java b/core/java/android/app/timedetector/TimeSignal.java
deleted file mode 100644
index b494260..0000000
--- a/core/java/android/app/timedetector/TimeSignal.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.timedetector;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.TimestampedValue;
-
-import java.util.Objects;
-
-/**
- * A time signal from a named source. The value consists of the number of milliseconds elapsed since
- * 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime clock when that number was
- * established. The elapsed realtime clock is considered accurate but volatile, so time signals
- * must not be persisted across device resets.
- *
- * @hide
- */
-public final class TimeSignal implements Parcelable {
-
-    public static final @android.annotation.NonNull Parcelable.Creator<TimeSignal> CREATOR =
-            new Parcelable.Creator<TimeSignal>() {
-                public TimeSignal createFromParcel(Parcel in) {
-                    return TimeSignal.createFromParcel(in);
-                }
-
-                public TimeSignal[] newArray(int size) {
-                    return new TimeSignal[size];
-                }
-            };
-
-    public static final String SOURCE_ID_NITZ = "nitz";
-
-    private final String mSourceId;
-    private final TimestampedValue<Long> mUtcTime;
-
-    public TimeSignal(String sourceId, TimestampedValue<Long> utcTime) {
-        mSourceId = Objects.requireNonNull(sourceId);
-        mUtcTime = Objects.requireNonNull(utcTime);
-    }
-
-    private static TimeSignal createFromParcel(Parcel in) {
-        String sourceId = in.readString();
-        TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
-        return new TimeSignal(sourceId, utcTime);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeString(mSourceId);
-        dest.writeParcelable(mUtcTime, 0);
-    }
-
-    @NonNull
-    public String getSourceId() {
-        return mSourceId;
-    }
-
-    @NonNull
-    public TimestampedValue<Long> getUtcTime() {
-        return mUtcTime;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        TimeSignal that = (TimeSignal) o;
-        return Objects.equals(mSourceId, that.mSourceId)
-                && Objects.equals(mUtcTime, that.mUtcTime);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mSourceId, mUtcTime);
-    }
-
-    @Override
-    public String toString() {
-        return "TimeSignal{"
-                + "mSourceId='" + mSourceId + '\''
-                + ", mUtcTime=" + mUtcTime
-                + '}';
-    }
-}
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index 30868bf..97e3f52 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -154,7 +154,7 @@
     }
 
     /**
-     * Returns the local name of the BLE device. The is a UTF-8 encoded string.
+     * Returns the local name of the BLE device. This is a UTF-8 encoded string.
      */
     @Nullable
     public String getDeviceName() {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a6b95a9..51bb85a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -74,6 +74,7 @@
 import android.view.textclassifier.TextClassificationManager;
 
 import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.compat.IPlatformCompatNative;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -3231,6 +3232,7 @@
             //@hide ROLE_CONTROLLER_SERVICE,
             CAMERA_SERVICE,
             //@hide: PLATFORM_COMPAT_SERVICE,
+            //@hide: PLATFORM_COMPAT_NATIVE_SERVICE,
             PRINT_SERVICE,
             CONSUMER_IR_SERVICE,
             //@hide: TRUST_SERVICE,
@@ -4596,6 +4598,14 @@
     public static final String PLATFORM_COMPAT_SERVICE = "platform_compat";
 
     /**
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link IPlatformCompatNative} IBinder for native code communicating with the platform compat
+     * service.
+     * @hide
+     */
+    public static final String PLATFORM_COMPAT_NATIVE_SERVICE = "platform_compat_native";
+
+    /**
      * Service to capture a bugreport.
      * @see #getSystemService(String)
      * @see android.os.BugreportManager
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 3e325b7..e3259ff 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -57,6 +57,9 @@
     private static final String TAG = "NetworkCapabilities";
     private static final int INVALID_UID = -1;
 
+    // Set to true when private DNS is broken.
+    private boolean mPrivateDnsBroken;
+
     /**
      * @hide
      */
@@ -86,6 +89,7 @@
         mUids = null;
         mEstablishingVpnAppUid = INVALID_UID;
         mSSID = null;
+        mPrivateDnsBroken = false;
     }
 
     /**
@@ -104,6 +108,7 @@
         mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
         mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
         mSSID = nc.mSSID;
+        mPrivateDnsBroken = nc.mPrivateDnsBroken;
     }
 
     /**
@@ -557,6 +562,9 @@
         }
         if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
         if (hasSignalStrength()) return "signalStrength";
+        if (isPrivateDnsBroken()) {
+            return "privateDnsBroken";
+        }
         return null;
     }
 
@@ -1443,7 +1451,8 @@
                 && equalsSpecifier(that)
                 && equalsTransportInfo(that)
                 && equalsUids(that)
-                && equalsSSID(that));
+                && equalsSSID(that)
+                && equalsPrivateDnsBroken(that));
     }
 
     @Override
@@ -1460,7 +1469,8 @@
                 + (mSignalStrength * 29)
                 + Objects.hashCode(mUids) * 31
                 + Objects.hashCode(mSSID) * 37
-                + Objects.hashCode(mTransportInfo) * 41;
+                + Objects.hashCode(mTransportInfo) * 41
+                + Objects.hashCode(mPrivateDnsBroken) * 43;
     }
 
     @Override
@@ -1479,6 +1489,7 @@
         dest.writeInt(mSignalStrength);
         dest.writeArraySet(mUids);
         dest.writeString(mSSID);
+        dest.writeBoolean(mPrivateDnsBroken);
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1498,6 +1509,7 @@
                 netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
                         null /* ClassLoader, null for default */);
                 netCap.mSSID = in.readString();
+                netCap.mPrivateDnsBroken = in.readBoolean();
                 return netCap;
             }
             @Override
@@ -1555,6 +1567,10 @@
             sb.append(" SSID: ").append(mSSID);
         }
 
+        if (mPrivateDnsBroken) {
+            sb.append(" Private DNS is broken");
+        }
+
         sb.append("]");
         return sb.toString();
     }
@@ -1706,4 +1722,28 @@
     public boolean isMetered() {
         return !hasCapability(NET_CAPABILITY_NOT_METERED);
     }
+
+    /**
+     * Check if private dns is broken.
+     *
+     * @return {@code true} if {@code mPrivateDnsBroken} is set when private DNS is broken.
+     * @hide
+     */
+    public boolean isPrivateDnsBroken() {
+        return mPrivateDnsBroken;
+    }
+
+    /**
+     * Set mPrivateDnsBroken to true when private dns is broken.
+     *
+     * @param broken the status of private DNS to be set.
+     * @hide
+     */
+    public void setPrivateDnsBroken(boolean broken) {
+        mPrivateDnsBroken = broken;
+    }
+
+    private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) {
+        return mPrivateDnsBroken == nc.mPrivateDnsBroken;
+    }
 }
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 9ba3bd9..4ad52d5 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -77,6 +77,12 @@
      */
     public boolean skip464xlat;
 
+    /**
+     * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
+     * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
+     */
+    public boolean hasShownBroken;
+
     public NetworkMisc() {
     }
 
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 767c15c..9c999b2 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -113,6 +113,21 @@
     public static final String ACTION_UPDATE_CARRIER_ID_DB
             = "android.os.action.UPDATE_CARRIER_ID_DB";
 
+    /**
+    * Broadcast intent action indicating that the updated emergency number database is available.
+    * <p>Extra: "VERSION" the numeric version of the new data. Devices should only install if the
+    * update version is newer than the current one.
+    * <p>Extra: "REQUIRED_HASH" the hash of the current update data.
+    * <p>Input: {@link android.content.Intent#getData} is URI of downloaded emergency number file.
+    * Devices should pick up the downloaded file and persist to the database
+    * {@code com.android.internal.telephony.emergency.EmergencyNumberTracker}.
+    *
+    * @hide
+    */
+    @SystemApi
+    public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB =
+            "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
+
     private ConfigUpdate() {
     }
 }
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index 77fd946..0e00d5e 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -104,16 +104,17 @@
      * Start DynamicSystem installation. This call may take an unbounded amount of time. The caller
      * may use another thread to call the getStartProgress() to get the progress.
      *
-     * @param systemSize system size in bytes
-     * @param userdataSize userdata size in bytes
+     * @param name The DSU partition name
+     * @param size Size of the DSU image in bytes
+     * @param readOnly True if the partition is read only, e.g. system.
      * @return {@code true} if the call succeeds. {@code false} either the device does not contain
      *     enough space or a DynamicSystem is currently in use where the {@link #isInUse} would be
      *     true.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
-    public Session startInstallation(long systemSize, long userdataSize) {
+    public Session startInstallation(String name, long size, boolean readOnly) {
         try {
-            if (mService.startInstallation(systemSize, userdataSize)) {
+            if (mService.startInstallation(name, size, readOnly)) {
                 return new Session();
             } else {
                 return null;
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index a6de170..75f6785 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -24,11 +24,12 @@
      * Start DynamicSystem installation. This call may take 60~90 seconds. The caller
      * may use another thread to call the getStartProgress() to get the progress.
      *
-     * @param systemSize system size in bytes
-     * @param userdataSize userdata size in bytes
+     * @param name The DSU partition name
+     * @param size Size of the DSU image in bytes
+     * @param readOnly True if this partition is readOnly
      * @return true if the call succeeds
      */
-    boolean startInstallation(long systemSize, long userdataSize);
+    boolean startInstallation(@utf8InCpp String name, long size, boolean readOnly);
 
     /**
      * Query the progress of the current installation operation. This can be called while
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index d2f22bf..ff8b135 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -29,7 +29,6 @@
 import android.telephony.euicc.DownloadableSubscription;
 import android.telephony.euicc.EuiccInfo;
 import android.telephony.euicc.EuiccManager.OtaStatus;
-import android.util.ArraySet;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -252,18 +251,6 @@
     public static final int RESULT_FIRST_USER = 1;
 
     /**
-     * List of all valid resolution actions for validation purposes.
-     * @hide
-     */
-    public static final ArraySet<String> RESOLUTION_ACTIONS;
-    static {
-        RESOLUTION_ACTIONS = new ArraySet<>();
-        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
-        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
-        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_RESOLVABLE_ERRORS);
-    }
-
-    /**
      * Boolean extra for resolution actions indicating whether the user granted consent.
      * This is used and set by the implementation and used in {@code EuiccOperation}.
      */
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index 72b0ad7..e0eb9af 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -21,6 +21,7 @@
 import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -37,7 +38,7 @@
     private static final String TAG = "CompatibilityChangeReporter";
     private int mSource;
 
-    private final class ChangeReport {
+    private static final class ChangeReport {
         long mChangeId;
         int mState;
 
@@ -65,9 +66,13 @@
     @GuardedBy("mReportedChanges")
     private final Map<Integer, Set<ChangeReport>> mReportedChanges;
 
+    // When true will of every time to debug (logcat).
+    private boolean mDebugLogAll;
+
     public ChangeReporter(int source) {
         mSource = source;
         mReportedChanges =  new HashMap<>();
+        mDebugLogAll = false;
     }
 
     /**
@@ -79,20 +84,76 @@
      * @param state    of the reported change - enabled/disabled/only logged
      */
     public void reportChange(int uid, long changeId, int state) {
-        ChangeReport report = new ChangeReport(changeId, state);
+        if (shouldWriteToStatsLog(uid, changeId, state)) {
+            StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
+                    state, mSource);
+        }
+        if (shouldWriteToDebug(uid, changeId, state)) {
+            debugLog(uid, changeId, state);
+        }
+        markAsReported(uid, new ChangeReport(changeId, state));
+    }
+
+    /**
+     * Start logging all the time to logcat.
+     */
+    public void startDebugLogAll() {
+        mDebugLogAll = true;
+    }
+
+    /**
+     * Stop logging all the time to logcat.
+     */
+    public void stopDebugLogAll() {
+        mDebugLogAll = false;
+    }
+
+
+    /**
+     * Returns whether the next report should be logged to statsLog.
+     *
+     * @param uid      affected by the change
+     * @param changeId the reported change id
+     * @param state    of the reported change - enabled/disabled/only logged
+     * @return true if the report should be logged
+     */
+    @VisibleForTesting
+    public boolean shouldWriteToStatsLog(int uid, long changeId, int state) {
+        return !isAlreadyReported(uid, new ChangeReport(changeId, state));
+    }
+
+    /**
+     * Returns whether the next report should be logged to logcat.
+     *
+     * @param uid      affected by the change
+     * @param changeId the reported change id
+     * @param state    of the reported change - enabled/disabled/only logged
+     * @return true if the report should be logged
+     */
+    @VisibleForTesting
+    public boolean shouldWriteToDebug(int uid, long changeId, int state) {
+        return mDebugLogAll || !isAlreadyReported(uid, new ChangeReport(changeId, state));
+    }
+
+    private boolean isAlreadyReported(int uid, ChangeReport report) {
+        synchronized (mReportedChanges) {
+            Set<ChangeReport> reportedChangesForUid = mReportedChanges.get(uid);
+            if (reportedChangesForUid == null) {
+                return false;
+            } else {
+                return reportedChangesForUid.contains(report);
+            }
+        }
+    }
+
+    private void markAsReported(int uid, ChangeReport report) {
         synchronized (mReportedChanges) {
             Set<ChangeReport> reportedChangesForUid = mReportedChanges.get(uid);
             if (reportedChangesForUid == null) {
                 mReportedChanges.put(uid, new HashSet<ChangeReport>());
                 reportedChangesForUid = mReportedChanges.get(uid);
             }
-            if (!reportedChangesForUid.contains(report)) {
-                debugLog(uid, changeId, state);
-                StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
-                        state, mSource);
-                reportedChangesForUid.add(report);
-            }
-
+            reportedChangesForUid.add(report);
         }
     }
 
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 4099cfa..8391ad2 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -49,9 +49,10 @@
      * you do not need to call this API directly. The change will be reported for you.
      *
      * @param changeId    The ID of the compatibility change taking effect.
+     * @param userId      The ID of the user that the operation is done for.
      * @param packageName The package name of the app in question.
      */
-     void reportChangeByPackageName(long changeId, in String packageName);
+     void reportChangeByPackageName(long changeId, in String packageName, int userId);
 
     /**
      * Reports that a compatibility change is affecting an app process now.
@@ -86,7 +87,7 @@
      * be called when implementing functionality on behalf of the affected app.
      *
      * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a package name
-     * instead of an {@link ApplicationInfo}
+     * and userId instead of an {@link ApplicationInfo}
      * object, and finds an app info object based on the package name. Returns {@code true} if
      * there is no installed package by that name.
      *
@@ -100,9 +101,10 @@
      *
      * @param changeId    The ID of the compatibility change in question.
      * @param packageName The package name of the app in question.
+     * @param userId      The ID of the user that the operation is done for.
      * @return {@code true} if the change is enabled for the current app.
      */
-    boolean isChangeEnabledByPackageName(long changeId, in String packageName);
+    boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId);
 
     /**
      * Query if a given compatibility change is enabled for an app process. This method should
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dee53db..c17d7d1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4868,6 +4868,14 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="com.android.server.updates.EmergencyNumberDbInstallReceiver"
+                  android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.os.action.UPDATE_EMERGENCY_NUMBER_DB" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="com.android.server.MasterClearReceiver"
             android:permission="android.permission.MASTER_CLEAR">
             <intent-filter
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e132c36..936ff0c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3431,6 +3431,15 @@
     <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
     <string name="wifi_no_internet_detailed">Tap for options</string>
 
+    <!-- A notification is shown when the user connects to a mobile network without internet access. This is the notification's title. -->
+    <string name="mobile_no_internet">Mobile network has no internet access</string>
+
+    <!-- A notification is shown when the user connects to a non-mobile and non-wifi network without internet access. This is the notification's title. -->
+    <string name="other_networks_no_internet">Network has no internet access</string>
+
+    <!-- A notification is shown when connected network without internet due to private dns validation failed. This is the notification's message. [CHAR LIMIT=NONE] -->
+    <string name="private_dns_broken_detailed">Private DNS server cannot be accessed</string>
+
     <!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] -->
     <string name="captive_portal_logged_in_detailed">Connected</string>
     <!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's title. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fa58c7a..aacc74f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -590,9 +590,12 @@
   <java-symbol type="string" name="menu_space_shortcut_label" />
   <java-symbol type="string" name="menu_shift_shortcut_label" />
   <java-symbol type="string" name="menu_sym_shortcut_label" />
+  <java-symbol type="string" name="mobile_no_internet" />
   <java-symbol type="string" name="notification_title" />
+  <java-symbol type="string" name="other_networks_no_internet" />
   <java-symbol type="string" name="permission_request_notification_with_subtitle" />
   <java-symbol type="string" name="prepend_shortcut_label" />
+  <java-symbol type="string" name="private_dns_broken_detailed" />
   <java-symbol type="string" name="paste_as_plain_text" />
   <java-symbol type="string" name="replace" />
   <java-symbol type="string" name="undo" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index f71c8b0..2d1c61c 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -90,7 +90,7 @@
     <shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
 
     <!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
-    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529|70296|83782|3011|73240" />
+    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529|70296|83782|3011|73240|72438" />
 
     <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
     <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}|4665" />
diff --git a/core/tests/PlatformCompatFramework/Android.bp b/core/tests/PlatformCompatFramework/Android.bp
new file mode 100644
index 0000000..3380265
--- /dev/null
+++ b/core/tests/PlatformCompatFramework/Android.bp
@@ -0,0 +1,14 @@
+android_test {
+    name: "PlatformCompatFrameworkTests",
+    // Include all test java files.
+    srcs: ["src/**/*.java"],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: [
+        "junit",
+        "androidx.test.rules",
+    ],
+    platform_apis: true,
+}
diff --git a/core/tests/PlatformCompatFramework/AndroidManifest.xml b/core/tests/PlatformCompatFramework/AndroidManifest.xml
new file mode 100644
index 0000000..4a6383b
--- /dev/null
+++ b/core/tests/PlatformCompatFramework/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.internal.compat">
+    <application android:label="ChangeReporterTest">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.internal.compat"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/PlatformCompatFramework/OWNERS b/core/tests/PlatformCompatFramework/OWNERS
new file mode 100644
index 0000000..2b7cdb0
--- /dev/null
+++ b/core/tests/PlatformCompatFramework/OWNERS
@@ -0,0 +1,7 @@
+# Use this reviewer by default.
+platform-compat-eng+reviews@google.com
+
+andreionea@google.com
+atrost@google.com
+mathewi@google.com
+satayev@google.com
diff --git a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
new file mode 100644
index 0000000..09bbe37
--- /dev/null
+++ b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ChangeReporterTest {
+    @Test
+    public void testStatsLogOnce() {
+        ChangeReporter reporter = new ChangeReporter(0);
+        int myUid = 1022, otherUid = 1023;
+        long myChangeId = 500L, otherChangeId = 600L;
+        int myState = 1, otherState = 2;
+
+        assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+        reporter.reportChange(myUid, myChangeId, myState);
+
+        // Same report will not be logged again.
+        assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+        // Other reports will be logged.
+        assertTrue(reporter.shouldWriteToStatsLog(otherUid, myChangeId, myState));
+        assertTrue(reporter.shouldWriteToStatsLog(myUid, otherChangeId, myState));
+        assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, otherState));
+    }
+
+    @Test
+    public void testStatsLogAfterReset() {
+        ChangeReporter reporter = new ChangeReporter(0);
+        int myUid = 1022;
+        long myChangeId = 500L;
+        int myState = 1;
+
+        assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+        reporter.reportChange(myUid, myChangeId, myState);
+
+        // Same report will not be logged again.
+        assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+        reporter.resetReportedChanges(myUid);
+
+        // Same report will be logged again after reset.
+        assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+    }
+
+    @Test
+    public void testDebugLogOnce() {
+        ChangeReporter reporter = new ChangeReporter(0);
+        int myUid = 1022, otherUid = 1023;
+        long myChangeId = 500L, otherChangeId = 600L;
+        int myState = 1, otherState = 2;
+
+        assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+        reporter.reportChange(myUid, myChangeId, myState);
+
+        // Same report will not be logged again.
+        assertFalse(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+        // Other reports will be logged.
+        assertTrue(reporter.shouldWriteToDebug(otherUid, myChangeId, myState));
+        assertTrue(reporter.shouldWriteToDebug(myUid, otherChangeId, myState));
+        assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, otherState));
+    }
+
+    @Test
+    public void testDebugLogAfterReset() {
+        ChangeReporter reporter = new ChangeReporter(0);
+        int myUid = 1022;
+        long myChangeId = 500L;
+        int myState = 1;
+
+        assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+        reporter.reportChange(myUid, myChangeId, myState);
+
+        // Same report will not be logged again.
+        assertFalse(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+        reporter.resetReportedChanges(myUid);
+
+        // Same report will be logged again after reset.
+        assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+    }
+
+    @Test
+    public void testDebugLogWithLogAll() {
+        ChangeReporter reporter = new ChangeReporter(0);
+        int myUid = 1022;
+        long myChangeId = 500L;
+        int myState = 1;
+
+        assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+        reporter.reportChange(myUid, myChangeId, myState);
+
+        reporter.startDebugLogAll();
+        // Same report will be logged again.
+        assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+        assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+
+        reporter.stopDebugLogAll();
+        assertFalse(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
new file mode 100644
index 0000000..1b5ad88
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2019 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.TimestampedValue;
+
+import org.junit.Test;
+
+public class PhoneTimeSuggestionTest {
+    private static final int PHONE_ID = 99999;
+
+    @Test
+    public void testEquals() {
+        PhoneTimeSuggestion one =
+                new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
+        assertEquals(one, one);
+
+        PhoneTimeSuggestion two =
+                new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
+        assertEquals(one, two);
+        assertEquals(two, one);
+
+        PhoneTimeSuggestion three =
+                new PhoneTimeSuggestion(PHONE_ID + 1, new TimestampedValue<>(1111L, 2222L));
+        assertNotEquals(one, three);
+        assertNotEquals(three, one);
+
+        // DebugInfo must not be considered in equals().
+        one.addDebugInfo("Debug info 1");
+        two.addDebugInfo("Debug info 2");
+        assertEquals(one, two);
+    }
+
+    @Test
+    public void testParcelable() {
+        PhoneTimeSuggestion one =
+                new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
+        assertEquals(one, roundTripParcelable(one));
+
+        // DebugInfo should also be stored (but is not checked by equals()
+        one.addDebugInfo("This is debug info");
+        PhoneTimeSuggestion two = roundTripParcelable(one);
+        assertEquals(one.getDebugInfo(), two.getDebugInfo());
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T extends Parcelable> T roundTripParcelable(T one) {
+        Parcel parcel = Parcel.obtain();
+        parcel.writeTypedObject(one, 0);
+        parcel.setDataPosition(0);
+
+        T toReturn = (T) parcel.readTypedObject(PhoneTimeSuggestion.CREATOR);
+        parcel.recycle();
+        return toReturn;
+    }
+}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 53bc65d..bb731a8 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
@@ -26,6 +27,8 @@
 import android.media.audiopolicy.AudioMix;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Map;
 
@@ -431,6 +434,50 @@
     public static final int DEAD_OBJECT        = -6;
     public static final int WOULD_BLOCK        = -7;
 
+    /** @hide */
+    @IntDef({
+            SUCCESS,
+            ERROR,
+            BAD_VALUE,
+            INVALID_OPERATION,
+            PERMISSION_DENIED,
+            NO_INIT,
+            DEAD_OBJECT,
+            WOULD_BLOCK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AudioSystemError {}
+
+    /**
+     * Convert an int error value to its String value for readability.
+     * Accepted error values are the java AudioSystem errors, matching android_media_AudioErrors.h,
+     * which map onto the native status_t type.
+     * @param error one of the java AudioSystem errors
+     * @return a human-readable string
+     */
+    public static String audioSystemErrorToString(@AudioSystemError int error) {
+        switch(error) {
+            case SUCCESS:
+                return "SUCCESS";
+            case ERROR:
+                return "ERROR";
+            case BAD_VALUE:
+                return "BAD_VALUE";
+            case INVALID_OPERATION:
+                return "INVALID_OPERATION";
+            case PERMISSION_DENIED:
+                return "PERMISSION_DENIED";
+            case NO_INIT:
+                return "NO_INIT";
+            case DEAD_OBJECT:
+                return "DEAD_OBJECT";
+            case WOULD_BLOCK:
+                return "WOULD_BLOCK";
+            default:
+                return ("unknown error:" + error);
+        }
+    }
+
     /*
      * AudioPolicyService methods
      */
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 39474e1..01f1250 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -853,6 +853,10 @@
                 Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
             }
         }
+
+        public void notifyUnregistration() {
+            setRegistration(null);
+        }
     };
 
     //==================================================
diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
index 107e7cd..1d58151 100644
--- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
+++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
@@ -34,4 +34,8 @@
 
     // callback for volume events
     void notifyVolumeAdjust(int adjustment);
+
+    // callback for unregistration (e.g. if policy couldn't automatically be re-registered after
+    // an audioserver crash)
+    void notifyUnregistration();
 }
diff --git a/media/java/android/media/midi/MidiDeviceInfo.aidl b/media/java/android/media/midi/MidiDeviceInfo.aidl
index 5b2ac9b..a248204 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.aidl
+++ b/media/java/android/media/midi/MidiDeviceInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.media.midi;
 
-parcelable MidiDeviceInfo cpp_header "media/MidiDeviceInfo.h";
+parcelable MidiDeviceInfo cpp_header "MidiDeviceInfo.h";
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index 58317ed..8f3e12e 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -17,6 +17,7 @@
 
     srcs: [
         "amidi.cpp",
+        "MidiDeviceInfo.cpp",
         ":IMidiDeviceServer.aidl",
     ],
 
@@ -31,12 +32,14 @@
         "-fvisibility=hidden",
     ],
 
+    header_libs: [
+        "media_ndk_headers",
+    ],
+
     shared_libs: [
         "liblog",
         "libbinder",
         "libutils",
-        "libmedia",
-        "libmediandk",
         "libandroid_runtime",
     ],
 
diff --git a/media/native/midi/MidiDeviceInfo.cpp b/media/native/midi/MidiDeviceInfo.cpp
new file mode 100644
index 0000000..ac68d26
--- /dev/null
+++ b/media/native/midi/MidiDeviceInfo.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MidiDeviceInfo"
+
+#include <MidiDeviceInfo.h>
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/String16.h>
+
+namespace android {
+namespace media {
+namespace midi {
+
+// The constant values need to be kept in sync with MidiDeviceInfo.java.
+// static
+const char* const MidiDeviceInfo::PROPERTY_NAME = "name";
+const char* const MidiDeviceInfo::PROPERTY_MANUFACTURER = "manufacturer";
+const char* const MidiDeviceInfo::PROPERTY_PRODUCT = "product";
+const char* const MidiDeviceInfo::PROPERTY_VERSION = "version";
+const char* const MidiDeviceInfo::PROPERTY_SERIAL_NUMBER = "serial_number";
+const char* const MidiDeviceInfo::PROPERTY_ALSA_CARD = "alsa_card";
+const char* const MidiDeviceInfo::PROPERTY_ALSA_DEVICE = "alsa_device";
+
+String16 MidiDeviceInfo::getProperty(const char* propertyName) {
+    String16 value;
+    if (mProperties.getString(String16(propertyName), &value)) {
+        return value;
+    } else {
+        return String16();
+    }
+}
+
+#define RETURN_IF_FAILED(calledOnce)                                     \
+    {                                                                    \
+        status_t returnStatus = calledOnce;                              \
+        if (returnStatus) {                                              \
+            ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+            return returnStatus;                                         \
+         }                                                               \
+    }
+
+status_t MidiDeviceInfo::writeToParcel(Parcel* parcel) const {
+    // Needs to be kept in sync with code in MidiDeviceInfo.java
+    RETURN_IF_FAILED(parcel->writeInt32(mType));
+    RETURN_IF_FAILED(parcel->writeInt32(mId));
+    RETURN_IF_FAILED(parcel->writeInt32((int32_t)mInputPortNames.size()));
+    RETURN_IF_FAILED(parcel->writeInt32((int32_t)mOutputPortNames.size()));
+    RETURN_IF_FAILED(writeStringVector(parcel, mInputPortNames));
+    RETURN_IF_FAILED(writeStringVector(parcel, mOutputPortNames));
+    RETURN_IF_FAILED(parcel->writeInt32(mIsPrivate ? 1 : 0));
+    RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
+    // This corresponds to "extra" properties written by Java code
+    RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
+    return OK;
+}
+
+status_t MidiDeviceInfo::readFromParcel(const Parcel* parcel) {
+    // Needs to be kept in sync with code in MidiDeviceInfo.java
+    RETURN_IF_FAILED(parcel->readInt32(&mType));
+    RETURN_IF_FAILED(parcel->readInt32(&mId));
+    int32_t inputPortCount;
+    RETURN_IF_FAILED(parcel->readInt32(&inputPortCount));
+    int32_t outputPortCount;
+    RETURN_IF_FAILED(parcel->readInt32(&outputPortCount));
+    RETURN_IF_FAILED(readStringVector(parcel, &mInputPortNames, inputPortCount));
+    RETURN_IF_FAILED(readStringVector(parcel, &mOutputPortNames, outputPortCount));
+    int32_t isPrivate;
+    RETURN_IF_FAILED(parcel->readInt32(&isPrivate));
+    mIsPrivate = isPrivate == 1;
+    RETURN_IF_FAILED(mProperties.readFromParcel(parcel));
+    // Ignore "extra" properties as they may contain Java Parcelables
+    return OK;
+}
+
+status_t MidiDeviceInfo::readStringVector(
+        const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength) {
+    std::unique_ptr<std::vector<std::unique_ptr<String16>>> v;
+    status_t result = parcel->readString16Vector(&v);
+    if (result != OK) return result;
+    vectorPtr->clear();
+    if (v.get() != nullptr) {
+        for (const auto& iter : *v) {
+            if (iter.get() != nullptr) {
+                vectorPtr->push_back(*iter);
+            } else {
+                vectorPtr->push_back(String16());
+            }
+        }
+    } else {
+        vectorPtr->resize(defaultLength);
+    }
+    return OK;
+}
+
+status_t MidiDeviceInfo::writeStringVector(Parcel* parcel, const Vector<String16>& vector) const {
+    std::vector<String16> v;
+    for (size_t i = 0; i < vector.size(); ++i) {
+        v.push_back(vector[i]);
+    }
+    return parcel->writeString16Vector(v);
+}
+
+// Vector does not define operator==
+static inline bool areVectorsEqual(const Vector<String16>& lhs, const Vector<String16>& rhs) {
+    if (lhs.size() != rhs.size()) return false;
+    for (size_t i = 0; i < lhs.size(); ++i) {
+        if (lhs[i] != rhs[i]) return false;
+    }
+    return true;
+}
+
+bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) {
+    return (lhs.mType == rhs.mType && lhs.mId == rhs.mId &&
+            areVectorsEqual(lhs.mInputPortNames, rhs.mInputPortNames) &&
+            areVectorsEqual(lhs.mOutputPortNames, rhs.mOutputPortNames) &&
+            lhs.mProperties == rhs.mProperties &&
+            lhs.mIsPrivate == rhs.mIsPrivate);
+}
+
+}  // namespace midi
+}  // namespace media
+}  // namespace android
diff --git a/media/native/midi/MidiDeviceInfo.h b/media/native/midi/MidiDeviceInfo.h
new file mode 100644
index 0000000..5b4a241
--- /dev/null
+++ b/media/native/midi/MidiDeviceInfo.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_MIDI_DEVICE_INFO_H
+#define ANDROID_MEDIA_MIDI_DEVICE_INFO_H
+
+#include <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace media {
+namespace midi {
+
+class MidiDeviceInfo : public Parcelable {
+public:
+    MidiDeviceInfo() = default;
+    virtual ~MidiDeviceInfo() = default;
+    MidiDeviceInfo(const MidiDeviceInfo& midiDeviceInfo) = default;
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+
+    int getType() const { return mType; }
+    int getUid() const { return mId; }
+    bool isPrivate() const { return mIsPrivate; }
+    const Vector<String16>& getInputPortNames() const { return mInputPortNames; }
+    const Vector<String16>&  getOutputPortNames() const { return mOutputPortNames; }
+    String16 getProperty(const char* propertyName);
+
+    // The constants need to be kept in sync with MidiDeviceInfo.java
+    enum {
+        TYPE_USB = 1,
+        TYPE_VIRTUAL = 2,
+        TYPE_BLUETOOTH = 3,
+    };
+    static const char* const PROPERTY_NAME;
+    static const char* const PROPERTY_MANUFACTURER;
+    static const char* const PROPERTY_PRODUCT;
+    static const char* const PROPERTY_VERSION;
+    static const char* const PROPERTY_SERIAL_NUMBER;
+    static const char* const PROPERTY_ALSA_CARD;
+    static const char* const PROPERTY_ALSA_DEVICE;
+
+    friend bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs);
+    friend bool operator!=(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) {
+        return !(lhs == rhs);
+    }
+
+private:
+    status_t readStringVector(
+            const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength);
+    status_t writeStringVector(Parcel* parcel, const Vector<String16>& vector) const;
+
+    int32_t mType;
+    int32_t mId;
+    Vector<String16> mInputPortNames;
+    Vector<String16> mOutputPortNames;
+    os::PersistableBundle mProperties;
+    bool mIsPrivate;
+};
+
+}  // namespace midi
+}  // namespace media
+}  // namespace android
+
+#endif  // ANDROID_MEDIA_MIDI_DEVICE_INFO_H
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index 1e9a194..e7c4d99 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -26,7 +26,7 @@
 #include <core_jni_helpers.h>
 
 #include "android/media/midi/BpMidiDeviceServer.h"
-#include "media/MidiDeviceInfo.h"
+#include "MidiDeviceInfo.h"
 
 #include "include/amidi/AMidi.h"
 #include "amidi_internal.h"
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 7c1af4a..ae8cb3a 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -110,3 +110,31 @@
     symbol_file: "libandroid_net.map.txt",
     unversioned: true,
 }
+
+
+// Aidl library for platform compat.
+cc_library_shared {
+    name: "lib-platform-compat-native-api",
+    defaults: ["libandroid_defaults"],
+
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    aidl: {
+        local_include_dirs: ["aidl"],
+        export_aidl_headers: true,
+    },
+    srcs: [
+         ":platform-compat-native-aidl",
+    ],
+    export_include_dirs: ["aidl"],
+}
+
+filegroup {
+    name: "platform-compat-native-aidl",
+    srcs: [
+        "aidl/com/android/internal/compat/IPlatformCompatNative.aidl",
+    ],
+    path: "aidl",
+}
\ No newline at end of file
diff --git a/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl
new file mode 100644
index 0000000..347e4e8
--- /dev/null
+++ b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+/**
+ * Platform native private API for talking with the PlatformCompat service.
+ *
+ * <p> Should be used for gating and logging from non-app processes running cpp code.
+ * For app processes please use android.compat.Compatibility API.
+ *
+ * {@hide}
+ */
+interface IPlatformCompatNative
+{
+    /**
+     * Reports that a compatibility change is affecting an app process now.
+     *
+     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, String)},
+     * you do not need to call this API directly. The change will be reported for you.
+     *
+     * @param changeId    The ID of the compatibility change taking effect.
+     * @param userId      The ID of the user that the operation is done for.
+     * @param packageName The package name of the app in question.
+     */
+     void reportChangeByPackageName(long changeId, @utf8InCpp String packageName, int userId);
+
+    /**
+     * Reports that a compatibility change is affecting an app process now.
+     *
+     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, int)},
+     * you do not need to call this API directly. The change will be reported for you.
+     *
+     * @param changeId The ID of the compatibility change taking effect.
+     * @param uid      The UID of the app in question.
+     */
+    void reportChangeByUid(long changeId, int uid);
+
+    /**
+     * Query if a given compatibility change is enabled for an app process. This method should
+     * be called when implementing functionality on behalf of the affected app.
+     *
+     * <p>Returns {@code true} if there is no installed package by the provided package name.
+     *
+     * <p>If this method returns {@code true}, the calling code should implement the compatibility
+     * change, resulting in differing behaviour compared to earlier releases. If this method
+     * returns
+     * {@code false}, the calling code should behave as it did in earlier releases.
+     *
+     * <p>It will also report the change as {@link #reportChange(long, String)} would, so there is
+     * no need to call that method directly.
+     *
+     * @param changeId    The ID of the compatibility change in question.
+     * @param packageName The package name of the app in question.
+     * @param userId      The ID of the user that the operation is done for.
+     * @return {@code true} if the change is enabled for the current app.
+     */
+    boolean isChangeEnabledByPackageName(long changeId, @utf8InCpp String packageName, int userId);
+
+    /**
+     * Query if a given compatibility change is enabled for an app process. This method should
+     * be called when implementing functionality on behalf of the affected app.
+     *
+     * <p> Returns {@code true} if there are no installed packages for the required UID, or if the
+     * change is enabled for ALL of the installed packages associated with the provided UID. Please
+     * use a more specific API if you want a different behaviour for multi-package UIDs.
+     *
+     * <p>If this method returns {@code true}, the calling code should implement the compatibility
+     * change, resulting in differing behaviour compared to earlier releases. If this method
+     * returns {@code false}, the calling code should behave as it did in earlier releases.
+     *
+     * <p>It will also report the change as {@link #reportChange(long, int)} would, so there is
+     * no need to call that method directly.
+     *
+     * @param changeId The ID of the compatibility change in question.
+     * @param uid      The UID of the app in question.
+     * @return {@code true} if the change is enabled for the current app.
+     */
+    boolean isChangeEnabledByUid(long changeId, int uid);
+}
\ No newline at end of file
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 81c5bcd..642dc82 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -35,7 +35,6 @@
 import android.net.http.SslError;
 import android.os.Bundle;
 import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -477,11 +476,11 @@
     }
 
     private static void logd(String s) {
-        Rlog.d(TAG, s);
+        Log.d(TAG, s);
     }
 
     private static void loge(String s) {
-        Rlog.d(TAG, s);
+        Log.d(TAG, s);
     }
 
 }
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
index 02c61d7..46b1d5f 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
@@ -19,7 +19,6 @@
 import android.content.Intent;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -27,7 +26,6 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -44,7 +42,7 @@
     private static final String INTER_GROUP_DELIMITER = "\\s*:\\s*";
 
     private static final String TAG = CustomConfigLoader.class.getSimpleName();
-    private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
 
     /**
      * loads and parses the carrier config, return a list of carrier action for the given signal
@@ -70,7 +68,7 @@
         // return an empty list if no match found
         List<Integer> actionList = new ArrayList<>();
         if (carrierConfigManager == null) {
-            Rlog.e(TAG, "load carrier config failure with carrier config manager uninitialized");
+            Log.e(TAG, "load carrier config failure with carrier config manager uninitialized");
             return actionList;
         }
         PersistableBundle b = carrierConfigManager.getConfig();
@@ -101,8 +99,8 @@
                             .EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false));
                     break;
                 default:
-                    Rlog.e(TAG, "load carrier config failure with un-configured key: " +
-                            intent.getAction());
+                    Log.e(TAG, "load carrier config failure with un-configured key: "
+                            + intent.getAction());
                     break;
             }
             if (!ArrayUtils.isEmpty(configs)) {
@@ -111,12 +109,12 @@
                     matchConfig(config, arg1, arg2, actionList);
                     if (!actionList.isEmpty()) {
                         // return the first match
-                        if (VDBG) Rlog.d(TAG, "found match action list: " + actionList.toString());
+                        if (VDBG) Log.d(TAG, "found match action list: " + actionList.toString());
                         return actionList;
                     }
                 }
             }
-            Rlog.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
+            Log.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
                     + "arg2: " + arg2);
         }
         return actionList;
@@ -166,7 +164,7 @@
                 try {
                     actionList.add(Integer.parseInt(idx));
                 } catch (NumberFormatException e) {
-                    Rlog.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
+                    Log.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
                             + e);
                 }
             }
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index cf286bd..738c425 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -99,11 +99,13 @@
             // init input stream before calling startInstallation(), which takes 90 seconds.
             initInputStream();
 
-            Thread thread = new Thread(() -> {
-                mInstallationSession =
-                        mDynSystem.startInstallation(mSystemSize, mUserdataSize);
-            });
-
+            Thread thread =
+                    new Thread(
+                            () -> {
+                                mDynSystem.startInstallation("userdata", mUserdataSize, false);
+                                mInstallationSession =
+                                        mDynSystem.startInstallation("system", mSystemSize, true);
+                            });
 
             thread.start();
 
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 0e91839..bdfa217 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -127,6 +127,9 @@
     <!-- Summary for Connected wifi network without internet -->
     <string name="wifi_connected_no_internet">Connected, no internet</string>
 
+    <!-- Summary for connected network without internet due to private dns validation failed [CHAR LIMIT=NONE] -->
+    <string name="private_dns_broken">Private DNS server cannot be accessed</string>
+
     <!-- Summary for connected wifi network with partial internet connectivity [CHAR LIMIT=50] -->
     <string name="wifi_limited_connection">Limited connection</string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index f16fb1c..9e71189 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -50,6 +50,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
@@ -1569,7 +1570,13 @@
                         NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
                     return context.getString(R.string.wifi_limited_connection);
                 } else if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
-                    return context.getString(R.string.wifi_connected_no_internet);
+                    final String mode = Settings.Global.getString(context.getContentResolver(),
+                            Settings.Global.PRIVATE_DNS_MODE);
+                    if (nc.isPrivateDnsBroken()) {
+                        return context.getString(R.string.private_dns_broken);
+                    } else {
+                        return context.getString(R.string.wifi_connected_no_internet);
+                    }
                 }
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 5352936..b11585a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -31,6 +31,7 @@
 import android.net.wifi.WifiSsid;
 import android.os.Handler;
 import android.os.Looper;
+import android.provider.Settings;
 
 import com.android.settingslib.R;
 
@@ -163,7 +164,13 @@
                 statusLabel = mContext.getString(R.string.wifi_limited_connection);
                 return;
             } else if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
-                statusLabel = mContext.getString(R.string.wifi_status_no_internet);
+                final String mode = Settings.Global.getString(mContext.getContentResolver(),
+                        Settings.Global.PRIVATE_DNS_MODE);
+                if (networkCapabilities.isPrivateDnsBroken()) {
+                    statusLabel = mContext.getString(R.string.private_dns_broken);
+                } else {
+                    statusLabel = mContext.getString(R.string.wifi_status_no_internet);
+                }
                 return;
             }
         }
diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS
new file mode 100644
index 0000000..2054129
--- /dev/null
+++ b/packages/SettingsProvider/OWNERS
@@ -0,0 +1,3 @@
+hackbod@google.com
+svetoslavganov@google.com
+moltmann@google.com
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 591af82..a25c4f7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -436,13 +436,13 @@
     <string name="data_connection_lte_plus">LTE+</string>
 
     <!-- Content description of the data connection type 5Ge. [CHAR LIMIT=NONE] -->
-    <string name="data_connection_5ge" translate="false">5Ge</string>
+    <string name="data_connection_5ge" translatable="false">5Ge</string>
 
     <!-- Content description of the data connection type 5G. [CHAR LIMIT=NONE] -->
-    <string name="data_connection_5g" translate="false">5G</string>
+    <string name="data_connection_5g" translatable="false">5G</string>
 
     <!-- Content description of the data connection type 5G+. [CHAR LIMIT=NONE] -->
-    <string name="data_connection_5g_plus" translate="false">5G+</string>
+    <string name="data_connection_5g_plus" translatable="false">5G+</string>
 
     <!-- Content description of the data connection type CDMA. [CHAR LIMIT=NONE] -->
     <string name="data_connection_cdma">1X</string>
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
new file mode 100644
index 0000000..ca69c18
--- /dev/null
+++ b/packages/Tethering/Android.bp
@@ -0,0 +1,79 @@
+//
+// Copyright (C) 2019 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.
+//
+
+java_defaults {
+    name: "TetheringAndroidLibraryDefaults",
+    platform_apis: true,
+    srcs: [
+        "src/**/*.java",
+        ":framework-tethering-shared-srcs",
+        ":services-tethering-shared-srcs",
+    ],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "netd_aidl_interface-java",
+        "networkstack-aidl-interfaces-java",
+        "tethering-client",
+    ],
+    manifest: "AndroidManifestBase.xml",
+}
+
+// Build tethering static library, used to compile both variants of the tethering.
+android_library {
+    name: "TetheringApiCurrentLib",
+    defaults: ["TetheringAndroidLibraryDefaults"],
+}
+
+// Common defaults for compiling the actual APK.
+java_defaults {
+    name: "TetheringAppDefaults",
+    platform_apis: true,
+    privileged: true,
+    resource_dirs: [
+        "res",
+    ],
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+}
+
+// Non-updatable tethering running in the system server process for devices not using the module
+// TODO: build in-process tethering APK here.
+
+// Updatable tethering packaged as an application
+android_app {
+    name: "Tethering",
+    defaults: ["TetheringAppDefaults"],
+    static_libs: ["TetheringApiCurrentLib"],
+    certificate: "networkstack",
+    manifest: "AndroidManifest.xml",
+    use_embedded_native_libs: true,
+    // The permission configuration *must* be included to ensure security of the device
+    required: ["NetworkPermissionConfig"],
+}
+
+// This group will be removed when tethering migration is done.
+filegroup {
+    name: "tethering-services-srcs",
+    srcs: [
+        "src/com/android/server/connectivity/tethering/TetheringConfiguration.java",
+        "src/android/net/dhcp/DhcpServerCallbacks.java",
+        "src/android/net/dhcp/DhcpServingParamsParcelExt.java",
+        "src/android/net/ip/IpServer.java",
+        "src/android/net/ip/RouterAdvertisementDaemon.java",
+        "src/android/net/util/InterfaceSet.java",
+    ],
+}
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
new file mode 100644
index 0000000..eb51593
--- /dev/null
+++ b/packages/Tethering/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.tethering"
+          android:sharedUserId="android.uid.networkstack">
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+
+    <application
+        android:process="com.android.networkstack.process"
+        android:extractNativeLibs="false"
+        android:persistent="true">
+    </application>
+</manifest>
diff --git a/packages/Tethering/AndroidManifestBase.xml b/packages/Tethering/AndroidManifestBase.xml
new file mode 100644
index 0000000..b9cac19
--- /dev/null
+++ b/packages/Tethering/AndroidManifestBase.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.tethering"
+          android:versionCode="1"
+          android:versionName="R-initial">
+    <application
+        android:label="Tethering"
+        android:defaultToDeviceProtectedStorage="true"
+        android:directBootAware="true"
+        android:usesCleartextTraffic="true">
+    </application>
+</manifest>
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
new file mode 100644
index 0000000..5b01b1e
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2019 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.
+//
+
+// AIDL interfaces between the core system and the tethering mainline module.
+aidl_interface {
+    name: "tethering-aidl-interfaces",
+    local_include_dir: "src",
+    srcs: [
+        "src/android/net/ITetheringConnector.aidl",
+    ],
+    backend: {
+        ndk: {
+            enabled: false,
+        },
+        cpp: {
+            enabled: false,
+        },
+    },
+}
+
+java_library {
+    name: "tethering-client",
+    platform_apis: true,
+    static_libs: [
+        "tethering-aidl-interfaces-java",
+    ],
+}
diff --git a/core/java/android/app/timedetector/TimeSignal.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
similarity index 62%
copy from core/java/android/app/timedetector/TimeSignal.aidl
copy to packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index d2ec357..443481e 100644
--- a/core/java/android/app/timedetector/TimeSignal.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -1,19 +1,20 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
+/**
+ * Copyright (c) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
+ * See the License for the specific language governing perNmissions and
  * limitations under the License.
  */
+package android.net;
 
-package android.app.timedetector;
-
-parcelable TimeSignal;
\ No newline at end of file
+/** @hide */
+oneway interface ITetheringConnector {
+}
diff --git a/packages/Tethering/proguard.flags b/packages/Tethering/proguard.flags
new file mode 100644
index 0000000..77fc024
--- /dev/null
+++ b/packages/Tethering/proguard.flags
@@ -0,0 +1 @@
+#TBD
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
new file mode 100644
index 0000000..37e679d
--- /dev/null
+++ b/packages/Tethering/res/values/config.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!--
+    OEMs that wish to change the below settings must do so via a runtime resource overlay package
+    and *NOT* by changing this file. This file is part of the tethering mainline module.
+    -->
+</resources>
diff --git a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpServerCallbacks.java
rename to packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
rename to packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
diff --git a/services/net/java/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
similarity index 94%
rename from services/net/java/android/net/ip/IpServer.java
rename to packages/Tethering/src/android/net/ip/IpServer.java
index 3d79bba..ff3d7bc 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -16,7 +16,7 @@
 
 package android.net.ip;
 
-import static android.net.NetworkUtils.numericToInetAddress;
+import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.util.NetworkConstants.FF;
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
@@ -77,6 +77,7 @@
     public static final int STATE_TETHERED    = 2;
     public static final int STATE_LOCAL_ONLY  = 3;
 
+    /** Get string name of |state|.*/
     public static String getStateString(int state) {
         switch (state) {
             case STATE_UNAVAILABLE: return "UNAVAILABLE";
@@ -103,15 +104,16 @@
     // TODO: have this configurable
     private static final int DHCP_LEASE_TIME_SECS = 3600;
 
-    private final static String TAG = "IpServer";
-    private final static boolean DBG = false;
-    private final static boolean VDBG = false;
-    private static final Class[] messageClasses = {
+    private static final String TAG = "IpServer";
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+    private static final Class[] sMessageClasses = {
             IpServer.class
     };
     private static final SparseArray<String> sMagicDecoderRing =
-            MessageUtils.findMessageNames(messageClasses);
+            MessageUtils.findMessageNames(sMessageClasses);
 
+    /** IpServer callback. */
     public static class Callback {
         /**
          * Notify that |who| has changed its tethering state.
@@ -131,11 +133,14 @@
         public void updateLinkProperties(IpServer who, LinkProperties newLp) {}
     }
 
+    /** Capture IpServer dependencies, for injection. */
     public static class Dependencies {
+        /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
             return new RouterAdvertisementDaemon(ifParams);
         }
 
+        /** Get |ifName|'s interface information.*/
         public InterfaceParams getInterfaceParams(String ifName) {
             return InterfaceParams.getByName(ifName);
         }
@@ -244,25 +249,51 @@
         setInitialState(mInitialState);
     }
 
-    public String interfaceName() { return mIfaceName; }
-
-    public int interfaceType() { return mInterfaceType; }
-
-    public int lastError() { return mLastError; }
-
-    public int servingMode() { return mServingMode; }
-
-    public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); }
-
-    public void stop() { sendMessage(CMD_INTERFACE_DOWN); }
-
-    public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); }
+    /** Interface name which IpServer served.*/
+    public String interfaceName() {
+        return mIfaceName;
+    }
 
     /**
-     * Internals.
+     * Tethering downstream type. It would be one of ConnectivityManager#TETHERING_*.
      */
+    public int interfaceType() {
+        return mInterfaceType;
+    }
 
-    private boolean startIPv4() { return configureIPv4(true); }
+    /** Last error from this IpServer. */
+    public int lastError() {
+        return mLastError;
+    }
+
+    /** Serving mode is the current state of IpServer state machine. */
+    public int servingMode() {
+        return mServingMode;
+    }
+
+    /** The properties of the network link which IpServer is serving. */
+    public LinkProperties linkProperties() {
+        return new LinkProperties(mLinkProperties);
+    }
+
+    /** Stop this IpServer. After this is called this IpServer should not be used any more. */
+    public void stop() {
+        sendMessage(CMD_INTERFACE_DOWN);
+    }
+
+    /**
+     * Tethering is canceled. IpServer state machine will be available and wait for
+     * next tethering request.
+     */
+    public void unwanted() {
+        sendMessage(CMD_TETHER_UNREQUESTED);
+    }
+
+    /** Internals. */
+
+    private boolean startIPv4() {
+        return configureIPv4(true);
+    }
 
     /**
      * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer
@@ -410,7 +441,7 @@
             prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
         } else {
             // BT configures the interface elsewhere: only start DHCP.
-            final Inet4Address srvAddr = (Inet4Address) numericToInetAddress(BLUETOOTH_IFACE_ADDR);
+            final Inet4Address srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
             return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
         }
 
@@ -422,7 +453,7 @@
                 return false;
             }
 
-            InetAddress addr = numericToInetAddress(ipAsString);
+            InetAddress addr = parseNumericAddress(ipAsString);
             linkAddr = new LinkAddress(addr, prefixLen);
             ifcg.setLinkAddress(linkAddr);
             if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
@@ -473,7 +504,7 @@
 
     private String getRandomWifiIPv4Address() {
         try {
-            byte[] bytes = numericToInetAddress(WIFI_HOST_IFACE_ADDR).getAddress();
+            byte[] bytes = parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress();
             bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
             return InetAddress.getByAddress(bytes).getHostAddress();
         } catch (Exception e) {
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
similarity index 88%
rename from services/net/java/android/net/ip/RouterAdvertisementDaemon.java
rename to packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 59aea21..4147413 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -119,6 +119,7 @@
     private volatile MulticastTransmitter mMulticastTransmitter;
     private volatile UnicastResponder mUnicastResponder;
 
+    /** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/
     public static class RaParams {
         // Tethered traffic will have the hop limit properly decremented.
         // Consequently, set the hoplimit greater by one than the upstream
@@ -150,10 +151,12 @@
             dnses = (HashSet) other.dnses.clone();
         }
 
-        // Returns the subset of RA parameters that become deprecated when
-        // moving from announcing oldRa to announcing newRa.
-        //
-        // Currently only tracks differences in |prefixes| and |dnses|.
+        /**
+         * Returns the subset of RA parameters that become deprecated when
+         * moving from announcing oldRa to announcing newRa.
+         *
+         * Currently only tracks differences in |prefixes| and |dnses|.
+         */
         public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
             RaParams newlyDeprecated = new RaParams();
 
@@ -179,7 +182,9 @@
         private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
         private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
 
-        Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); }
+        Set<IpPrefix> getPrefixes() {
+            return mPrefixes.keySet();
+        }
 
         void putPrefixes(Set<IpPrefix> prefixes) {
             for (IpPrefix ipp : prefixes) {
@@ -193,7 +198,9 @@
             }
         }
 
-        Set<Inet6Address> getDnses() { return mDnses.keySet(); }
+        Set<Inet6Address> getDnses() {
+            return mDnses.keySet();
+        }
 
         void putDnses(Set<Inet6Address> dnses) {
             for (Inet6Address dns : dnses) {
@@ -207,7 +214,9 @@
             }
         }
 
-        boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); }
+        boolean isEmpty() {
+            return mPrefixes.isEmpty() && mDnses.isEmpty();
+        }
 
         private boolean decrementCounters() {
             boolean removed = decrementCounter(mPrefixes);
@@ -219,7 +228,7 @@
             boolean removed = false;
 
             for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
-                 it.hasNext();) {
+                    it.hasNext();) {
                 Map.Entry<T, Integer> kv = it.next();
                 if (kv.getValue() == 0) {
                     it.remove();
@@ -240,6 +249,7 @@
         mDeprecatedInfoTracker = new DeprecatedInfoTracker();
     }
 
+    /** Build new RA.*/
     public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
         synchronized (mLock) {
             if (deprecatedParams != null) {
@@ -260,6 +270,7 @@
         maybeNotifyMulticastTransmitter();
     }
 
+    /** Start router advertisement daemon. */
     public boolean start() {
         if (!createSocket()) {
             return false;
@@ -274,6 +285,7 @@
         return true;
     }
 
+    /** Stop router advertisement daemon. */
     public void stop() {
         closeSocket();
         // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
@@ -362,8 +374,12 @@
         }
     }
 
-    private static byte asByte(int value) { return (byte) value; }
-    private static short asShort(int value) { return (short) value; }
+    private static byte asByte(int value) {
+        return (byte) value;
+    }
+    private static short asShort(int value) {
+        return (short) value;
+    }
 
     private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) {
         /**
@@ -384,14 +400,14 @@
             +-+-+-+-+-+-+-+-+-+-+-+-
         */
         ra.put(ICMPV6_ND_ROUTER_ADVERT)
-          .put(asByte(0))
-          .putShort(asShort(0))
-          .put(hopLimit)
-          // RFC 4191 "high" preference, iff. advertising a default route.
-          .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
-          .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
-          .putInt(0)
-          .putInt(0);
+                .put(asByte(0))
+                .putShort(asShort(0))
+                .put(hopLimit)
+                // RFC 4191 "high" preference, iff. advertising a default route.
+                .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
+                .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
+                .putInt(0)
+                .putInt(0);
     }
 
     private static void putSlla(ByteBuffer ra, byte[] slla) {
@@ -408,11 +424,12 @@
             // Only IEEE 802.3 6-byte addresses are supported.
             return;
         }
-        final byte ND_OPTION_SLLA = 1;
-        final byte SLLA_NUM_8OCTETS = 1;
-        ra.put(ND_OPTION_SLLA)
-          .put(SLLA_NUM_8OCTETS)
-          .put(slla);
+
+        final byte nd_option_slla = 1;
+        final byte slla_num_8octets = 1;
+        ra.put(nd_option_slla)
+            .put(slla_num_8octets)
+            .put(slla);
     }
 
     private static void putExpandedFlagsOption(ByteBuffer ra) {
@@ -428,13 +445,13 @@
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          */
 
-        final byte ND_OPTION_EFO = 26;
-        final byte EFO_NUM_8OCTETS = 1;
+        final byte nd_option__efo = 26;
+        final byte efo_num_8octets = 1;
 
-        ra.put(ND_OPTION_EFO)
-          .put(EFO_NUM_8OCTETS)
-          .putShort(asShort(0))
-          .putInt(0);
+        ra.put(nd_option__efo)
+            .put(efo_num_8octets)
+            .putShort(asShort(0))
+            .putInt(0);
     }
 
     private static void putMtu(ByteBuffer ra, int mtu) {
@@ -449,12 +466,12 @@
             |                              MTU                              |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         */
-        final byte ND_OPTION_MTU = 5;
-        final byte MTU_NUM_8OCTETS = 1;
-        ra.put(ND_OPTION_MTU)
-          .put(MTU_NUM_8OCTETS)
-          .putShort(asShort(0))
-          .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
+        final byte nd_option_mtu = 5;
+        final byte mtu_num_8octs = 1;
+        ra.put(nd_option_mtu)
+            .put(mtu_num_8octs)
+            .putShort(asShort(0))
+            .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
     }
 
     private static void putPio(ByteBuffer ra, IpPrefix ipp,
@@ -486,22 +503,22 @@
         if (prefixLength != 64) {
             return;
         }
-        final byte ND_OPTION_PIO = 3;
-        final byte PIO_NUM_8OCTETS = 4;
+        final byte nd_option_pio = 3;
+        final byte pio_num_8octets = 4;
 
         if (validTime < 0) validTime = 0;
         if (preferredTime < 0) preferredTime = 0;
         if (preferredTime > validTime) preferredTime = validTime;
 
         final byte[] addr = ipp.getAddress().getAddress();
-        ra.put(ND_OPTION_PIO)
-          .put(PIO_NUM_8OCTETS)
-          .put(asByte(prefixLength))
-          .put(asByte(0xc0)) /* L & A set */
-          .putInt(validTime)
-          .putInt(preferredTime)
-          .putInt(0)
-          .put(addr);
+        ra.put(nd_option_pio)
+            .put(pio_num_8octets)
+            .put(asByte(prefixLength))
+            .put(asByte(0xc0)) /* L & A set */
+            .putInt(validTime)
+            .putInt(preferredTime)
+            .putInt(0)
+            .put(addr);
     }
 
     private static void putRio(ByteBuffer ra, IpPrefix ipp) {
@@ -524,16 +541,16 @@
         if (prefixLength > 64) {
             return;
         }
-        final byte ND_OPTION_RIO = 24;
-        final byte RIO_NUM_8OCTETS = asByte(
+        final byte nd_option_rio = 24;
+        final byte rio_num_8octets = asByte(
                 (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3);
 
         final byte[] addr = ipp.getAddress().getAddress();
-        ra.put(ND_OPTION_RIO)
-          .put(RIO_NUM_8OCTETS)
-          .put(asByte(prefixLength))
-          .put(asByte(0x18))
-          .putInt(DEFAULT_LIFETIME);
+        ra.put(nd_option_rio)
+            .put(rio_num_8octets)
+            .put(asByte(prefixLength))
+            .put(asByte(0x18))
+            .putInt(DEFAULT_LIFETIME);
 
         // Rely upon an IpPrefix's address being properly zeroed.
         if (prefixLength > 0) {
@@ -566,12 +583,12 @@
         }
         if (filteredDnses.isEmpty()) return;
 
-        final byte ND_OPTION_RDNSS = 25;
-        final byte RDNSS_NUM_8OCTETS = asByte(dnses.size() * 2 + 1);
-        ra.put(ND_OPTION_RDNSS)
-          .put(RDNSS_NUM_8OCTETS)
-          .putShort(asShort(0))
-          .putInt(lifetime);
+        final byte nd_option_rdnss = 25;
+        final byte rdnss_num_8octets = asByte(dnses.size() * 2 + 1);
+        ra.put(nd_option_rdnss)
+            .put(rdnss_num_8octets)
+            .putShort(asShort(0))
+            .putInt(lifetime);
 
         for (Inet6Address dns : filteredDnses) {
             // NOTE: If the full of list DNS servers doesn't fit in the packet,
@@ -585,7 +602,7 @@
     }
 
     private boolean createSocket() {
-        final int SEND_TIMEOUT_MS = 300;
+        final int send_timout_ms = 300;
 
         final int oldTag = TrafficStats.getAndSetThreadStatsTag(
                 TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR);
@@ -593,7 +610,7 @@
             mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
             // Setting SNDTIMEO is purely for defensive purposes.
             Os.setsockoptTimeval(
-                    mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(SEND_TIMEOUT_MS));
+                    mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
             Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name);
             NetworkUtils.protectFromVpn(mSocket);
             NetworkUtils.setupRaSocket(mSocket, mInterface.index);
@@ -611,7 +628,7 @@
         if (mSocket != null) {
             try {
                 IoBridge.closeAndSignalBlockedThreads(mSocket);
-            } catch (IOException ignored) {}
+            } catch (IOException ignored) { }
         }
         mSocket = null;
     }
@@ -627,9 +644,9 @@
         }
 
         final InetAddress destip = dest.getAddress();
-        return (destip instanceof Inet6Address) &&
-                destip.isLinkLocalAddress() &&
-               (((Inet6Address) destip).getScopeId() == mInterface.index);
+        return (destip instanceof Inet6Address)
+               && destip.isLinkLocalAddress()
+               && (((Inet6Address) destip).getScopeId() == mInterface.index);
     }
 
     private void maybeSendRA(InetSocketAddress dest) {
@@ -654,11 +671,11 @@
     }
 
     private final class UnicastResponder extends Thread {
-        private final InetSocketAddress solicitor = new InetSocketAddress();
+        private final InetSocketAddress mSolicitor = new InetSocketAddress();
         // The recycled buffer for receiving Router Solicitations from clients.
         // If the RS is larger than IPV6_MIN_MTU the packets are truncated.
         // This is fine since currently only byte 0 is examined anyway.
-        private final byte mSolication[] = new byte[IPV6_MIN_MTU];
+        private final byte[] mSolicitation = new byte[IPV6_MIN_MTU];
 
         @Override
         public void run() {
@@ -666,9 +683,9 @@
                 try {
                     // Blocking receive.
                     final int rval = Os.recvfrom(
-                            mSocket, mSolication, 0, mSolication.length, 0, solicitor);
+                            mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor);
                     // Do the least possible amount of validation.
-                    if (rval < 1 || mSolication[0] != ICMPV6_ND_ROUTER_SOLICIT) {
+                    if (rval < 1 || mSolicitation[0] != ICMPV6_ND_ROUTER_SOLICIT) {
                         continue;
                     }
                 } catch (ErrnoException | SocketException e) {
@@ -678,7 +695,7 @@
                     continue;
                 }
 
-                maybeSendRA(solicitor);
+                maybeSendRA(mSolicitor);
             }
         }
     }
diff --git a/services/net/java/android/net/util/InterfaceSet.java b/packages/Tethering/src/android/net/util/InterfaceSet.java
similarity index 95%
rename from services/net/java/android/net/util/InterfaceSet.java
rename to packages/Tethering/src/android/net/util/InterfaceSet.java
index 9f26fa1..7589787 100644
--- a/services/net/java/android/net/util/InterfaceSet.java
+++ b/packages/Tethering/src/android/net/util/InterfaceSet.java
@@ -47,6 +47,6 @@
     public boolean equals(Object obj) {
         return obj != null
                 && obj instanceof InterfaceSet
-                && ifnames.equals(((InterfaceSet)obj).ifnames);
+                && ifnames.equals(((InterfaceSet) obj).ifnames);
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
similarity index 96%
rename from services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
rename to packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index a1b94ca..7709727 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -82,7 +82,7 @@
         "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
     };
 
-    private final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
+    private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
 
     public final String[] tetherableUsbRegexs;
     public final String[] tetherableWifiRegexs;
@@ -133,10 +133,12 @@
         configLog.log(toString());
     }
 
+    /** Check whether input interface belong to usb.*/
     public boolean isUsb(String iface) {
         return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
     }
 
+    /** Check whether input interface belong to wifi.*/
     public boolean isWifi(String iface) {
         return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
     }
@@ -146,18 +148,22 @@
         return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs);
     }
 
+    /** Check whether using legacy mode for wifi P2P. */
     public boolean isWifiP2pLegacyTetheringMode() {
         return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0);
     }
 
+    /** Check whether input interface belong to bluetooth.*/
     public boolean isBluetooth(String iface) {
         return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
     }
 
+    /** Check whether no ui entitlement application is available.*/
     public boolean hasMobileHotspotProvisionApp() {
         return !TextUtils.isEmpty(provisioningAppNoUi);
     }
 
+    /** Does the dumping.*/
     public void dump(PrintWriter pw) {
         pw.print("subId: ");
         pw.println(subId);
@@ -186,6 +192,7 @@
         pw.println(enableLegacyDhcpServer);
     }
 
+    /** Returns the string representation of this object.*/
     public String toString() {
         final StringJoiner sj = new StringJoiner(" ");
         sj.add(String.format("subId:%d", subId));
@@ -210,7 +217,7 @@
 
         if (values != null) {
             final StringJoiner sj = new StringJoiner(", ", "[", "]");
-            for (String value : values) { sj.add(value); }
+            for (String value : values) sj.add(value);
             pw.print(sj.toString());
         } else {
             pw.print("null");
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
new file mode 100644
index 0000000..da62107
--- /dev/null
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -0,0 +1,50 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test {
+    name: "TetheringTests",
+    certificate: "platform",
+    srcs: ["src/**/*.java"],
+    test_suites: ["device-tests"],
+    static_libs: [
+        "androidx.test.rules",
+        "frameworks-base-testutils",
+        "mockito-target-extended-minus-junit4",
+        "TetheringApiCurrentLib",
+        "testables",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+    ],
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+}
+
+// This group would be removed when tethering migration is done.
+filegroup {
+    name: "tethering-tests-src",
+    srcs: [
+        "src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
+        "src/android/net/dhcp/DhcpServingParamsParcelExtTest.java",
+        "src/android/net/ip/IpServerTest.java",
+        "src/android/net/util/InterfaceSetTest.java",
+    ],
+}
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
new file mode 100644
index 0000000..049ff6d
--- /dev/null
+++ b/packages/Tethering/tests/unit/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.tethering.tests.unit">
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.tethering.tests.unit"
+        android:label="Tethering service tests">
+    </instrumentation>
+</manifest>
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
similarity index 100%
rename from tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
rename to packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
similarity index 99%
rename from tests/net/java/android/net/ip/IpServerTest.java
rename to packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index b6ccebb..4358cd6 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -183,7 +183,7 @@
     @Test
     public void shouldDoNothingUntilRequested() throws Exception {
         initStateMachine(TETHERING_BLUETOOTH);
-        final int [] NOOP_COMMANDS = {
+        final int [] noOp_commands = {
             IpServer.CMD_TETHER_UNREQUESTED,
             IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
             IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
@@ -192,7 +192,7 @@
             IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
             IpServer.CMD_TETHER_CONNECTION_CHANGED
         };
-        for (int command : NOOP_COMMANDS) {
+        for (int command : noOp_commands) {
             // None of these commands should trigger us to request action from
             // the rest of the system.
             dispatchCommand(command);
diff --git a/tests/net/java/android/net/util/InterfaceSetTest.java b/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
similarity index 100%
rename from tests/net/java/android/net/util/InterfaceSetTest.java
rename to packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
similarity index 91%
rename from tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
rename to packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index e282963..9f9221f 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -24,7 +24,12 @@
 import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
+import static com.android.internal.R.array.config_tether_bluetooth_regexs;
+import static com.android.internal.R.array.config_tether_dhcp_range;
 import static com.android.internal.R.array.config_tether_upstream_types;
+import static com.android.internal.R.array.config_tether_usb_regexs;
+import static com.android.internal.R.array.config_tether_wifi_regexs;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -86,7 +91,9 @@
         }
 
         @Override
-        public Resources getResources() { return mResources; }
+        public Resources getResources() {
+            return mResources;
+        }
 
         @Override
         public Object getSystemService(String name) {
@@ -105,17 +112,13 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
-                .thenReturn(new String[0]);
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
-                .thenReturn(new String[0]);
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
+        when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]);
+        when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]);
+        when(mResources.getStringArray(config_tether_wifi_regexs))
                 .thenReturn(new String[]{ "test_wlan\\d" });
-        when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
-                .thenReturn(new String[0]);
+        when(mResources.getStringArray(config_tether_bluetooth_regexs)).thenReturn(new String[0]);
         when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
-        when(mResources.getStringArray(
-                com.android.internal.R.array.config_mobile_hotspot_provision_app))
+        when(mResources.getStringArray(config_mobile_hotspot_provision_app))
                 .thenReturn(new String[0]);
         mContentResolver = new MockContentResolver();
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -297,19 +300,16 @@
 
     private void setUpResourceForSubId() {
         when(mResourcesForSubId.getStringArray(
-                com.android.internal.R.array.config_tether_dhcp_range)).thenReturn(new String[0]);
+                config_tether_dhcp_range)).thenReturn(new String[0]);
         when(mResourcesForSubId.getStringArray(
-                com.android.internal.R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
+                config_tether_usb_regexs)).thenReturn(new String[0]);
         when(mResourcesForSubId.getStringArray(
-                com.android.internal.R.array.config_tether_wifi_regexs))
-                .thenReturn(new String[]{ "test_wlan\\d" });
+                config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
         when(mResourcesForSubId.getStringArray(
-                com.android.internal.R.array.config_tether_bluetooth_regexs))
-                .thenReturn(new String[0]);
+                config_tether_bluetooth_regexs)).thenReturn(new String[0]);
         when(mResourcesForSubId.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
         when(mResourcesForSubId.getStringArray(
-                com.android.internal.R.array.config_mobile_hotspot_provision_app))
-                .thenReturn(PROVISIONING_APP_NAME);
+                config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
     }
 
 }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index ffbf1ae..ef071a4 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -249,6 +249,8 @@
     NOTE_NETWORK_LOGGED_IN = 744;
     // A partial connectivity network was detected during network validation
     NOTE_NETWORK_PARTIAL_CONNECTIVITY = 745;
+    // Private DNS is broken in strict mode
+    NOTE_NETWORK_PRIVATE_DNS_BROKEN = 746;
 
     // Notify the user that their work profile has been deleted
     // Package: android
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0bb72cb..ce0e9e7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -26,6 +26,7 @@
 import static android.net.ConnectivityManager.TYPE_VPN;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
@@ -528,6 +529,15 @@
     private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45;
 
     /**
+     * Event for NetworkMonitor to inform ConnectivityService that the probe status has changed.
+     * Both of the arguments are bitmasks, and the value of bits come from
+     * INetworkMonitor.NETWORK_VALIDATION_PROBE_*.
+     * arg1 = A bitmask to describe which probes are completed.
+     * arg2 = A bitmask to describe which probes are successful.
+     */
+    public static final int EVENT_PROBE_STATUS_CHANGED = 46;
+
+    /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
      * should be shown.
      */
@@ -2663,6 +2673,41 @@
             switch (msg.what) {
                 default:
                     return false;
+                case EVENT_PROBE_STATUS_CHANGED: {
+                    final Integer netId = (Integer) msg.obj;
+                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+                    if (nai == null) {
+                        break;
+                    }
+                    final boolean probePrivateDnsCompleted =
+                            ((msg.arg1 & NETWORK_VALIDATION_PROBE_PRIVDNS) != 0);
+                    final boolean privateDnsBroken =
+                            ((msg.arg2 & NETWORK_VALIDATION_PROBE_PRIVDNS) == 0);
+                    if (probePrivateDnsCompleted) {
+                        if (nai.networkCapabilities.isPrivateDnsBroken() != privateDnsBroken) {
+                            nai.networkCapabilities.setPrivateDnsBroken(privateDnsBroken);
+                            final int oldScore = nai.getCurrentScore();
+                            updateCapabilities(oldScore, nai, nai.networkCapabilities);
+                        }
+                        // Only show the notification when the private DNS is broken and the
+                        // PRIVATE_DNS_BROKEN notification hasn't shown since last valid.
+                        if (privateDnsBroken && !nai.networkMisc.hasShownBroken) {
+                            showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN);
+                        }
+                        nai.networkMisc.hasShownBroken = privateDnsBroken;
+                    } else if (nai.networkCapabilities.isPrivateDnsBroken()) {
+                        // If probePrivateDnsCompleted is false but nai.networkCapabilities says
+                        // private DNS is broken, it means this network is being reevaluated.
+                        // Either probing private DNS is not necessary any more or it hasn't been
+                        // done yet. In either case, the networkCapabilities should be updated to
+                        // reflect the new status.
+                        nai.networkCapabilities.setPrivateDnsBroken(false);
+                        final int oldScore = nai.getCurrentScore();
+                        updateCapabilities(oldScore, nai, nai.networkCapabilities);
+                        nai.networkMisc.hasShownBroken = false;
+                    }
+                    break;
+                }
                 case EVENT_NETWORK_TESTED: {
                     final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                     if (nai == null) break;
@@ -2705,14 +2750,20 @@
                         if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
                         if (valid) {
                             handleFreshlyValidatedNetwork(nai);
-                            // Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET
-                            // notifications if network becomes valid.
+                            // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
+                            // LOST_INTERNET notifications if network becomes valid.
                             mNotifier.clearNotification(nai.network.netId,
                                     NotificationType.NO_INTERNET);
                             mNotifier.clearNotification(nai.network.netId,
                                     NotificationType.LOST_INTERNET);
                             mNotifier.clearNotification(nai.network.netId,
                                     NotificationType.PARTIAL_CONNECTIVITY);
+                            mNotifier.clearNotification(nai.network.netId,
+                                    NotificationType.PRIVATE_DNS_BROKEN);
+                            // If network becomes valid, the hasShownBroken should be reset for
+                            // that network so that the notification will be fired when the private
+                            // DNS is broken again.
+                            nai.networkMisc.hasShownBroken = false;
                         }
                     } else if (partialConnectivityChanged) {
                         updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -2863,6 +2914,13 @@
         }
 
         @Override
+        public void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) {
+            mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+                    EVENT_PROBE_STATUS_CHANGED,
+                    probesCompleted, probesSucceeded, new Integer(mNetId)));
+        }
+
+        @Override
         public void showProvisioningNotification(String action, String packageName) {
             final Intent intent = new Intent(action);
             intent.setPackage(packageName);
@@ -3679,6 +3737,11 @@
                 // High priority because it is only displayed for explicitly selected networks.
                 highPriority = true;
                 break;
+            case PRIVATE_DNS_BROKEN:
+                action = Settings.ACTION_WIRELESS_SETTINGS;
+                // High priority because we should let user know why there is no internet.
+                highPriority = true;
+                break;
             case LOST_INTERNET:
                 action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
                 // High priority because it could help the user avoid unexpected data usage.
@@ -3696,7 +3759,7 @@
         }
 
         Intent intent = new Intent(action);
-        if (type != NotificationType.LOGGED_IN) {
+        if (type != NotificationType.LOGGED_IN && type != NotificationType.PRIVATE_DNS_BROKEN) {
             intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             intent.setClassName("com.android.settings",
@@ -5162,6 +5225,13 @@
         ns.assertValidFromUid(Binder.getCallingUid());
     }
 
+    private void ensureValid(NetworkCapabilities nc) {
+        ensureValidNetworkSpecifier(nc);
+        if (nc.isPrivateDnsBroken()) {
+            throw new IllegalArgumentException("Can't request broken private DNS");
+        }
+    }
+
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
             Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
@@ -5195,7 +5265,7 @@
         if (timeoutMs < 0) {
             throw new IllegalArgumentException("Bad timeout specified");
         }
-        ensureValidNetworkSpecifier(networkCapabilities);
+        ensureValid(networkCapabilities);
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId(), type);
@@ -5337,7 +5407,7 @@
         // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
         // can't request networks.
         restrictBackgroundRequestForCaller(nc);
-        ensureValidNetworkSpecifier(nc);
+        ensureValid(nc);
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
@@ -5355,7 +5425,7 @@
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
         }
-        ensureValidNetworkSpecifier(networkCapabilities);
+        ensureValid(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
                 Binder.getCallingPid(), Binder.getCallingUid());
 
@@ -5841,6 +5911,7 @@
         } else {
             newNc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
         }
+        newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
 
         return newNc;
     }
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 18009e1..190e6cf 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -115,7 +115,8 @@
     }
 
     @Override
-    public boolean startInstallation(long systemSize, long userdataSize) throws RemoteException {
+    public boolean startInstallation(String name, long size, boolean readOnly)
+            throws RemoteException {
         // priority from high to low: sysprop -> sdcard -> /data
         String path = SystemProperties.get("os.aot.path");
         if (path.isEmpty()) {
@@ -137,11 +138,18 @@
             }
             Slog.i(TAG, "startInstallation -> " + path);
         }
+        IGsiService service = getGsiService();
         GsiInstallParams installParams = new GsiInstallParams();
         installParams.installDir = path;
-        installParams.gsiSize = systemSize;
-        installParams.userdataSize = userdataSize;
-        return getGsiService().beginGsiInstall(installParams) == 0;
+        installParams.name = name;
+        installParams.size = size;
+        installParams.wipe = readOnly;
+        installParams.readOnly = readOnly;
+        if (service.beginGsiInstall(installParams) != 0) {
+            Slog.i(TAG, "Failed to install " + name);
+            return false;
+        }
+        return true;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e26cbc4..055a4bd 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1018,7 +1018,14 @@
 
         synchronized (mAudioPolicies) {
             for (AudioPolicyProxy policy : mAudioPolicies.values()) {
-                policy.connectMixes();
+                final int status = policy.connectMixes();
+                if (status != AudioSystem.SUCCESS) {
+                    // note that PERMISSION_DENIED may also indicate trouble getting to APService
+                    Log.e(TAG, "onAudioServerDied: error "
+                            + AudioSystem.audioSystemErrorToString(status)
+                            + " when connecting mixes for policy " + policy.toLogFriendlyString());
+                    policy.release();
+                }
             }
         }
 
@@ -7007,16 +7014,8 @@
         }
 
         public void binderDied() {
-            synchronized (mAudioPolicies) {
-                Log.i(TAG, "audio policy " + mPolicyCallback + " died");
-                release();
-                mAudioPolicies.remove(mPolicyCallback.asBinder());
-            }
-            if (mIsVolumeController) {
-                synchronized (mExtVolumeControllerLock) {
-                    mExtVolumeController = null;
-                }
-            }
+            Log.i(TAG, "audio policy " + mPolicyCallback + " died");
+            release();
         }
 
         String getRegistrationId() {
@@ -7040,9 +7039,20 @@
                     Log.e(TAG, "Fail to unregister Audiopolicy callback from MediaProjection");
                 }
             }
+            if (mIsVolumeController) {
+                synchronized (mExtVolumeControllerLock) {
+                    mExtVolumeController = null;
+                }
+            }
             final long identity = Binder.clearCallingIdentity();
             AudioSystem.registerPolicyMixes(mMixes, false);
             Binder.restoreCallingIdentity(identity);
+            synchronized (mAudioPolicies) {
+                mAudioPolicies.remove(mPolicyCallback.asBinder());
+            }
+            try {
+                mPolicyCallback.notifyUnregistration();
+            } catch (RemoteException e) { }
         }
 
         boolean hasMixAffectingUsage(int usage, int excludedFlags) {
@@ -7093,7 +7103,7 @@
             }
         }
 
-        int connectMixes() {
+        @AudioSystem.AudioSystemError int connectMixes() {
             final long identity = Binder.clearCallingIdentity();
             int status = AudioSystem.registerPolicyMixes(mMixes, true);
             Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 8a7dcc1..9ac9955 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.util.StatsLog;
 
@@ -53,8 +54,8 @@
     }
 
     @Override
-    public void reportChangeByPackageName(long changeId, String packageName) {
-        ApplicationInfo appInfo = getApplicationInfo(packageName);
+    public void reportChangeByPackageName(long changeId, String packageName, int userId) {
+        ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
         if (appInfo == null) {
             return;
         }
@@ -79,8 +80,8 @@
     }
 
     @Override
-    public boolean isChangeEnabledByPackageName(long changeId, String packageName) {
-        ApplicationInfo appInfo = getApplicationInfo(packageName);
+    public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+        ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
         if (appInfo == null) {
             return true;
         }
@@ -95,7 +96,8 @@
         }
         boolean enabled = true;
         for (String packageName : packages) {
-            enabled = enabled && isChangeEnabledByPackageName(changeId, packageName);
+            enabled = enabled && isChangeEnabledByPackageName(changeId, packageName,
+                    UserHandle.getUserId(uid));
         }
         return enabled;
     }
@@ -126,9 +128,9 @@
         mChangeReporter.resetReportedChanges(appInfo.uid);
     }
 
-    private ApplicationInfo getApplicationInfo(String packageName) {
+    private ApplicationInfo getApplicationInfo(String packageName, int userId) {
         try {
-            return mContext.getPackageManager().getApplicationInfo(packageName, 0);
+            return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0, userId);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.e(TAG, "No installed package " + packageName);
         }
diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java
new file mode 100644
index 0000000..85dfbf4
--- /dev/null
+++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.compat;
+
+import com.android.internal.compat.IPlatformCompatNative;
+
+/**
+ * @hide
+ */
+public class PlatformCompatNative extends IPlatformCompatNative.Stub {
+    private final PlatformCompat mPlatformCompat;
+
+    public PlatformCompatNative(PlatformCompat platformCompat) {
+        mPlatformCompat = platformCompat;
+    }
+
+    @Override
+    public void reportChangeByPackageName(long changeId, String packageName, int userId) {
+        mPlatformCompat.reportChangeByPackageName(changeId, packageName, userId);
+    }
+
+    @Override
+    public void reportChangeByUid(long changeId, int uid) {
+        mPlatformCompat.reportChangeByUid(changeId, uid);
+    }
+
+    @Override
+    public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+        return mPlatformCompat.isChangeEnabledByPackageName(changeId, packageName, userId);
+    }
+
+    @Override
+    public boolean isChangeEnabledByUid(long changeId, int uid) {
+        return mPlatformCompat.isChangeEnabledByUid(changeId, uid);
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 077c405..d13e675 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -53,7 +53,8 @@
         NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET),
         LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN),
         PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY),
-        SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN);
+        SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN),
+        PRIVATE_DNS_BROKEN(SystemMessage.NOTE_NETWORK_PRIVATE_DNS_BROKEN);
 
         public final int eventId;
 
@@ -175,13 +176,23 @@
         }
 
         Resources r = Resources.getSystem();
-        CharSequence title;
-        CharSequence details;
+        final CharSequence title;
+        final CharSequence details;
         int icon = getIcon(transportType, notifyType);
         if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet,
                     WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
             details = r.getString(R.string.wifi_no_internet_detailed);
+        } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
+            if (transportType == TRANSPORT_CELLULAR) {
+                title = r.getString(R.string.mobile_no_internet);
+            } else if (transportType == TRANSPORT_WIFI) {
+                title = r.getString(R.string.wifi_no_internet,
+                        WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
+            } else {
+                title = r.getString(R.string.other_networks_no_internet);
+            }
+            details = r.getString(R.string.private_dns_broken_detailed);
         } else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
                 && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.network_partial_connectivity,
@@ -357,8 +368,10 @@
         }
         switch (t) {
             case SIGN_IN:
-                return 5;
+                return 6;
             case PARTIAL_CONNECTIVITY:
+                return 5;
+            case PRIVATE_DNS_BROKEN:
                 return 4;
             case NO_INTERNET:
                 return 3;
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index 7bdc8a3..9dbbf16 100644
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
 import android.util.Slog;
 import android.util.TimestampedValue;
@@ -48,9 +48,8 @@
     // @NonNull after initialize()
     private Callback mCallback;
 
-    // NITZ state.
-    @Nullable private TimestampedValue<Long> mLastNitzTime;
-
+    // Last phone suggestion.
+    @Nullable private PhoneTimeSuggestion mLastPhoneSuggestion;
 
     // Information about the last time signal received: Used when toggling auto-time.
     @Nullable private TimestampedValue<Long> mLastSystemClockTime;
@@ -65,46 +64,40 @@
     }
 
     @Override
-    public void suggestTime(@NonNull TimeSignal timeSignal) {
-        if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) {
-            Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal);
-            return;
-        }
-
+    public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
         // NITZ logic
 
-        TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime();
-        boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime);
-        if (!nitzTimeIsValid) {
+        boolean timeSuggestionIsValid =
+                validateNewPhoneSuggestion(timeSuggestion, mLastPhoneSuggestion);
+        if (!timeSuggestionIsValid) {
             return;
         }
         // Always store the last NITZ value received, regardless of whether we go on to use it to
         // update the system clock. This is so that we can validate future NITZ signals.
-        mLastNitzTime = newNitzUtcTime;
+        mLastPhoneSuggestion = timeSuggestion;
 
         // System clock update logic.
 
         // Historically, Android has sent a telephony broadcast only when setting the time using
         // NITZ.
-        final boolean sendNetworkBroadcast =
-                TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId());
+        final boolean sendNetworkBroadcast = true;
 
-        final TimestampedValue<Long> newUtcTime = newNitzUtcTime;
+        final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
         setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast);
     }
 
-    private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime,
-            TimestampedValue<Long> lastNitzTime) {
+    private static boolean validateNewPhoneSuggestion(@NonNull PhoneTimeSuggestion newSuggestion,
+            @Nullable PhoneTimeSuggestion lastSuggestion) {
 
-        if (lastNitzTime != null) {
-            long referenceTimeDifference =
-                    TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime);
+        if (lastSuggestion != null) {
+            long referenceTimeDifference = TimestampedValue.referenceTimeDifference(
+                    newSuggestion.getUtcTime(), lastSuggestion.getUtcTime());
             if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
                 // Out of order or bogus.
                 Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received."
                         + " referenceTimeDifference=" + referenceTimeDifference
-                        + " lastNitzTime=" + lastNitzTime
-                        + " newNitzUtcTime=" + newNitzUtcTime);
+                        + " lastSuggestion=" + lastSuggestion
+                        + " newSuggestion=" + newSuggestion);
                 return false;
             }
         }
@@ -182,7 +175,7 @@
 
     @Override
     public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
-        pw.println("mLastNitzTime=" + mLastNitzTime);
+        pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
         pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet);
         pw.println("mLastSystemClockTime=" + mLastSystemClockTime);
         pw.println("mLastSystemClockTimeSendNetworkBroadcast="
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 9c83000..ee42279 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.timedetector.ITimeDetectorService;
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -96,14 +96,14 @@
     }
 
     @Override
-    public void suggestTime(@NonNull TimeSignal timeSignal) {
+    public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSignal) {
         enforceSetTimePermission();
         Objects.requireNonNull(timeSignal);
 
         long idToken = Binder.clearCallingIdentity();
         try {
             synchronized (mStrategyLock) {
-                mTimeDetectorStrategy.suggestTime(timeSignal);
+                mTimeDetectorStrategy.suggestPhoneTime(timeSignal);
             }
         } finally {
             Binder.restoreCallingIdentity(idToken);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index e050865..7c2a945 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
 import android.util.TimestampedValue;
 
@@ -72,7 +72,7 @@
     void initialize(@NonNull Callback callback);
 
     /** Process the suggested time. */
-    void suggestTime(@NonNull TimeSignal timeSignal);
+    void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion);
 
     /** Handle the auto-time setting being toggled on or off. */
     void handleAutoTimeDetectionToggle(boolean enabled);
diff --git a/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java b/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java
new file mode 100644
index 0000000..852f707
--- /dev/null
+++ b/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.updates;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.Slog;
+
+/**
+ * Emergency Number Database Install Receiver.
+ */
+public class EmergencyNumberDbInstallReceiver extends ConfigUpdateInstallReceiver {
+
+    private static final String TAG = "EmergencyNumberDbInstallReceiver";
+
+    public EmergencyNumberDbInstallReceiver() {
+        super("/data/misc/emergencynumberdb", "emergency_number_db", "metadata/", "version");
+    }
+
+    @Override
+    protected void postInstall(Context context, Intent intent) {
+        Slog.i(TAG, "Emergency number database is updated in file partition");
+        // TODO Send a notification to EmergencyNumberTracker for updating of emergency number db.
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5c1dfef..f465855 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -89,6 +89,7 @@
 import com.android.server.camera.CameraServiceProxy;
 import com.android.server.clipboard.ClipboardService;
 import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.PlatformCompatNative;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.coverage.CoverageService;
@@ -639,8 +640,10 @@
         // Platform compat service is used by ActivityManagerService, PackageManagerService, and
         // possibly others in the future. b/135010838.
         traceBeginAndSlog("PlatformCompat");
-        ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE,
-                new PlatformCompat(mSystemContext));
+        PlatformCompat platformCompat = new PlatformCompat(mSystemContext);
+        ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
+        ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
+                new PlatformCompatNative(platformCompat));
         traceEnd();
 
         // Wait for installd to finish starting up so that it has a chance to
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 1ca96ed..08cdbfc 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,10 +1,14 @@
 java_library_static {
     name: "services.net",
-    srcs: ["java/**/*.java"],
+    srcs: [
+        ":tethering-services-srcs",
+        "java/**/*.java",
+    ],
     static_libs: [
         "dnsresolver_aidl_interface-V2-java",
         "netd_aidl_interface-java",
         "networkstack-client",
+        "tethering-client",
     ],
 }
 
@@ -17,3 +21,17 @@
         "java/android/net/netlink/*.java",
     ],
 }
+
+filegroup {
+    name: "services-tethering-shared-srcs",
+    srcs: [
+        ":framework-annotations",
+        "java/android/net/ConnectivityModuleConnector.java",
+        "java/android/net/NetworkStackClient.java",
+        "java/android/net/ip/InterfaceController.java",
+        "java/android/net/util/InterfaceParams.java",
+        "java/android/net/util/NetdService.java",
+        "java/android/net/util/NetworkConstants.java",
+        "java/android/net/util/SharedLog.java"
+    ],
+}
diff --git a/services/net/java/android/net/netlink/InetDiagMessage.java b/services/net/java/android/net/netlink/InetDiagMessage.java
index 31a2556..ca07630 100644
--- a/services/net/java/android/net/netlink/InetDiagMessage.java
+++ b/services/net/java/android/net/netlink/InetDiagMessage.java
@@ -26,6 +26,7 @@
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.NETLINK_INET_DIAG;
 
+import android.annotation.Nullable;
 import android.net.util.SocketUtils;
 import android.system.ErrnoException;
 import android.util.Log;
@@ -53,7 +54,35 @@
     private static final int TIMEOUT_MS = 500;
 
     public static byte[] InetDiagReqV2(int protocol, InetSocketAddress local,
-                                       InetSocketAddress remote, int family, short flags) {
+            InetSocketAddress remote, int family, short flags) {
+        return InetDiagReqV2(protocol, local, remote, family, flags, 0 /* pad */,
+                0 /* idiagExt */, StructInetDiagReqV2.INET_DIAG_REQ_V2_ALL_STATES);
+    }
+
+    /**
+     * Construct an inet_diag_req_v2 message. This method will throw {@code NullPointerException}
+     * if local and remote are not both null or both non-null.
+     *
+     * @param protocol the request protocol type. This should be set to one of IPPROTO_TCP,
+     *                 IPPROTO_UDP, or IPPROTO_UDPLITE.
+     * @param local local socket address of the target socket. This will be packed into a
+     *              {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of
+     *              local or remote address is null.
+     * @param remote remote socket address of the target socket. This will be packed into a
+     *              {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of
+     *              local or remote address is null.
+     * @param family the ip family of the request message. This should be set to either AF_INET or
+     *               AF_INET6 for IPv4 or IPv6 sockets respectively.
+     * @param flags message flags. See &lt;linux_src&gt;/include/uapi/linux/netlink.h.
+     * @param pad for raw socket protocol specification.
+     * @param idiagExt a set of flags defining what kind of extended information to report.
+     * @param state a bit mask that defines a filter of socket states.
+     *
+     * @return bytes array representation of the message
+     **/
+    public static byte[] InetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
+            @Nullable InetSocketAddress remote, int family, short flags, int pad, int idiagExt,
+            int state) throws NullPointerException {
         final byte[] bytes = new byte[StructNlMsgHdr.STRUCT_SIZE + StructInetDiagReqV2.STRUCT_SIZE];
         final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
         byteBuffer.order(ByteOrder.nativeOrder());
@@ -63,9 +92,9 @@
         nlMsgHdr.nlmsg_type = SOCK_DIAG_BY_FAMILY;
         nlMsgHdr.nlmsg_flags = flags;
         nlMsgHdr.pack(byteBuffer);
+        final StructInetDiagReqV2 inetDiagReqV2 =
+                new StructInetDiagReqV2(protocol, local, remote, family, pad, idiagExt, state);
 
-        final StructInetDiagReqV2 inetDiagReqV2 = new StructInetDiagReqV2(protocol, local, remote,
-                family);
         inetDiagReqV2.pack(byteBuffer);
         return bytes;
     }
diff --git a/services/net/java/android/net/netlink/StructInetDiagReqV2.java b/services/net/java/android/net/netlink/StructInetDiagReqV2.java
index 49a9325..2268113 100644
--- a/services/net/java/android/net/netlink/StructInetDiagReqV2.java
+++ b/services/net/java/android/net/netlink/StructInetDiagReqV2.java
@@ -16,10 +16,10 @@
 
 package android.net.netlink;
 
-import static java.nio.ByteOrder.BIG_ENDIAN;
+import android.annotation.Nullable;
+
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
 
 /**
  * struct inet_diag_req_v2
@@ -40,41 +40,58 @@
 public class StructInetDiagReqV2 {
     public static final int STRUCT_SIZE = 8 + StructInetDiagSockId.STRUCT_SIZE;
 
-    private final byte sdiag_family;
-    private final byte sdiag_protocol;
-    private final StructInetDiagSockId id;
-    private final int INET_DIAG_REQ_V2_ALL_STATES = (int) 0xffffffff;
-
+    private final byte mSdiagFamily;
+    private final byte mSdiagProtocol;
+    private final byte mIdiagExt;
+    private final byte mPad;
+    private final StructInetDiagSockId mId;
+    private final int mState;
+    public static final int INET_DIAG_REQ_V2_ALL_STATES = (int) 0xffffffff;
 
     public StructInetDiagReqV2(int protocol, InetSocketAddress local, InetSocketAddress remote,
-                               int family) {
-        sdiag_family = (byte) family;
-        sdiag_protocol = (byte) protocol;
-        id = new StructInetDiagSockId(local, remote);
+            int family) {
+        this(protocol, local, remote, family, 0 /* pad */, 0 /* extension */,
+                INET_DIAG_REQ_V2_ALL_STATES);
+    }
+
+    public StructInetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
+            @Nullable InetSocketAddress remote, int family, int pad, int extension, int state)
+            throws NullPointerException {
+        mSdiagFamily = (byte) family;
+        mSdiagProtocol = (byte) protocol;
+        // Request for all sockets if no specific socket is requested. Specify the local and remote
+        // socket address information for target request socket.
+        if ((local == null) != (remote == null)) {
+            throw new NullPointerException("Local and remote must be both null or both non-null");
+        }
+        mId = ((local != null && remote != null) ? new StructInetDiagSockId(local, remote) : null);
+        mPad = (byte) pad;
+        mIdiagExt = (byte) extension;
+        mState = state;
     }
 
     public void pack(ByteBuffer byteBuffer) {
         // The ByteOrder must have already been set by the caller.
-        byteBuffer.put((byte) sdiag_family);
-        byteBuffer.put((byte) sdiag_protocol);
-        byteBuffer.put((byte) 0);
-        byteBuffer.put((byte) 0);
-        byteBuffer.putInt(INET_DIAG_REQ_V2_ALL_STATES);
-        id.pack(byteBuffer);
+        byteBuffer.put((byte) mSdiagFamily);
+        byteBuffer.put((byte) mSdiagProtocol);
+        byteBuffer.put((byte) mIdiagExt);
+        byteBuffer.put((byte) mPad);
+        byteBuffer.putInt(mState);
+        if (mId != null) mId.pack(byteBuffer);
     }
 
     @Override
     public String toString() {
-        final String familyStr = NetlinkConstants.stringForAddressFamily(sdiag_family);
-        final String protocolStr = NetlinkConstants.stringForAddressFamily(sdiag_protocol);
+        final String familyStr = NetlinkConstants.stringForAddressFamily(mSdiagFamily);
+        final String protocolStr = NetlinkConstants.stringForAddressFamily(mSdiagProtocol);
 
         return "StructInetDiagReqV2{ "
                 + "sdiag_family{" + familyStr + "}, "
                 + "sdiag_protocol{" + protocolStr + "}, "
-                + "idiag_ext{" + 0 + ")}, "
-                + "pad{" + 0 + "}, "
-                + "idiag_states{" + Integer.toHexString(INET_DIAG_REQ_V2_ALL_STATES) + "}, "
-                + id.toString()
+                + "idiag_ext{" + mIdiagExt + ")}, "
+                + "pad{" + mPad + "}, "
+                + "idiag_states{" + Integer.toHexString(mState) + "}, "
+                + ((mId != null) ? mId.toString() : "inet_diag_sockid=null")
                 + "}";
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
index 4a9dd97..0605d9e 100644
--- a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
@@ -36,7 +36,7 @@
     public void test1() {
         assertTrue("dynamic_system service available", mService != null);
         try {
-            mService.startInstallation(1 << 20, 8 << 30);
+            mService.startInstallation("userdata", 8L << 30, false);
             fail("DynamicSystemService did not throw SecurityException as expected");
         } catch (SecurityException e) {
             // expected
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
index 9e00077..d797955 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
@@ -23,7 +23,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
 import android.icu.util.Calendar;
 import android.icu.util.GregorianCalendar;
@@ -45,6 +45,8 @@
             .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
             .build();
 
+    private static final int ARBITRARY_PHONE_ID = 123456;
+
     private Script mScript;
 
     @Before
@@ -53,30 +55,32 @@
     }
 
     @Test
-    public void testSuggestTime_nitz_timeDetectionEnabled() {
+    public void testSuggestPhoneTime_nitz_timeDetectionEnabled() {
         Scenario scenario = SCENARIO_1;
         mScript.pokeFakeClocks(scenario)
                 .pokeTimeDetectionEnabled(true);
 
-        TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+        PhoneTimeSuggestion timeSuggestion =
+                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
         final int clockIncrement = 1000;
         long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
 
         mScript.simulateTimePassing(clockIncrement)
-                .simulateTimeSignalReceived(timeSignal)
+                .simulatePhoneTimeSuggestion(timeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis);
     }
 
     @Test
-    public void testSuggestTime_systemClockThreshold() {
+    public void testSuggestPhoneTime_systemClockThreshold() {
         Scenario scenario = SCENARIO_1;
         final int systemClockUpdateThresholdMillis = 1000;
         mScript.pokeFakeClocks(scenario)
                 .pokeThresholds(systemClockUpdateThresholdMillis)
                 .pokeTimeDetectionEnabled(true);
 
-        TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
-        TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+        PhoneTimeSuggestion timeSuggestion1 =
+                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
+        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
 
         final int clockIncrement = 100;
         // Increment the the device clocks to simulate the passage of time.
@@ -86,7 +90,7 @@
                 TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
 
         // Send the first time signal. It should be used.
-        mScript.simulateTimeSignalReceived(timeSignal1)
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
                 .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis1);
 
         // Now send another time signal, but one that is too similar to the last one and should be
@@ -95,9 +99,9 @@
         TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
                 mScript.peekElapsedRealtimeMillis(),
                 mScript.peekSystemClockMillis() + underThresholdMillis);
-        TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+        PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
         mScript.simulateTimePassing(clockIncrement)
-                .simulateTimeSignalReceived(timeSignal2)
+                .simulatePhoneTimeSuggestion(timeSuggestion2)
                 .verifySystemClockWasNotSetAndResetCallTracking();
 
         // Now send another time signal, but one that is on the threshold and so should be used.
@@ -105,42 +109,44 @@
                 mScript.peekElapsedRealtimeMillis(),
                 mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
 
-        TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
+        PhoneTimeSuggestion timeSuggestion3 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
         mScript.simulateTimePassing(clockIncrement);
 
         long expectSystemClockMillis3 =
                 TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis());
 
-        mScript.simulateTimeSignalReceived(timeSignal3)
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
                 .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis3);
     }
 
     @Test
-    public void testSuggestTime_nitz_timeDetectionDisabled() {
+    public void testSuggestPhoneTime_nitz_timeDetectionDisabled() {
         Scenario scenario = SCENARIO_1;
         mScript.pokeFakeClocks(scenario)
                 .pokeTimeDetectionEnabled(false);
 
-        TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
-        mScript.simulateTimeSignalReceived(timeSignal)
+        PhoneTimeSuggestion timeSuggestion =
+                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion)
                 .verifySystemClockWasNotSetAndResetCallTracking();
     }
 
     @Test
-    public void testSuggestTime_nitz_invalidNitzReferenceTimesIgnored() {
+    public void testSuggestPhoneTime_nitz_invalidNitzReferenceTimesIgnored() {
         Scenario scenario = SCENARIO_1;
         final int systemClockUpdateThreshold = 2000;
         mScript.pokeFakeClocks(scenario)
                 .pokeThresholds(systemClockUpdateThreshold)
                 .pokeTimeDetectionEnabled(true);
-        TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
-        TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+        PhoneTimeSuggestion timeSuggestion1 =
+                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
+        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
 
         // Initialize the strategy / device with a time set from NITZ.
         mScript.simulateTimePassing(100);
         long expectedSystemClockMillis1 =
                 TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
-        mScript.simulateTimeSignalReceived(timeSignal1)
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
                 .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1);
 
         // The UTC time increment should be larger than the system clock update threshold so we
@@ -152,8 +158,8 @@
         long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
         TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
                 referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
-        TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
-        mScript.simulateTimeSignalReceived(timeSignal2)
+        PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
                 .verifySystemClockWasNotSetAndResetCallTracking();
 
         // Now supply a new signal that has an obviously bogus reference time : substantially in the
@@ -162,8 +168,8 @@
                 utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
         TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
                 referenceTimeInFutureMillis, validUtcTimeMillis);
-        TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
-        mScript.simulateTimeSignalReceived(timeSignal3)
+        PhoneTimeSuggestion timeSuggestion3 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
                 .verifySystemClockWasNotSetAndResetCallTracking();
 
         // Just to prove validUtcTimeMillis is valid.
@@ -172,13 +178,13 @@
                 validReferenceTimeMillis, validUtcTimeMillis);
         long expectedSystemClockMillis4 =
                 TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
-        TimeSignal timeSignal4 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime4);
-        mScript.simulateTimeSignalReceived(timeSignal4)
+        PhoneTimeSuggestion timeSuggestion4 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime4);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
                 .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4);
     }
 
     @Test
-    public void testSuggestTime_timeDetectionToggled() {
+    public void testSuggestPhoneTime_timeDetectionToggled() {
         Scenario scenario = SCENARIO_1;
         final int clockIncrementMillis = 100;
         final int systemClockUpdateThreshold = 2000;
@@ -186,15 +192,16 @@
                 .pokeThresholds(systemClockUpdateThreshold)
                 .pokeTimeDetectionEnabled(false);
 
-        TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
-        TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+        PhoneTimeSuggestion timeSuggestion1 =
+                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
+        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
 
         // Simulate time passing.
         mScript.simulateTimePassing(clockIncrementMillis);
 
         // Simulate the time signal being received. It should not be used because auto time
         // detection is off but it should be recorded.
-        mScript.simulateTimeSignalReceived(timeSignal1)
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
                 .verifySystemClockWasNotSetAndResetCallTracking();
 
         // Simulate more time passing.
@@ -216,7 +223,7 @@
         TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
                 mScript.peekElapsedRealtimeMillis(),
                 mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
-        TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+        PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
 
         // Simulate more time passing.
         mScript.simulateTimePassing(clockIncrementMillis);
@@ -226,7 +233,7 @@
 
         // The new time, though valid, should not be set in the system clock because auto time is
         // disabled.
-        mScript.simulateTimeSignalReceived(timeSignal2)
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
                 .verifySystemClockWasNotSetAndResetCallTracking();
 
         // Turn on auto time detection.
@@ -234,17 +241,6 @@
                 .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2);
     }
 
-    @Test
-    public void testSuggestTime_unknownSource() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(true);
-
-        TimeSignal timeSignal = scenario.createTimeSignalForActual("unknown");
-        mScript.simulateTimeSignalReceived(timeSignal)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-    }
-
     /**
      * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
      * like the real thing should, it also asserts preconditions.
@@ -407,8 +403,8 @@
             return mFakeCallback.peekSystemClockMillis();
         }
 
-        Script simulateTimeSignalReceived(TimeSignal timeSignal) {
-            mSimpleTimeDetectorStrategy.suggestTime(timeSignal);
+        Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) {
+            mSimpleTimeDetectorStrategy.suggestPhoneTime(timeSuggestion);
             return this;
         }
 
@@ -466,10 +462,10 @@
             return mActualTimeMillis;
         }
 
-        TimeSignal createTimeSignalForActual(String sourceId) {
+        PhoneTimeSuggestion createPhoneTimeSuggestionForActual(int phoneId) {
             TimestampedValue<Long> time = new TimestampedValue<>(
                     mInitialDeviceRealtimeMillis, mActualTimeMillis);
-            return new TimeSignal(sourceId, time);
+            return new PhoneTimeSuggestion(phoneId, time);
         }
 
         static class Builder {
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 45fef76..37da018 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -28,7 +28,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.util.TimestampedValue;
@@ -67,10 +67,10 @@
     public void testStubbedCall_withoutPermission() {
         doThrow(new SecurityException("Mock"))
                 .when(mMockContext).enforceCallingPermission(anyString(), any());
-        TimeSignal timeSignal = createNitzTimeSignal();
+        PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
 
         try {
-            mTimeDetectorService.suggestTime(timeSignal);
+            mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
         } finally {
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.SET_TIME), anyString());
@@ -78,15 +78,15 @@
     }
 
     @Test
-    public void testSuggestTime() {
+    public void testSuggestPhoneTime() {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
-        TimeSignal timeSignal = createNitzTimeSignal();
-        mTimeDetectorService.suggestTime(timeSignal);
+        PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
+        mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
 
         verify(mMockContext)
                 .enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
-        mStubbedTimeDetectorStrategy.verifySuggestTimeCalled(timeSignal);
+        mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
     }
 
     @Test
@@ -115,15 +115,16 @@
         mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false);
     }
 
-    private static TimeSignal createNitzTimeSignal() {
+    private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
+        int phoneId = 1234;
         TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
-        return new TimeSignal(TimeSignal.SOURCE_ID_NITZ, timeValue);
+        return new PhoneTimeSuggestion(phoneId, timeValue);
     }
 
     private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
 
         // Call tracking.
-        private TimeSignal mLastSuggestedTime;
+        private PhoneTimeSuggestion mLastPhoneSuggestion;
         private Boolean mLastAutoTimeDetectionToggle;
         private boolean mDumpCalled;
 
@@ -132,9 +133,9 @@
         }
 
         @Override
-        public void suggestTime(TimeSignal timeSignal) {
+        public void suggestPhoneTime(PhoneTimeSuggestion timeSuggestion) {
             resetCallTracking();
-            mLastSuggestedTime = timeSignal;
+            mLastPhoneSuggestion = timeSuggestion;
         }
 
         @Override
@@ -150,13 +151,13 @@
         }
 
         void resetCallTracking() {
-            mLastSuggestedTime = null;
+            mLastPhoneSuggestion = null;
             mLastAutoTimeDetectionToggle = null;
             mDumpCalled = false;
         }
 
-        void verifySuggestTimeCalled(TimeSignal expectedSignal) {
-            assertEquals(expectedSignal, mLastSuggestedTime);
+        void verifySuggestPhoneTimeCalled(PhoneTimeSuggestion expectedSignal) {
+            assertEquals(expectedSignal, mLastPhoneSuggestion);
         }
 
         void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 3f348a4..60290e3 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -2049,6 +2049,10 @@
                 return "DISCONNECTING";
             case STATE_SELECT_PHONE_ACCOUNT:
                 return "SELECT_PHONE_ACCOUNT";
+            case STATE_SIMULATED_RINGING:
+                return "SIMULATED_RINGING";
+            case STATE_AUDIO_PROCESSING:
+                return "AUDIO_PROCESSING";
             default:
                 Log.w(Call.class, "Unknown state %d", state);
                 return "UNKNOWN";
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 258a873..b7dab16 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.CallSuper;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,6 +35,15 @@
     /** @hide */
     public static final int INVALID_CHANNEL_NUMBER = -1;
 
+    /**
+     * parameters for validation
+     * @hide
+     */
+    public static final int MCC_LENGTH = 3;
+
+    private static final int MNC_MIN_LENGTH = 2;
+    private static final int MNC_MAX_LENGTH = 3;
+
     // Log tag
     /** @hide */
     protected final String mTag;
@@ -61,7 +71,7 @@
         mType = type;
 
         // Only allow INT_MAX if unknown string mcc/mnc
-        if (mcc == null || mcc.matches("^[0-9]{3}$")) {
+        if (mcc == null || isMcc(mcc)) {
             mMccStr = mcc;
         } else if (mcc.isEmpty() || mcc.equals(String.valueOf(Integer.MAX_VALUE))) {
             // If the mccStr is empty or unknown, set it as null.
@@ -73,7 +83,7 @@
             log("invalid MCC format: " + mcc);
         }
 
-        if (mnc == null || mnc.matches("^[0-9]{2,3}$")) {
+        if (mnc == null || isMnc(mnc)) {
             mMncStr = mnc;
         } else if (mnc.isEmpty() || mnc.equals(String.valueOf(Integer.MAX_VALUE))) {
             // If the mncStr is empty or unknown, set it as null.
@@ -206,6 +216,17 @@
         dest.writeString(mAlphaShort);
     }
 
+    /** Used by phone interface manager to verify if a given string is valid MccMnc
+     * @hide
+     */
+    public static boolean isValidPlmn(@NonNull String plmn) {
+        if (plmn.length() < MCC_LENGTH + MNC_MIN_LENGTH
+                || plmn.length() > MCC_LENGTH + MNC_MAX_LENGTH) {
+            return false;
+        }
+        return (isMcc(plmn.substring(0, MCC_LENGTH)) && isMnc(plmn.substring(MCC_LENGTH)));
+    }
+
     /**
      * Construct from Parcel
      * @hide
@@ -262,4 +283,31 @@
         if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
         return value;
     }
+
+    /** @hide */
+    private static boolean isMcc(@NonNull String mcc) {
+        // ensure no out of bounds indexing
+        if (mcc.length() != MCC_LENGTH) return false;
+
+        // Character.isDigit allows all unicode digits, not just [0-9]
+        for (int i = 0; i < MCC_LENGTH; i++) {
+            if (mcc.charAt(i) < '0' || mcc.charAt(i) > '9') return false;
+        }
+
+        return true;
+    }
+
+    /** @hide */
+    private static boolean isMnc(@NonNull String mnc) {
+        // ensure no out of bounds indexing
+        if (mnc.length() < MNC_MIN_LENGTH || mnc.length() > MNC_MAX_LENGTH) return false;
+
+        // Character.isDigit allows all unicode digits, not just [0-9]
+        for (int i = 0; i < mnc.length(); i++) {
+            if (mnc.charAt(i) < '0' || mnc.charAt(i) > '9') return false;
+        }
+
+        return true;
+    }
+
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 42d65e2..1aac919 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6756,6 +6756,40 @@
     }
 
     /**
+     * Replace the contents of the forbidden PLMN SIM file with the provided values.
+     * Passing an empty list will clear the contents of the EFfplmn file.
+     * If the provided list is shorter than the size of EFfplmn, then the list will be padded
+     * up to the file size with 'FFFFFF'. (required by 3GPP TS 31.102 spec 4.2.16)
+     * If the list is longer than the size of EFfplmn, then the file will be written from the
+     * beginning of the list up to the file size.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param fplmns a list of PLMNs to be forbidden.
+     *
+     * @return number of PLMNs that were successfully written to the SIM FPLMN list.
+     * This may be less than the number of PLMNs passed in where the SIM file does not have enough
+     * room for all of the values passed in. Return -1 in the event of an unexpected failure
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public int setForbiddenPlmns(@NonNull List<String> fplmns) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) return 0;
+            return telephony.setForbiddenPlmns(
+                    getSubId(), APPTYPE_USIM, fplmns, getOpPackageName());
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setForbiddenPlmns RemoteException: " + ex.getMessage());
+        } catch (NullPointerException ex) {
+            // This could happen before phone starts
+            Rlog.e(TAG, "setForbiddenPlmns NullPointerException: " + ex.getMessage());
+        }
+        return 0;
+    }
+
+    /**
      * Get P-CSCF address from PCO after data connection is established or modified.
      * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
      * @return array of P-CSCF address
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 919a0b0..e4674f1 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1603,6 +1603,18 @@
     String[] getForbiddenPlmns(int subId, int appType, String callingPackage);
 
     /**
+     * Set the forbidden PLMN list from the givven app type (ex APPTYPE_USIM) on a particular
+     * subscription.
+     *
+     * @param subId subId the id of the subscription
+     * @param appType appType the uicc app type, must be USIM or SIM.
+     * @param fplmns plmns the Forbiden plmns list that needed to be written to the SIM.
+     * @param content callingPackage the op Package name.
+     * @return number of fplmns that is successfully written to the SIM
+     */
+    int setForbiddenPlmns(int subId, int appType, in List<String> fplmns, String callingPackage);
+
+    /**
      * Check if phone is in emergency callback mode
      * @return true if phone is in emergency callback mode
      * @param subId the subscription ID that this action applies to.
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 6b3df94..96e215c 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -22,9 +22,11 @@
 import android.graphics.Color;
 import android.telephony.Rlog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet;
 
 import java.io.UnsupportedEncodingException;
+import java.util.List;
 
 /**
  * Various methods, useful for dealing with SIM data.
@@ -32,6 +34,11 @@
 public class IccUtils {
     static final String LOG_TAG="IccUtils";
 
+    // 3GPP specification constants
+    // Spec reference TS 31.102 section 4.2.16
+    @VisibleForTesting
+    static final int FPLMN_BYTE_SIZE = 3;
+
     // A table mapping from a number to a hex character for fast encoding hex strings.
     private static final char[] HEX_CHARS = {
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
@@ -883,4 +890,27 @@
         }
         return 0;
     }
+
+    /**
+     * Encode the Fplmns into byte array to write to EF.
+     *
+     * @param fplmns Array of fplmns to be serialized.
+     * @param dataLength the size of the EF file.
+     * @return the encoded byte array in the correct format for FPLMN file.
+     */
+    public static byte[] encodeFplmns(List<String> fplmns, int dataLength) {
+        byte[] serializedFplmns = new byte[dataLength];
+        int offset = 0;
+        for (String fplmn : fplmns) {
+            if (offset >= dataLength) break;
+            stringToBcdPlmn(fplmn, serializedFplmns, offset);
+            offset += FPLMN_BYTE_SIZE;
+        }
+        //pads to the length of the EF file.
+        while (offset < dataLength) {
+            // required by 3GPP TS 31.102 spec 4.2.16
+            serializedFplmns[offset++] = (byte) 0xff;
+        }
+        return serializedFplmns;
+    }
 }
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index c132007..b4f2663 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -22,8 +22,8 @@
         <!-- we need this magic flag, otherwise it always reboots and breaks the selinux -->
         <option name="force-skip-system-props" value="true" />
 
-        <option name="run-command" value="setprop dalvik.vm.profilesystemserver true" />
-        <option name="run-command" value="setprop dalvik.vm.profilebootclasspath true" />
+        <option name="run-command" value="device_config put runtime_native_boot profilesystemserver true" />
+        <option name="run-command" value="device_config put runtime_native_boot profilebootclasspath true" />
 
         <!-- Profiling does not pick up the above changes we restart the shell -->
         <option name="run-command" value="stop" />
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index fe1d9d2..81937e6 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -46,17 +46,23 @@
      */
     @Test
     public void testProperties() throws Exception {
-        String res = mTestDevice.getProperty("dalvik.vm.profilebootclasspath");
+        String res = mTestDevice.getProperty(
+                "persist.device_config.runtime_native_boot.profilebootclasspath");
         assertTrue("profile boot class path not enabled", res != null && res.equals("true"));
-        res = mTestDevice.getProperty("dalvik.vm.profilesystemserver");
+        res = mTestDevice.getProperty(
+                "persist.device_config.runtime_native_boot.profilesystemserver");
         assertTrue("profile system server not enabled", res != null && res.equals("true"));
     }
 
-    private void forceSaveProfile(String pkg) throws Exception {
+    private boolean forceSaveProfile(String pkg) throws Exception {
         String pid = mTestDevice.executeShellCommand("pidof " + pkg).trim();
-        assertTrue("Invalid pid " + pid, pid.length() > 0);
+        if (pid.length() == 0) {
+            // Not yet running.
+            return false;
+        }
         String res = mTestDevice.executeShellCommand("kill -s SIGUSR1 " + pid).trim();
         assertTrue("kill SIGUSR1: " + res, res.length() == 0);
+        return true;
     }
 
     @Test
@@ -69,10 +75,13 @@
         // Wait up to 20 seconds for the profile to be saved.
         for (int i = 0; i < 20; ++i) {
             // Force save the profile since we truncated it.
-            forceSaveProfile("system_server");
-            String s = mTestDevice.executeShellCommand("wc -c <" + SYSTEM_SERVER_PROFILE).trim();
-            if (!"0".equals(s)) {
-                break;
+            if (forceSaveProfile("system_server")) {
+                // Might fail if system server is not yet running.
+                String s = mTestDevice.executeShellCommand(
+                        "wc -c <" + SYSTEM_SERVER_PROFILE).trim();
+                if (!"0".equals(s)) {
+                    break;
+                }
             }
             Thread.sleep(1000);
         }
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index beb5e0d..b9b2238 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -44,7 +44,11 @@
 android_test {
     name: "FrameworksNetTests",
     defaults: ["FrameworksNetTests-jni-defaults"],
-    srcs: ["java/**/*.java", "java/**/*.kt"],
+    srcs: [
+        ":tethering-tests-src",
+        "java/**/*.java",
+        "java/**/*.kt",
+    ],
     platform_apis: true,
     test_suites: ["device-tests"],
     certificate: "platform",
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 2ca0d1a..1569112 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -271,7 +271,7 @@
             .addCapability(NET_CAPABILITY_NOT_METERED);
         assertParcelingIsLossless(netCap);
         netCap.setSSID(TEST_SSID);
-        assertParcelSane(netCap, 11);
+        assertParcelSane(netCap, 12);
     }
 
     @Test
diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
index 46e27c1..1f2bb0a 100644
--- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -30,6 +30,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.app.Instrumentation;
 import android.content.Context;
@@ -283,6 +284,107 @@
         assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_BYTES, msg);
     }
 
+    // Hexadecimal representation of InetDiagReqV2 request with extension, INET_DIAG_INFO.
+    private static final String INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_HEX =
+            // struct nlmsghdr
+            "48000000" +     // length = 72
+            "1400" +         // type = SOCK_DIAG_BY_FAMILY
+            "0100" +         // flags = NLM_F_REQUEST
+            "00000000" +     // seqno
+            "00000000" +     // pid (0 == kernel)
+            // struct inet_diag_req_v2
+            "02" +           // family = AF_INET
+            "06" +           // protcol = IPPROTO_TCP
+            "02" +           // idiag_ext = INET_DIAG_INFO
+            "00" +           // pad
+            "ffffffff" +   // idiag_states
+            // inet_diag_sockid
+            "3039" +         // idiag_sport = 12345
+            "d431" +         // idiag_dport = 54321
+            "01020304000000000000000000000000" + // idiag_src = 1.2.3.4
+            "08080404000000000000000000000000" + // idiag_dst = 8.8.4.4
+            "00000000" +     // idiag_if
+            "ffffffffffffffff"; // idiag_cookie = INET_DIAG_NOCOOKIE
+
+    private static final byte[] INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_BYTES =
+            HexEncoding.decode(INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_HEX.toCharArray(), false);
+    private static final int TCP_ALL_STATES = 0xffffffff;
+    @Test
+    public void testInetDiagReqV2TcpInetWithExt() throws Exception {
+        InetSocketAddress local = new InetSocketAddress(
+                InetAddress.getByName("1.2.3.4"), 12345);
+        InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.4.4"),
+                54321);
+        byte[] msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, remote, AF_INET,
+                NLM_F_REQUEST, 0 /* pad */, 2 /* idiagExt */, TCP_ALL_STATES);
+
+        assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_BYTES, msg);
+
+        local = new InetSocketAddress(
+                InetAddress.getByName("fe80::86c9:b2ff:fe6a:ed4b"), 42462);
+        remote = new InetSocketAddress(InetAddress.getByName("8.8.8.8"),
+                47473);
+        msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, remote, AF_INET6,
+                NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
+
+        assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_BYTES, msg);
+    }
+
+    // Hexadecimal representation of InetDiagReqV2 request with no socket specified.
+    private static final String INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFIED_HEX =
+            // struct nlmsghdr
+            "48000000" +     // length = 72
+            "1400" +         // type = SOCK_DIAG_BY_FAMILY
+            "0100" +         // flags = NLM_F_REQUEST
+            "00000000" +     // seqno
+            "00000000" +     // pid (0 == kernel)
+            // struct inet_diag_req_v2
+            "0a" +           // family = AF_INET6
+            "06" +           // protcol = IPPROTO_TCP
+            "00" +           // idiag_ext
+            "00" +           // pad
+            "ffffffff" +     // idiag_states
+            // inet_diag_sockid
+            "0000" +         // idiag_sport
+            "0000" +         // idiag_dport
+            "00000000000000000000000000000000" + // idiag_src
+            "00000000000000000000000000000000" + // idiag_dst
+            "00000000" +     // idiag_if
+            "0000000000000000"; // idiag_cookie
+
+    private static final byte[] INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFED_BYTES =
+            HexEncoding.decode(INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFIED_HEX.toCharArray(), false);
+
+    @Test
+    public void testInetDiagReqV2TcpInet6NoIdSpecified() throws Exception {
+        InetSocketAddress local = new InetSocketAddress(
+                InetAddress.getByName("fe80::fe6a:ed4b"), 12345);
+        InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.4.4"),
+                54321);
+        // Verify no socket specified if either local or remote socket address is null.
+        byte[] msgExt = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, null, null, AF_INET6,
+                NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
+        byte[] msg;
+        try {
+            msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, null, remote, AF_INET6,
+                    NLM_F_REQUEST);
+            fail("Both remote and local should be null, expected UnknownHostException");
+        } catch (NullPointerException e) {
+        }
+
+        try {
+            msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, null, AF_INET6,
+                    NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
+            fail("Both remote and local should be null, expected UnknownHostException");
+        } catch (NullPointerException e) {
+        }
+
+        msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, null, null, AF_INET6,
+                NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
+        assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFED_BYTES, msg);
+        assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFED_BYTES, msgExt);
+    }
+
     // Hexadecimal representation of InetDiagReqV2 request.
     private static final String INET_DIAG_MSG_HEX =
             // struct nlmsghdr
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index cf3fba8..61f37fd 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -33,6 +33,7 @@
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
+import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
@@ -492,6 +493,8 @@
         private INetworkMonitor mNetworkMonitor;
         private INetworkMonitorCallbacks mNmCallbacks;
         private int mNmValidationResult = VALIDATION_RESULT_BASE;
+        private int mProbesCompleted;
+        private int mProbesSucceeded;
         private String mNmValidationRedirectUrl = null;
         private boolean mNmProvNotificationRequested = false;
 
@@ -559,6 +562,7 @@
                 mNmProvNotificationRequested = false;
             }
 
+            mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded);
             mNmCallbacks.notifyNetworkTested(
                     mNmValidationResult, mNmValidationRedirectUrl);
 
@@ -581,7 +585,7 @@
          * @param validated Indicate if network should pretend to be validated.
          */
         public void connect(boolean validated) {
-            connect(validated, true);
+            connect(validated, true, false /* isStrictMode */);
         }
 
         /**
@@ -589,13 +593,13 @@
          * @param validated Indicate if network should pretend to be validated.
          * @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
          */
-        public void connect(boolean validated, boolean hasInternet) {
+        public void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
             assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET));
 
             ConnectivityManager.NetworkCallback callback = null;
             final ConditionVariable validatedCv = new ConditionVariable();
             if (validated) {
-                setNetworkValid();
+                setNetworkValid(isStrictMode);
                 NetworkRequest request = new NetworkRequest.Builder()
                         .addTransportType(getNetworkCapabilities().getTransportTypes()[0])
                         .clearCapabilities()
@@ -620,15 +624,15 @@
             if (validated) {
                 // Wait for network to validate.
                 waitFor(validatedCv);
-                setNetworkInvalid();
+                setNetworkInvalid(isStrictMode);
             }
 
             if (callback != null) mCm.unregisterNetworkCallback(callback);
         }
 
-        public void connectWithCaptivePortal(String redirectUrl) {
-            setNetworkPortal(redirectUrl);
-            connect(false);
+        public void connectWithCaptivePortal(String redirectUrl, boolean isStrictMode) {
+            setNetworkPortal(redirectUrl, isStrictMode);
+            connect(false, true /* hasInternet */, isStrictMode);
         }
 
         public void connectWithPartialConnectivity() {
@@ -636,34 +640,75 @@
             connect(false);
         }
 
-        public void connectWithPartialValidConnectivity() {
-            setNetworkPartialValid();
-            connect(false);
+        public void connectWithPartialValidConnectivity(boolean isStrictMode) {
+            setNetworkPartialValid(isStrictMode);
+            connect(false, true /* hasInternet */, isStrictMode);
         }
 
-        void setNetworkValid() {
+        void setNetworkValid(boolean isStrictMode) {
             mNmValidationResult = VALIDATION_RESULT_VALID;
             mNmValidationRedirectUrl = null;
+            int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTP;
+            if (isStrictMode) {
+                probesSucceeded |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+            }
+            // The probesCompleted equals to probesSucceeded for the case of valid network, so put
+            // the same value into two different parameter of the method.
+            setProbesStatus(probesSucceeded, probesSucceeded);
         }
 
-        void setNetworkInvalid() {
+        void setNetworkInvalid(boolean isStrictMode) {
             mNmValidationResult = VALIDATION_RESULT_INVALID;
             mNmValidationRedirectUrl = null;
+            int probesCompleted = VALIDATION_RESULT_BASE;
+            int probesSucceeded = VALIDATION_RESULT_INVALID;
+            // If the isStrictMode is true, it means the network is invalid when NetworkMonitor
+            // tried to validate the private DNS but failed.
+            if (isStrictMode) {
+                probesCompleted &= ~NETWORK_VALIDATION_PROBE_HTTP;
+                probesSucceeded = probesCompleted;
+                probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+            }
+            setProbesStatus(probesCompleted, probesSucceeded);
         }
 
-        void setNetworkPortal(String redirectUrl) {
-            setNetworkInvalid();
+        void setNetworkPortal(String redirectUrl, boolean isStrictMode) {
+            setNetworkInvalid(isStrictMode);
             mNmValidationRedirectUrl = redirectUrl;
+            // Suppose the portal is found when NetworkMonitor probes NETWORK_VALIDATION_PROBE_HTTP
+            // in the beginning, so the NETWORK_VALIDATION_PROBE_HTTPS hasn't probed yet.
+            int probesCompleted = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+            int probesSucceeded = VALIDATION_RESULT_INVALID;
+            if (isStrictMode) {
+                probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+            }
+            setProbesStatus(probesCompleted, probesSucceeded);
         }
 
         void setNetworkPartial() {
             mNmValidationResult = VALIDATION_RESULT_PARTIAL;
             mNmValidationRedirectUrl = null;
+            int probesCompleted = VALIDATION_RESULT_BASE;
+            int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+            setProbesStatus(probesCompleted, probesSucceeded);
         }
 
-        void setNetworkPartialValid() {
-            mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID;
-            mNmValidationRedirectUrl = null;
+        void setNetworkPartialValid(boolean isStrictMode) {
+            setNetworkPartial();
+            mNmValidationResult |= VALIDATION_RESULT_VALID;
+            int probesCompleted = VALIDATION_RESULT_BASE;
+            int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+            // Suppose the partial network cannot pass the private DNS validation as well, so only
+            // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded.
+            if (isStrictMode) {
+                probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+            }
+            setProbesStatus(probesCompleted, probesSucceeded);
+        }
+
+        void setProbesStatus(int probesCompleted, int probesSucceeded) {
+            mProbesCompleted = probesCompleted;
+            mProbesSucceeded = probesSucceeded;
         }
 
         public String waitForRedirectUrl() {
@@ -2226,7 +2271,7 @@
 
         // With HTTPS probe disabled, NetworkMonitor should pass the network validation with http
         // probe.
-        mWiFiNetworkAgent.setNetworkPartialValid();
+        mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
         // If the user chooses yes to use this partial connectivity wifi, switch the default
         // network to wifi and check if wifi becomes valid or not.
         mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
@@ -2299,7 +2344,7 @@
         callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
-        mWiFiNetworkAgent.setNetworkValid();
+        mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
 
         // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
         // validated.
@@ -2317,7 +2362,7 @@
         // NetworkMonitor will immediately (once the HTTPS probe fails...) report the network as
         // valid, because ConnectivityService calls setAcceptPartialConnectivity before it calls
         // notifyNetworkConnected.
-        mWiFiNetworkAgent.connectWithPartialValidConnectivity();
+        mWiFiNetworkAgent.connectWithPartialValidConnectivity(false /* isStrictMode */);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
         callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
@@ -2343,7 +2388,7 @@
         // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         String redirectUrl = "http://android.com/path";
-        mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl);
+        mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */);
         captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), redirectUrl);
 
@@ -2361,7 +2406,7 @@
                 mWiFiNetworkAgent);
 
         // Report partial connectivity is accepted.
-        mWiFiNetworkAgent.setNetworkPartialValid();
+        mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
         mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
                 false /* always */);
         waitForIdle();
@@ -2392,7 +2437,7 @@
         // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         String firstRedirectUrl = "http://example.com/firstPath";
-        mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
+        mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */);
         captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
 
@@ -2405,13 +2450,13 @@
         // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         String secondRedirectUrl = "http://example.com/secondPath";
-        mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
+        mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl, false /* isStrictMode */);
         captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
 
         // Make captive portal disappear then revalidate.
         // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
-        mWiFiNetworkAgent.setNetworkValid();
+        mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
         captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
 
@@ -2423,7 +2468,7 @@
 
         // Break network connectivity.
         // Expect NET_CAPABILITY_VALIDATED onLost callback.
-        mWiFiNetworkAgent.setNetworkInvalid();
+        mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
         validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
     }
@@ -2454,7 +2499,7 @@
         mServiceContext.expectNoStartActivityIntent(fastTimeoutMs);
 
         // Turn into a captive portal.
-        mWiFiNetworkAgent.setNetworkPortal("http://example.com");
+        mWiFiNetworkAgent.setNetworkPortal("http://example.com", false /* isStrictMode */);
         mCm.reportNetworkConnectivity(wifiNetwork, false);
         captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -2475,7 +2520,7 @@
         assertEquals(testValue, signInIntent.getStringExtra(testKey));
 
         // Report that the captive portal is dismissed, and check that callbacks are fired
-        mWiFiNetworkAgent.setNetworkValid();
+        mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
         mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
         validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -2504,7 +2549,7 @@
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         String firstRedirectUrl = "http://example.com/firstPath";
 
-        mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
+        mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */);
         mWiFiNetworkAgent.expectDisconnected();
         mWiFiNetworkAgent.expectPreventReconnectReceived();
 
@@ -3219,7 +3264,7 @@
         Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
 
         // Fail validation on wifi.
-        mWiFiNetworkAgent.setNetworkInvalid();
+        mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
         mCm.reportNetworkConnectivity(wifiNetwork, false);
         defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -3263,7 +3308,7 @@
         wifiNetwork = mWiFiNetworkAgent.getNetwork();
 
         // Fail validation on wifi and expect the dialog to appear.
-        mWiFiNetworkAgent.setNetworkInvalid();
+        mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
         mCm.reportNetworkConnectivity(wifiNetwork, false);
         defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -4299,7 +4344,7 @@
         mCm.registerNetworkCallback(request, callback);
 
         // Bring up wifi aware network.
-        wifiAware.connect(false, false);
+        wifiAware.connect(false, false, false /* isStrictMode */);
         callback.expectAvailableCallbacksUnvalidated(wifiAware);
 
         assertNull(mCm.getActiveNetworkInfo());
@@ -4537,6 +4582,44 @@
     }
 
     @Test
+    public void testPrivateDnsNotification() throws Exception {
+        NetworkRequest request = new NetworkRequest.Builder()
+                .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(request, callback);
+        // Bring up wifi.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(false);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        // Private DNS resolution failed, checking if the notification will be shown or not.
+        mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+        mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+        waitForIdle();
+        // If network validation failed, NetworkMonitor will re-evaluate the network.
+        // ConnectivityService should filter the redundant notification. This part is trying to
+        // simulate that situation and check if ConnectivityService could filter that case.
+        mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+        waitForIdle();
+        verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).notifyAsUser(anyString(),
+                eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL));
+        // If private DNS resolution successful, the PRIVATE_DNS_BROKEN notification shouldn't be
+        // shown.
+        mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */);
+        mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+        waitForIdle();
+        verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).cancelAsUser(anyString(),
+                eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), eq(UserHandle.ALL));
+        // If private DNS resolution failed again, the PRIVATE_DNS_BROKEN notification should be
+        // shown again.
+        mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+        mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+        waitForIdle();
+        verify(mNotificationManager, timeout(TIMEOUT_MS).times(2)).notifyAsUser(anyString(),
+                eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL));
+    }
+
+    @Test
     public void testPrivateDnsSettingsChange() throws Exception {
         // Clear any interactions that occur as a result of CS starting up.
         reset(mMockDnsResolver);
@@ -4793,7 +4876,7 @@
         // by NetworkMonitor
         assertFalse(NetworkMonitorUtils.isValidationRequired(
                 vpnNetworkAgent.getNetworkCapabilities()));
-        vpnNetworkAgent.setNetworkValid();
+        vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
 
         vpnNetworkAgent.connect(false);
         mMockVpn.connect();
@@ -4882,7 +4965,8 @@
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+                false /* isStrictMode */);
         mMockVpn.connect();
 
         defaultCallback.assertNoCallback();
@@ -4913,7 +4997,8 @@
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
+        vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */,
+                false /* isStrictMode */);
         mMockVpn.connect();
 
         defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
@@ -4945,7 +5030,8 @@
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
+        vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */,
+                false /* isStrictMode */);
         mMockVpn.connect();
 
         // Even though the VPN is unvalidated, it becomes the default network for our app.
@@ -4968,7 +5054,7 @@
                 vpnNetworkAgent.getNetworkCapabilities()));
 
         // Pretend that the VPN network validates.
-        vpnNetworkAgent.setNetworkValid();
+        vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
         vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
         // Expect to see the validated capability, but no other changes, because the VPN is already
         // the default network for the app.
@@ -5000,7 +5086,8 @@
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.connect();
         mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+                false /* isStrictMode */);
 
         vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
         nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
@@ -5099,7 +5186,8 @@
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.connect();
         mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+                false /* isStrictMode */);
 
         vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
         nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 4ce4406..c08f9b0 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -95,7 +95,7 @@
     ],
 }
 
-cc_library_shared {
+cc_library {
     name: "libstatslog",
     host_supported: true,
     generated_sources: ["statslog.cpp"],