Merge "Add UNINITIALIZED and rename INVALID"
diff --git a/Android.bp b/Android.bp
index fcea5d0..42b7fd9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,7 +104,6 @@
         "core/java/android/app/timedetector/ITimeDetectorService.aidl",
         "core/java/android/app/timezone/ICallback.aidl",
         "core/java/android/app/timezone/IRulesManager.aidl",
-        "core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl",
         "core/java/android/app/usage/ICacheQuotaService.aidl",
         "core/java/android/app/usage/IStorageStatsManager.aidl",
         "core/java/android/app/usage/IUsageStatsManager.aidl",
@@ -681,7 +680,6 @@
 
     static_libs: [
         "apex_aidl_interface-java",
-        "networkstack-aidl-interfaces-java",
         "framework-protos",
         "android.hidl.base-V1.0-java",
         "android.hardware.cas-V1.0-java",
@@ -833,6 +831,7 @@
         "core/java/android/net/ProxyInfoParcelable.aidl",
         "core/java/android/net/RouteInfoParcelable.aidl",
         "core/java/android/net/StaticIpConfigurationParcelable.aidl",
+        "core/java/android/net/TcpKeepalivePacketDataParcelable.aidl",
         "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
         "core/java/android/net/dhcp/IDhcpServer.aidl",
         "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
@@ -1110,58 +1109,12 @@
     "org/apache/http/params",
 ]
 
-// The since flag (-since N.xml API_LEVEL) is used to add API Level information
-// to the reference documentation. Must be in order of oldest to newest.
-//
-// Conscrypt (com.android.org.conscrypt) is an implementation detail and should
-// not be referenced in the documentation.
-framework_docs_args = "-android -manifest $(location core/res/AndroidManifest.xml) " +
-     "-hidePackage com.android.internal " +
-     "-hidePackage com.android.internal.util " +
-     "-hidePackage com.android.okhttp " +
-     "-hidePackage com.android.org.conscrypt " +
-     "-hidePackage com.android.server " +
-     "-since $(location 1/public/api/android.xml) 1 " +
-     "-since $(location 2/public/api/android.xml) 2 " +
-     "-since $(location 3/public/api/android.xml) 3 " +
-     "-since $(location 4/public/api/android.xml) 4 " +
-     "-since $(location 5/public/api/android.xml) 5 " +
-     "-since $(location 6/public/api/android.xml) 6 " +
-     "-since $(location 7/public/api/android.xml) 7 " +
-     "-since $(location 8/public/api/android.xml) 8 " +
-     "-since $(location 9/public/api/android.xml) 9 " +
-     "-since $(location 10/public/api/android.xml) 10 " +
-     "-since $(location 11/public/api/android.xml) 11 " +
-     "-since $(location 12/public/api/android.xml) 12 " +
-     "-since $(location 13/public/api/android.xml) 13 " +
-     "-since $(location 14/public/api/android.txt) 14 " +
-     "-since $(location 15/public/api/android.txt) 15 " +
-     "-since $(location 16/public/api/android.txt) 16 " +
-     "-since $(location 17/public/api/android.txt) 17 " +
-     "-since $(location 18/public/api/android.txt) 18 " +
-     "-since $(location 19/public/api/android.txt) 19 " +
-     "-since $(location 20/public/api/android.txt) 20 " +
-     "-since $(location 21/public/api/android.txt) 21 " +
-     "-since $(location 22/public/api/android.txt) 22 " +
-     "-since $(location 23/public/api/android.txt) 23 " +
-     "-since $(location 24/public/api/android.txt) 24 " +
-     "-since $(location 25/public/api/android.txt) 25 " +
-     "-since $(location 26/public/api/android.txt) 26 " +
-     "-since $(location 27/public/api/android.txt) 27 " +
-     "-since $(location 28/public/api/android.txt) 28 " +
-     "-since $(location api/current.txt) Q " +
-     "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
-     "-overview $(location core/java/overview.html) " +
-     // Federate Support Library references against local API file.
-     "-federate SupportLib https://developer.android.com " +
-     "-federationapi SupportLib $(location current/support-api.txt) "
-
 framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " +
      "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
      "-overview $(location core/java/overview.html) " +
      // Federate Support Library references against local API file.
      "-federate SupportLib https://developer.android.com " +
-     "-federationapi SupportLib $(location current/support-api.txt) "
+     "-federationapi SupportLib $(location :current-support-api) "
 
 framework_docs_only_libs = [
     "voip-common",
diff --git a/Android.mk b/Android.mk
index 65d4d24..9a91dd1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -72,6 +72,9 @@
 	$(hide) mkdir -p $(OUT_DOCS)/offline-sdk
 	( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
 
+.PHONY: docs offline-sdk-docs
+docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp
+
 # Run this for checkbuild
 checkbuild: doc-comment-check-docs
 # Check comment when you are updating the API
@@ -79,34 +82,6 @@
 
 # ==== hiddenapi lists =======================================
 ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
-.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
-$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
-    PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS)
-$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
-    frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
-    frameworks/base/config/hiddenapi-greylist.txt \
-    frameworks/base/config/hiddenapi-greylist-max-p.txt \
-    frameworks/base/config/hiddenapi-greylist-max-o.txt \
-    frameworks/base/config/hiddenapi-force-blacklist.txt \
-    $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) \
-    $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
-    $(SOONG_HIDDENAPI_FLAGS)
-	frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
-	    --csv $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) $(PRIVATE_FLAGS_INPUTS) \
-	    --greylist frameworks/base/config/hiddenapi-greylist.txt \
-	    --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
-	    --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \
-	    --greylist-max-o-ignore-conflicts \
-	        frameworks/base/config/hiddenapi-greylist-max-o.txt \
-	    --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \
-	    --output $@.tmp
-	$(call commit-change-for-toc,$@)
-
-$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \
-    frameworks/base/tools/hiddenapi/merge_csv.py \
-    $(PRIVATE_METADATA_INPUTS)
-	frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@
-
 $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
 $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
 endif  # UNSAFE_DISABLE_HIDDENAPI_FLAGS
diff --git a/api/TEST_MAPPING b/api/TEST_MAPPING
new file mode 100644
index 0000000..8a676e9
--- /dev/null
+++ b/api/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsCurrentApiSignatureTestCases"
+    }
+  ]
+}
diff --git a/api/current.txt b/api/current.txt
index 970e3e8..172140f 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -18656,9 +18656,9 @@
     method public static android.icu.text.BreakIterator getSentenceInstance(java.util.Locale);
     method public static android.icu.text.BreakIterator getSentenceInstance(android.icu.util.ULocale);
     method public abstract java.text.CharacterIterator getText();
-    method public static android.icu.text.BreakIterator getTitleInstance();
-    method public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale);
-    method public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale);
+    method @Deprecated public static android.icu.text.BreakIterator getTitleInstance();
+    method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale);
+    method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale);
     method public static android.icu.text.BreakIterator getWordInstance();
     method public static android.icu.text.BreakIterator getWordInstance(java.util.Locale);
     method public static android.icu.text.BreakIterator getWordInstance(android.icu.util.ULocale);
@@ -18675,7 +18675,7 @@
     field public static final int KIND_CHARACTER = 0; // 0x0
     field public static final int KIND_LINE = 2; // 0x2
     field public static final int KIND_SENTENCE = 3; // 0x3
-    field public static final int KIND_TITLE = 4; // 0x4
+    field @Deprecated public static final int KIND_TITLE = 4; // 0x4
     field public static final int KIND_WORD = 1; // 0x1
     field public static final int WORD_IDEO = 400; // 0x190
     field public static final int WORD_IDEO_LIMIT = 500; // 0x1f4
@@ -27167,7 +27167,7 @@
     field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
     field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo";
     field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
-    field public static final String EXTRA_NETWORK_TYPE = "networkType";
+    field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType";
     field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity";
     field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
     field public static final String EXTRA_REASON = "reason";
@@ -40410,6 +40410,7 @@
     method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static boolean access(String, int) throws android.system.ErrnoException;
     method public static void bind(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+    method public static void bind(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static void chmod(String, int) throws android.system.ErrnoException;
     method public static void chown(String, int, int) throws android.system.ErrnoException;
     method public static void close(java.io.FileDescriptor) throws android.system.ErrnoException;
@@ -40478,6 +40479,7 @@
     method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+    method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method @Deprecated public static void setegid(int) throws android.system.ErrnoException;
     method public static void setenv(String, String, boolean) throws android.system.ErrnoException;
     method @Deprecated public static void seteuid(int) throws android.system.ErrnoException;
@@ -41285,6 +41287,15 @@
     method public abstract void onScreenCall(@NonNull android.telecom.Call.Details);
     method public final void provideCallIdentification(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallIdentification);
     method public final void respondToCall(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallScreeningService.CallResponse);
+    field public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED = "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED";
+    field public static final int CALL_DURATION_LONG = 4; // 0x4
+    field public static final int CALL_DURATION_MEDIUM = 3; // 0x3
+    field public static final int CALL_DURATION_SHORT = 2; // 0x2
+    field public static final int CALL_DURATION_VERY_SHORT = 1; // 0x1
+    field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
+    field public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE";
+    field public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE";
+    field public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE";
     field public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
   }
 
@@ -41890,6 +41901,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, String);
     method @RequiresPermission(anyOf={android.Manifest.permission.CALL_PHONE, android.Manifest.permission.MANAGE_OWN_CALLS}) public void placeCall(android.net.Uri, android.os.Bundle);
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void reportNuisanceCallStatus(@NonNull android.net.Uri, boolean);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
@@ -42083,7 +42095,7 @@
   }
 
   public final class AvailableNetworkInfo implements android.os.Parcelable {
-    ctor public AvailableNetworkInfo(int, int, java.util.ArrayList<java.lang.String>);
+    ctor public AvailableNetworkInfo(int, int, java.util.List<java.lang.String>);
     method public int describeContents();
     method public java.util.List<java.lang.String> getMccMncs();
     method public int getPriority();
@@ -42324,6 +42336,7 @@
     method public int getChannelNumber();
     method public String getMccString();
     method public String getMncString();
+    method public long getNci();
     method public int getPci();
     method public int getTac();
     method public void writeToParcel(android.os.Parcel, int);
@@ -42337,6 +42350,7 @@
     method public String getMccString();
     method public String getMncString();
     method @Nullable public String getMobileNetworkOperator();
+    method public int getUarfcn();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
   }
@@ -43004,12 +43018,12 @@
     method public boolean canChangeDtmfToneLength();
     method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
     method public int getCardIdForDefaultEuicc();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
     method public int getCarrierIdFromSimMccMnc();
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.CellLocation getCellLocation();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int);
     method public int getDataActivity();
@@ -43038,8 +43052,8 @@
     method public int getNetworkType();
     method public int getPhoneCount();
     method public int getPhoneType();
-    method public int getPreferredOpportunisticDataSubscription();
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
+    method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
+    method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
     method @Nullable public android.telephony.SignalStrength getSignalStrength();
     method public int getSimCarrierId();
     method @Nullable public CharSequence getSimCarrierIdName();
@@ -43081,8 +43095,8 @@
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+    method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(String);
     method public String sendEnvelopeWithStatus(String);
     method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
@@ -43541,6 +43555,483 @@
 
 }
 
+package android.telephony.ims {
+
+  public class Rcs1To1Thread extends android.telephony.ims.RcsThread {
+    method @WorkerThread public long getFallbackThreadId() throws android.telephony.ims.RcsMessageStoreException;
+    method @NonNull @WorkerThread public android.telephony.ims.RcsParticipant getRecipient() throws android.telephony.ims.RcsMessageStoreException;
+    method public boolean isGroup();
+    method @WorkerThread public void setFallbackThreadId(long) throws android.telephony.ims.RcsMessageStoreException;
+  }
+
+  public abstract class RcsEvent {
+    ctor protected RcsEvent(long);
+    method public long getTimestamp();
+  }
+
+  public final class RcsEventQueryParams implements android.os.Parcelable {
+    method public int describeContents();
+    method @android.telephony.ims.RcsEventQueryParams.EventType public int getEventType();
+    method public int getLimit();
+    method public boolean getSortDirection();
+    method public int getSortingProperty();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ALL_EVENTS = -1; // 0xffffffff
+    field public static final int ALL_GROUP_THREAD_EVENTS = 0; // 0x0
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryParams> CREATOR;
+    field public static final int GROUP_THREAD_ICON_CHANGED_EVENT = 8; // 0x8
+    field public static final int GROUP_THREAD_NAME_CHANGED_EVENT = 16; // 0x10
+    field public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = 2; // 0x2
+    field public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = 4; // 0x4
+    field public static final int PARTICIPANT_ALIAS_CHANGED_EVENT = 1; // 0x1
+    field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
+    field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
+  }
+
+  public static class RcsEventQueryParams.Builder {
+    ctor public RcsEventQueryParams.Builder();
+    method public android.telephony.ims.RcsEventQueryParams build();
+    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setEventType(@android.telephony.ims.RcsEventQueryParams.EventType int);
+    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setGroupThread(@NonNull android.telephony.ims.RcsGroupThread);
+    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
+    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortDirection(boolean);
+    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortProperty(@android.telephony.ims.RcsEventQueryParams.SortingProperty int);
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.ALL_EVENTS, android.telephony.ims.RcsEventQueryParams.ALL_GROUP_THREAD_EVENTS, android.telephony.ims.RcsEventQueryParams.PARTICIPANT_ALIAS_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_JOINED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_LEFT_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_NAME_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_ICON_CHANGED_EVENT}) public static @interface RcsEventQueryParams.EventType {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsEventQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsEventQueryParams.SortingProperty {
+  }
+
+  public final class RcsEventQueryResult implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
+    method public java.util.List<android.telephony.ims.RcsEvent> getEvents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryResult> CREATOR;
+  }
+
+  public final class RcsFileTransferCreationParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public String getContentMimeType();
+    method public android.net.Uri getContentUri();
+    method public long getFileSize();
+    method @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus();
+    method public int getHeight();
+    method public long getMediaDuration();
+    method public String getPreviewMimeType();
+    method public android.net.Uri getPreviewUri();
+    method public String getRcsFileTransferSessionId();
+    method public long getTransferOffset();
+    method public int getWidth();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsFileTransferCreationParams> CREATOR;
+  }
+
+  public class RcsFileTransferCreationParams.Builder {
+    ctor public RcsFileTransferCreationParams.Builder();
+    method public android.telephony.ims.RcsFileTransferCreationParams build();
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentMimeType(String);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentUri(android.net.Uri);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileSize(long);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferSessionId(String);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setHeight(int);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setMediaDuration(long);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewMimeType(String);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewUri(android.net.Uri);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setTransferOffset(long);
+    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setWidth(int);
+  }
+
+  public class RcsFileTransferPart {
+    method @WorkerThread @Nullable public String getContentMimeType() throws android.telephony.ims.RcsMessageStoreException;
+    method @Nullable @WorkerThread public android.net.Uri getContentUri() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public long getFileSize() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public String getFileTransferSessionId() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public int getHeight() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public long getLength() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public String getPreviewMimeType() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public android.net.Uri getPreviewUri() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public long getTransferOffset() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public int getWidth() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setContentMimeType(String) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setContentUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setFileSize(long) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setFileTransferSessionId(String) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setHeight(int) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setLength(long) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setPreviewMimeType(String) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setPreviewUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setTransferOffset(long) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setWidth(int) throws android.telephony.ims.RcsMessageStoreException;
+    field public static final int DOWNLOADING = 6; // 0x6
+    field public static final int DOWNLOADING_CANCELLED = 9; // 0x9
+    field public static final int DOWNLOADING_FAILED = 8; // 0x8
+    field public static final int DOWNLOADING_PAUSED = 7; // 0x7
+    field public static final int DRAFT = 1; // 0x1
+    field public static final int NOT_SET = 0; // 0x0
+    field public static final int SENDING = 2; // 0x2
+    field public static final int SENDING_CANCELLED = 5; // 0x5
+    field public static final int SENDING_FAILED = 4; // 0x4
+    field public static final int SENDING_PAUSED = 3; // 0x3
+    field public static final int SUCCEEDED = 10; // 0xa
+  }
+
+  @IntDef({android.telephony.ims.RcsFileTransferPart.DRAFT, android.telephony.ims.RcsFileTransferPart.SENDING, android.telephony.ims.RcsFileTransferPart.SENDING_PAUSED, android.telephony.ims.RcsFileTransferPart.SENDING_FAILED, android.telephony.ims.RcsFileTransferPart.SENDING_CANCELLED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_PAUSED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_FAILED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_CANCELLED, android.telephony.ims.RcsFileTransferPart.SUCCEEDED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsFileTransferPart.RcsFileTransferStatus {
+  }
+
+  public class RcsGroupThread extends android.telephony.ims.RcsThread {
+    method @WorkerThread public void addParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
+    method @Nullable @WorkerThread public android.net.Uri getConferenceUri() throws android.telephony.ims.RcsMessageStoreException;
+    method @Nullable public android.net.Uri getGroupIcon() throws android.telephony.ims.RcsMessageStoreException;
+    method @Nullable @WorkerThread public String getGroupName() throws android.telephony.ims.RcsMessageStoreException;
+    method @Nullable @WorkerThread public android.telephony.ims.RcsParticipant getOwner() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public java.util.Set<android.telephony.ims.RcsParticipant> getParticipants() throws android.telephony.ims.RcsMessageStoreException;
+    method public boolean isGroup();
+    method @WorkerThread public void removeParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
+    method @Nullable @WorkerThread public void setConferenceUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setGroupIcon(@Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setGroupName(String) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setOwner(@Nullable android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
+  }
+
+  public abstract class RcsGroupThreadEvent extends android.telephony.ims.RcsEvent {
+    method @NonNull public android.telephony.ims.RcsParticipant getOriginatingParticipant();
+    method @NonNull public android.telephony.ims.RcsGroupThread getRcsGroupThread();
+  }
+
+  public final class RcsGroupThreadIconChangedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable {
+    ctor public RcsGroupThreadIconChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable android.net.Uri);
+    method public int describeContents();
+    method @Nullable public android.net.Uri getNewIcon();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadIconChangedEvent> CREATOR;
+  }
+
+  public final class RcsGroupThreadNameChangedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable {
+    ctor public RcsGroupThreadNameChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable String);
+    method public int describeContents();
+    method @Nullable public String getNewName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadNameChangedEvent> CREATOR;
+  }
+
+  public final class RcsGroupThreadParticipantJoinedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable {
+    ctor public RcsGroupThreadParticipantJoinedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant);
+    method public int describeContents();
+    method public android.telephony.ims.RcsParticipant getJoinedParticipant();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadParticipantJoinedEvent> CREATOR;
+  }
+
+  public final class RcsGroupThreadParticipantLeftEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable {
+    ctor public RcsGroupThreadParticipantLeftEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant);
+    method public int describeContents();
+    method @NonNull public android.telephony.ims.RcsParticipant getLeavingParticipantId();
+    method public void persist() throws android.telephony.ims.RcsMessageStoreException;
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadParticipantLeftEvent> CREATOR;
+  }
+
+  public class RcsIncomingMessage extends android.telephony.ims.RcsMessage {
+    method @WorkerThread public long getArrivalTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public android.telephony.ims.RcsParticipant getSenderParticipant() throws android.telephony.ims.RcsMessageStoreException;
+    method public boolean isIncoming();
+    method @WorkerThread public void setArrivalTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+  }
+
+  public final class RcsIncomingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getArrivalTimestamp();
+    method public long getSeenTimestamp();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsIncomingMessageCreationParams> CREATOR;
+  }
+
+  public static class RcsIncomingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder {
+    ctor public RcsIncomingMessageCreationParams.Builder(long, long, int);
+    method public android.telephony.ims.RcsIncomingMessageCreationParams build();
+    method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setArrivalTimestamp(long);
+    method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSeenTimestamp(long);
+    method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSenderParticipant(android.telephony.ims.RcsParticipant);
+  }
+
+  public class RcsManager {
+    method public android.telephony.ims.RcsMessageStore getRcsMessageStore();
+  }
+
+  public abstract class RcsMessage {
+    method @NonNull @WorkerThread public java.util.Set<android.telephony.ims.RcsFileTransferPart> getFileTransferParts() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public double getLatitude() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public double getLongitude() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public long getOriginationTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public String getRcsMessageId() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException;
+    method public int getSubscriptionId() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public String getText() throws android.telephony.ims.RcsMessageStoreException;
+    method @NonNull @WorkerThread public android.telephony.ims.RcsFileTransferPart insertFileTransfer(android.telephony.ims.RcsFileTransferCreationParams) throws android.telephony.ims.RcsMessageStoreException;
+    method public abstract boolean isIncoming();
+    method @WorkerThread public void removeFileTransferPart(@NonNull android.telephony.ims.RcsFileTransferPart) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setLatitude(double) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setLongitude(double) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setOriginationTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setRcsMessageId(String) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setSubscriptionId(int) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setText(String) throws android.telephony.ims.RcsMessageStoreException;
+    field public static final int DRAFT = 1; // 0x1
+    field public static final int FAILED = 6; // 0x6
+    field public static final double LOCATION_NOT_SET = 4.9E-324;
+    field public static final int NOT_SET = 0; // 0x0
+    field public static final int QUEUED = 2; // 0x2
+    field public static final int RECEIVED = 7; // 0x7
+    field public static final int RETRYING = 5; // 0x5
+    field public static final int SEEN = 9; // 0x9
+    field public static final int SENDING = 3; // 0x3
+    field public static final int SENT = 4; // 0x4
+  }
+
+  @IntDef({android.telephony.ims.RcsMessage.DRAFT, android.telephony.ims.RcsMessage.QUEUED, android.telephony.ims.RcsMessage.SENDING, android.telephony.ims.RcsMessage.SENT, android.telephony.ims.RcsMessage.RETRYING, android.telephony.ims.RcsMessage.FAILED, android.telephony.ims.RcsMessage.RECEIVED, android.telephony.ims.RcsMessage.SEEN}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsMessage.RcsMessageStatus {
+  }
+
+  public class RcsMessageCreationParams {
+    ctor protected RcsMessageCreationParams(android.telephony.ims.RcsMessageCreationParams.Builder);
+    method public double getLatitude();
+    method public double getLongitude();
+    method public int getMessageStatus();
+    method public long getOriginationTimestamp();
+    method @Nullable public String getRcsMessageGlobalId();
+    method public int getSubId();
+    method @Nullable public String getText();
+  }
+
+  public static class RcsMessageCreationParams.Builder {
+    method public android.telephony.ims.RcsMessageCreationParams build();
+    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLatitude(double);
+    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLongitude(double);
+    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setRcsMessageId(String);
+    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int);
+    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setText(String);
+  }
+
+  public final class RcsMessageQueryParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getFileTransferPresence();
+    method public int getLimit();
+    method public String getMessageLike();
+    method public int getMessageType();
+    method public boolean getSortDirection();
+    method @android.telephony.ims.RcsMessageQueryParams.SortingProperty public int getSortingProperty();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryParams> CREATOR;
+    field public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 8; // 0x8
+    field public static final int MESSAGES_WITH_FILE_TRANSFERS = 4; // 0x4
+    field public static final int MESSAGE_TYPE_INCOMING = 1; // 0x1
+    field public static final int MESSAGE_TYPE_OUTGOING = 2; // 0x2
+    field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
+    field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
+  }
+
+  public static class RcsMessageQueryParams.Builder {
+    ctor public RcsMessageQueryParams.Builder();
+    method public android.telephony.ims.RcsMessageQueryParams build();
+    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setFileTransferPresence(int);
+    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageLike(String);
+    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageType(int);
+    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
+    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortDirection(boolean);
+    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortProperty(@android.telephony.ims.RcsMessageQueryParams.SortingProperty int);
+    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setThread(@Nullable android.telephony.ims.RcsThread);
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsMessageQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsMessageQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsMessageQueryParams.SortingProperty {
+  }
+
+  public final class RcsMessageQueryResult implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
+    method @NonNull public java.util.List<android.telephony.ims.RcsMessage> getMessages();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryResult> CREATOR;
+  }
+
+  public final class RcsMessageSnippet implements android.os.Parcelable {
+    method public int describeContents();
+    method @android.telephony.ims.RcsMessage.RcsMessageStatus public int getSnippetStatus();
+    method @Nullable public String getSnippetText();
+    method public long getSnippetTimestamp();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageSnippet> CREATOR;
+  }
+
+  public class RcsMessageStore {
+    ctor public RcsMessageStore();
+    method @WorkerThread @NonNull public android.telephony.ims.RcsGroupThread createGroupThread(@Nullable java.util.List<android.telephony.ims.RcsParticipant>, @Nullable String, @Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.Rcs1To1Thread createRcs1To1Thread(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsParticipant createRcsParticipant(String, @Nullable String) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void deleteThread(@NonNull android.telephony.ims.RcsThread) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@Nullable android.telephony.ims.RcsEventQueryParams) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@Nullable android.telephony.ims.RcsMessageQueryParams) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@Nullable android.telephony.ims.RcsParticipantQueryParams) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@Nullable android.telephony.ims.RcsThreadQueryParams) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public void persistRcsEvent(android.telephony.ims.RcsEvent) throws android.telephony.ims.RcsMessageStoreException;
+  }
+
+  public class RcsMessageStoreException extends java.lang.Exception {
+    ctor public RcsMessageStoreException(String);
+  }
+
+  public class RcsOutgoingMessage extends android.telephony.ims.RcsMessage {
+    method @NonNull @WorkerThread public java.util.List<android.telephony.ims.RcsOutgoingMessageDelivery> getOutgoingDeliveries() throws android.telephony.ims.RcsMessageStoreException;
+    method public boolean isIncoming();
+  }
+
+  public final class RcsOutgoingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsOutgoingMessageCreationParams> CREATOR;
+  }
+
+  public static class RcsOutgoingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder {
+    ctor public RcsOutgoingMessageCreationParams.Builder(long, int);
+    method public android.telephony.ims.RcsOutgoingMessageCreationParams build();
+  }
+
+  public class RcsOutgoingMessageDelivery {
+    method @WorkerThread public long getDeliveredTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+    method @NonNull public android.telephony.ims.RcsOutgoingMessage getMessage();
+    method @NonNull public android.telephony.ims.RcsParticipant getRecipient();
+    method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setDeliveredTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException;
+  }
+
+  public class RcsParticipant {
+    method @Nullable @WorkerThread public String getAlias() throws android.telephony.ims.RcsMessageStoreException;
+    method @Nullable @WorkerThread public String getCanonicalAddress() throws android.telephony.ims.RcsMessageStoreException;
+    method @Nullable @WorkerThread public String getContactId() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setAlias(String) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void setContactId(String) throws android.telephony.ims.RcsMessageStoreException;
+  }
+
+  public final class RcsParticipantAliasChangedEvent extends android.telephony.ims.RcsEvent implements android.os.Parcelable {
+    ctor public RcsParticipantAliasChangedEvent(long, @NonNull android.telephony.ims.RcsParticipant, @Nullable String);
+    method public int describeContents();
+    method @Nullable public String getNewAlias();
+    method @NonNull public android.telephony.ims.RcsParticipant getParticipantId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantAliasChangedEvent> CREATOR;
+  }
+
+  public final class RcsParticipantQueryParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public String getAliasLike();
+    method public String getCanonicalAddressLike();
+    method public int getLimit();
+    method public boolean getSortDirection();
+    method public int getSortingProperty();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryParams> CREATOR;
+    field public static final int SORT_BY_ALIAS = 1; // 0x1
+    field public static final int SORT_BY_CANONICAL_ADDRESS = 2; // 0x2
+    field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
+  }
+
+  public static class RcsParticipantQueryParams.Builder {
+    ctor public RcsParticipantQueryParams.Builder();
+    method public android.telephony.ims.RcsParticipantQueryParams build();
+    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setAliasLike(String);
+    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setCanonicalAddressLike(String);
+    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
+    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortDirection(boolean);
+    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortProperty(@android.telephony.ims.RcsParticipantQueryParams.SortingProperty int);
+    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setThread(android.telephony.ims.RcsThread);
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_ALIAS, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS}) public static @interface RcsParticipantQueryParams.SortingProperty {
+  }
+
+  public final class RcsParticipantQueryResult implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
+    method @NonNull public java.util.List<android.telephony.ims.RcsParticipant> getParticipants();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryResult> CREATOR;
+  }
+
+  public final class RcsQueryContinuationToken implements android.os.Parcelable {
+    method public int describeContents();
+    method @android.telephony.ims.RcsQueryContinuationToken.ContinuationTokenType public int getQueryType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsQueryContinuationToken> CREATOR;
+    field public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0; // 0x0
+    field public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1; // 0x1
+    field public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2; // 0x2
+    field public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3; // 0x3
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsQueryContinuationToken.EVENT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.THREAD_QUERY_CONTINUATION_TOKEN_TYPE}) public static @interface RcsQueryContinuationToken.ContinuationTokenType {
+  }
+
+  public abstract class RcsThread {
+    method @WorkerThread @NonNull public android.telephony.ims.RcsIncomingMessage addIncomingMessage(@NonNull android.telephony.ims.RcsIncomingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsOutgoingMessage addOutgoingMessage(@NonNull android.telephony.ims.RcsOutgoingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread public void deleteMessage(@NonNull android.telephony.ims.RcsMessage) throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getMessages() throws android.telephony.ims.RcsMessageStoreException;
+    method @WorkerThread @NonNull public android.telephony.ims.RcsMessageSnippet getSnippet() throws android.telephony.ims.RcsMessageStoreException;
+    method public abstract boolean isGroup();
+  }
+
+  public final class RcsThreadQueryParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getLimit();
+    method public boolean getSortDirection();
+    method @android.telephony.ims.RcsThreadQueryParams.SortingProperty public int getSortingProperty();
+    method public int getThreadType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryParams> CREATOR;
+    field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
+    field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
+    field public static final int THREAD_TYPE_1_TO_1 = 2; // 0x2
+    field public static final int THREAD_TYPE_GROUP = 1; // 0x1
+  }
+
+  public static class RcsThreadQueryParams.Builder {
+    ctor public RcsThreadQueryParams.Builder();
+    method public android.telephony.ims.RcsThreadQueryParams build();
+    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipant(@NonNull android.telephony.ims.RcsParticipant);
+    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipants(@NonNull java.util.List<android.telephony.ims.RcsParticipant>);
+    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
+    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortDirection(boolean);
+    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortProperty(@android.telephony.ims.RcsThreadQueryParams.SortingProperty int);
+    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setThreadType(int);
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsThreadQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsThreadQueryParams.SortingProperty {
+  }
+
+  public final class RcsThreadQueryResult implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
+    method @NonNull public java.util.List<android.telephony.ims.RcsThread> getThreads();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryResult> CREATOR;
+  }
+
+}
+
 package android.telephony.mbms {
 
   public class DownloadProgressListener {
@@ -43607,9 +44098,9 @@
   }
 
   public interface GroupCallCallback {
-    method public void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int);
-    method public void onError(int, @Nullable String);
-    method public void onGroupCallStateChanged(int, int);
+    method public default void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int);
+    method public default void onError(int, @Nullable String);
+    method public default void onGroupCallStateChanged(int, int);
     field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
   }
 
@@ -43667,10 +44158,10 @@
   }
 
   public interface MbmsGroupCallSessionCallback {
-    method public void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>);
-    method public void onError(int, @Nullable String);
-    method public void onMiddlewareReady();
-    method public void onServiceInterfaceAvailable(@NonNull String, int);
+    method public default void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>);
+    method public default void onError(int, @Nullable String);
+    method public default void onMiddlewareReady();
+    method public default void onServiceInterfaceAvailable(@NonNull String, int);
   }
 
   public class MbmsStreamingSessionCallback {
@@ -58474,9 +58965,9 @@
     method public String getName();
     method public int getReferenceKind();
     method public default boolean isVarArgs();
-    method public static boolean refKindIsField(int);
-    method public static boolean refKindIsValid(int);
-    method public static String refKindName(int);
+    method @Deprecated public static boolean refKindIsField(int);
+    method @Deprecated public static boolean refKindIsValid(int);
+    method @Deprecated public static String refKindName(int);
     method public static String referenceKindToString(int);
     method public <T extends java.lang.reflect.Member> T reflectAs(Class<T>, java.lang.invoke.MethodHandles.Lookup);
     method public static String toString(int, Class<?>, String, java.lang.invoke.MethodType);
diff --git a/api/removed.txt b/api/removed.txt
index 05d52d4..72202ad 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -540,7 +540,7 @@
 
   public class TelephonyManager {
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
-    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 8046006..33cc416 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -862,6 +862,32 @@
     method public void sendOrderedBroadcast(android.content.Intent, String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
   }
 
+  public class DynamicAndroidClient {
+    ctor public DynamicAndroidClient(@NonNull android.content.Context);
+    method public void bind();
+    method public void setOnStatusChangedListener(@NonNull android.content.DynamicAndroidClient.OnStatusChangedListener, @NonNull java.util.concurrent.Executor);
+    method public void setOnStatusChangedListener(@NonNull android.content.DynamicAndroidClient.OnStatusChangedListener);
+    method public void start(String, long);
+    method public void start(String, long, long);
+    method public void unbind();
+    field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6
+    field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4
+    field public static final int CAUSE_ERROR_IO = 3; // 0x3
+    field public static final int CAUSE_ERROR_IPC = 5; // 0x5
+    field public static final int CAUSE_INSTALL_CANCELLED = 2; // 0x2
+    field public static final int CAUSE_INSTALL_COMPLETED = 1; // 0x1
+    field public static final int CAUSE_NOT_SPECIFIED = 0; // 0x0
+    field public static final int STATUS_IN_PROGRESS = 2; // 0x2
+    field public static final int STATUS_IN_USE = 4; // 0x4
+    field public static final int STATUS_NOT_STARTED = 1; // 0x1
+    field public static final int STATUS_READY = 3; // 0x3
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  public static interface DynamicAndroidClient.OnStatusChangedListener {
+    method public void onStatusChanged(int, int, long);
+  }
+
   public class Intent implements java.lang.Cloneable android.os.Parcelable {
     field public static final String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED";
     field public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
@@ -3069,11 +3095,13 @@
 
   public class ConnectivityManager {
     method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+    method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
     method public boolean getAvoidBadWifi();
     method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
@@ -5527,6 +5555,7 @@
     method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers();
     method public int getMultiSimPolicy();
     method public boolean isAllCarriersAllowed();
+    method public java.util.List<java.lang.Boolean> isCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; // 0x1
     field public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; // 0x0
@@ -6250,10 +6279,13 @@
 
   public class SubscriptionManager {
     method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
     method public void requestEmbeddedSubscriptionInfoListRefresh();
     method public void requestEmbeddedSubscriptionInfoListRefresh(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
     field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
     field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
     field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
@@ -6320,11 +6352,13 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getLogicalToPhysicalSlotMapping();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmap();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
     method public int getSimApplicationState();
     method public int getSimCardState();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSimLocale();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSupportedRadioAccessFamily();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
     method @Nullable public android.os.Bundle getVisualVoicemailSettings();
@@ -6337,12 +6371,13 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMultisimCarrierRestricted();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRebootRequiredForModemConfigChange();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
-    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
@@ -6352,6 +6387,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultisimCarrierRestriction(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmap(long);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int);
@@ -6365,6 +6401,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
     method public void updateServiceLocation();
+    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_DEBUG_EVENT = "android.telephony.action.DEBUG_EVENT";
     field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
     field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
     field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
@@ -6372,29 +6409,32 @@
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+    field public static final String EXTRA_DEBUG_EVENT_DESCRIPTION = "android.telephony.extra.DEBUG_EVENT_DESCRIPTION";
+    field public static final String EXTRA_DEBUG_EVENT_ID = "android.telephony.extra.DEBUG_EVENT_ID";
     field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
     field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
     field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
     field public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000L; // 0xea60L
-    field public static final int NETWORK_TYPE_BITMASK_1xRTT = 128; // 0x80
-    field public static final int NETWORK_TYPE_BITMASK_CDMA = 16; // 0x10
-    field public static final int NETWORK_TYPE_BITMASK_EDGE = 4; // 0x4
-    field public static final int NETWORK_TYPE_BITMASK_EHRPD = 16384; // 0x4000
-    field public static final int NETWORK_TYPE_BITMASK_EVDO_0 = 32; // 0x20
-    field public static final int NETWORK_TYPE_BITMASK_EVDO_A = 64; // 0x40
-    field public static final int NETWORK_TYPE_BITMASK_EVDO_B = 4096; // 0x1000
-    field public static final int NETWORK_TYPE_BITMASK_GPRS = 2; // 0x2
-    field public static final int NETWORK_TYPE_BITMASK_GSM = 65536; // 0x10000
-    field public static final int NETWORK_TYPE_BITMASK_HSDPA = 256; // 0x100
-    field public static final int NETWORK_TYPE_BITMASK_HSPA = 1024; // 0x400
-    field public static final int NETWORK_TYPE_BITMASK_HSPAP = 32768; // 0x8000
-    field public static final int NETWORK_TYPE_BITMASK_HSUPA = 512; // 0x200
-    field public static final int NETWORK_TYPE_BITMASK_LTE = 8192; // 0x2000
-    field public static final int NETWORK_TYPE_BITMASK_LTE_CA = 524288; // 0x80000
-    field public static final int NETWORK_TYPE_BITMASK_NR = 1048576; // 0x100000
-    field public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = 131072; // 0x20000
-    field public static final int NETWORK_TYPE_BITMASK_UMTS = 8; // 0x8
-    field public static final int NETWORK_TYPE_BITMASK_UNKNOWN = 1; // 0x1
+    field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
+    field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
+    field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
+    field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
+    field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L
+    field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L
+    field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L
+    field public static final long NETWORK_TYPE_BITMASK_GPRS = 1L; // 0x1L
+    field public static final long NETWORK_TYPE_BITMASK_GSM = 32768L; // 0x8000L
+    field public static final long NETWORK_TYPE_BITMASK_HSDPA = 128L; // 0x80L
+    field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L
+    field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L
+    field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L
+    field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
+    field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
+    field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
+    field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L
+    field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
+    field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
+    field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
     field public static final int RADIO_POWER_OFF = 0; // 0x0
     field public static final int RADIO_POWER_ON = 1; // 0x1
     field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
diff --git a/api/test-current.txt b/api/test-current.txt
index 34c8f6e..0112724 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -608,6 +608,7 @@
   }
 
   public class ConnectivityManager {
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
   }
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index 1b3c32b..469c964 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -2,7 +2,6 @@
     class core animation
     user graphics
     group graphics audio
-    updatable
     disabled
     oneshot
     writepid /dev/stune/top-app/tasks
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5fd148e..1d629da 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -161,6 +161,8 @@
         BluetoothBondStateChanged bluetooth_bond_state_changed = 165;
         BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported = 166;
         BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167;
+        ProcessStartTime process_start_time = 169;
+        BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
     }
 
     // Pulled events will start at field 10000.
@@ -1719,6 +1721,44 @@
 }
 
 /**
+ * Logs when a Bluetooth socket’s connection state changed
+ *
+ * Logged from:
+ *   system/bt
+ */
+message BluetoothSocketConnectionStateChanged {
+    // An identifier that can be used to match events for this device.
+    // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+    // Salt: Randomly generated 256 bit value
+    // Hash algorithm: HMAC-SHA256
+    // Size: 32 byte
+    // Default: null or empty if this is a server listener socket
+    optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // Temporary port of this socket for the current connection or session only
+    // Default 0 when unknown or don't care
+    optional int32 port = 2;
+    // Socket type as mentioned in
+    // frameworks/base/core/java/android/bluetooth/BluetoothSocket.java
+    // Default: SOCKET_TYPE_UNKNOWN
+    optional android.bluetooth.SocketTypeEnum type = 3;
+    // Socket connection state
+    // Default: SOCKET_CONNECTION_STATE_UNKNOWN
+    optional android.bluetooth.SocketConnectionstateEnum state = 4;
+    // Number of bytes sent to remote device during this connection
+    optional int64 tx_bytes = 5;
+    // Number of bytes received from remote device during this connection
+    optional int64 rx_bytes = 6;
+    // Socket owner's UID
+    optional int32 uid = 7 [(is_uid) = true];
+    // Server port of this socket, if any. When both |server_port| and |port| fields are populated,
+    // |port| must be spawned by |server_port|
+    // Default 0 when unknown or don't care
+    optional int32 server_port = 8;
+    // Whether this is a server listener socket
+    optional android.bluetooth.SocketRoleEnum is_server = 9;
+}
+
+/**
  * Logs when something is plugged into or removed from the USB-C connector.
  *
  * Logged from:
@@ -2929,3 +2969,59 @@
 
     optional string package_name = 3;
 }
+
+/*
+* Logs number of milliseconds it takes to start a process.
+* The definition of app process start time is from the app launch time to
+* the time that Zygote finished forking the app process and loaded the
+* application package's java classes.
+
+* This metric is different from AppStartOccurred which is for foreground
+* activity only.
+
+* ProcessStartTime can report all processes (both foreground and background)
+* start time.
+*
+* Logged from:
+*   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+*/
+message ProcessStartTime {
+    // The uid of the ProcessRecord.
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // The process pid.
+    optional int32 pid = 2;
+
+    // The process name.
+    // Usually package name, "system" for system server.
+    // Provided by ActivityManagerService.
+    optional string process_name = 3;
+
+    enum StartType {
+        UNKNOWN = 0;
+        WARM = 1;
+        HOT = 2;
+        COLD = 3;
+    }
+
+    // The start type.
+    optional StartType type = 4;
+
+    // The elapsed realtime at the start of the process.
+    optional int64 process_start_time_millis = 5;
+
+    // Number of milliseconds it takes to reach bind application.
+    optional int32 bind_application_delay_millis = 6;
+
+    // Number of milliseconds it takes to finish start of the process.
+    optional int32 process_start_delay_millis = 7;
+
+    // hostingType field in ProcessRecord, the component type such as "activity",
+    // "service", "content provider", "broadcast" or other strings.
+    optional string hosting_type = 8;
+
+    // hostingNameStr field in ProcessRecord. The component class name that runs
+    // in this process.
+    optional string hosting_name = 9;
+}
+
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 3d5b32a..2369198 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -2546,7 +2546,6 @@
 Lcom/android/internal/telephony/cat/CatService;->mCmdIf:Lcom/android/internal/telephony/CommandsInterface;
 Lcom/android/internal/telephony/cat/CatService;->mContext:Landroid/content/Context;
 Lcom/android/internal/telephony/cat/CatService;->mCurrntCmd:Lcom/android/internal/telephony/cat/CatCmdMessage;
-Lcom/android/internal/telephony/cat/CatService;->mHandlerThread:Landroid/os/HandlerThread;
 Lcom/android/internal/telephony/cat/CatService;->mMenuCmd:Lcom/android/internal/telephony/cat/CatCmdMessage;
 Lcom/android/internal/telephony/cat/CatService;->mMsgDecoder:Lcom/android/internal/telephony/cat/RilMessageDecoder;
 Lcom/android/internal/telephony/cat/CatService;->mSlotId:I
diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS
new file mode 100644
index 0000000..265674a
--- /dev/null
+++ b/core/java/android/accessibilityservice/OWNERS
@@ -0,0 +1,3 @@
+svetoslavganov@google.com
+pweaver@google.com
+rhedjao@google.com
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0d3110c..ac33c16 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -850,8 +850,9 @@
             }
         }
 
-        // /vendor/lib, /odm/lib and /product/lib are added to the native lib search
-        // paths of the classloader. Note that this is done AFTER the classloader is
+        // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib
+        // are added to the native lib search paths of the classloader.
+        // Note that this is done AFTER the classloader is
         // created by ApplicationLoaders.getDefault().getClassLoader(...). The
         // reason is because if we have added the paths when creating the classloader
         // above, the paths are also added to the search path of the linker namespace
@@ -868,8 +869,11 @@
         // System.loadLibrary(). In order to prevent the problem, we explicitly
         // add the paths only to the classloader, and not to the native loader
         // (linker namespace).
-        List<String> extraLibPaths = new ArrayList<>(3);
+        List<String> extraLibPaths = new ArrayList<>(4);
         String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : "";
+        if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) {
+            extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix);
+        }
         if (!defaultSearchPaths.contains("/vendor/lib")) {
             extraLibPaths.add("/vendor/lib" + abiSuffix);
         }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index a8fc9d24..c42a2bc 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -26,7 +26,6 @@
 import android.app.slice.SliceManager;
 import android.app.timedetector.TimeDetector;
 import android.app.timezone.RulesManager;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.app.trust.TrustManager;
 import android.app.usage.IStorageStatsManager;
 import android.app.usage.IUsageStatsManager;
@@ -1060,13 +1059,6 @@
                             throws ServiceNotFoundException {
                         return new TimeDetector();
                     }});
-        registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class,
-                new CachedServiceFetcher<TimeZoneDetector>() {
-                    @Override
-                    public TimeZoneDetector createService(ContextImpl ctx)
-                            throws ServiceNotFoundException {
-                        return new TimeZoneDetector();
-                    }});
         registerService(Context.DYNAMIC_ANDROID_SERVICE, DynamicAndroidManager.class,
                 new CachedServiceFetcher<DynamicAndroidManager>() {
                     @Override
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
deleted file mode 100644
index ef2cbab..0000000
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ /dev/null
@@ -1,34 +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.timezonedetector;
-
-/**
- * System private API to comunicate with time zone detector service.
- *
- * <p>Used by parts of the Android system with signals associated with the device's time zone to
- * provide information to the Time Zone Detector Service.
- *
- * <p>Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through
- * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
- * for more complete documentation.
- *
- *
- * {@hide}
- */
-interface ITimeZoneDetectorService {
-  void stubbedCall();
-}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
deleted file mode 100644
index be3c764..0000000
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ /dev/null
@@ -1,57 +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.timezonedetector;
-
-import android.annotation.SystemService;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
-
-/**
- * The interface through which system components can send signals to the TimeZoneDetectorService.
- * @hide
- */
-@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
-public final class TimeZoneDetector {
-
-    private static final String TAG = "timezonedetector.TimeZoneDetector";
-    private static final boolean DEBUG = false;
-
-    private final ITimeZoneDetectorService mITimeZoneDetectorService;
-
-    public TimeZoneDetector() throws ServiceNotFoundException {
-        mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
-                ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
-
-    }
-    /**
-     * Does nothing.
-     * TODO: Remove this when the service implementation is built out.
-     */
-    public void stubbedCall() {
-        if (DEBUG) {
-            Log.d(TAG, "stubbedCall called");
-        }
-        try {
-            mITimeZoneDetectorService.stubbedCall();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ba6e7ae8..493aac6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3087,7 +3087,6 @@
             CROSS_PROFILE_APPS_SERVICE,
             //@hide: SYSTEM_UPDATE_SERVICE,
             //@hide: TIME_DETECTOR_SERVICE,
-            //@hide: TIME_ZONE_DETECTOR_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -4290,15 +4289,6 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve an
-     * {@link android.app.timezonedetector.ITimeZoneDetectorService}.
-     * @hide
-     *
-     * @see #getSystemService(String)
-     */
-    public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
-
-    /**
-     * Use with {@link #getSystemService(String)} to retrieve an
      * {@link android.telephony.ims.RcsManager}.
      * @hide
      */
diff --git a/core/java/android/content/DynamicAndroidClient.java b/core/java/android/content/DynamicAndroidClient.java
new file mode 100644
index 0000000..571cba4
--- /dev/null
+++ b/core/java/android/content/DynamicAndroidClient.java
@@ -0,0 +1,370 @@
+/*
+ * 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.content;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+/**
+ * This class contains methods and constants used to start DynamicAndroid
+ * installation, and a listener for progress update.
+ * @hide
+ */
+@SystemApi
+public class DynamicAndroidClient {
+    /** @hide */
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_UNKNOWN,
+            STATUS_NOT_STARTED,
+            STATUS_IN_PROGRESS,
+            STATUS_READY,
+            STATUS_IN_USE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstallationStatus {}
+
+    /** @hide */
+    @IntDef(prefix = { "CAUSE_" }, value = {
+            CAUSE_NOT_SPECIFIED,
+            CAUSE_INSTALL_COMPLETED,
+            CAUSE_INSTALL_CANCELLED,
+            CAUSE_ERROR_IO,
+            CAUSE_ERROR_INVALID_URL,
+            CAUSE_ERROR_IPC,
+            CAUSE_ERROR_EXCEPTION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusChangedCause {}
+
+    private static final String TAG = "DynAndroidClient";
+
+    private static final long DEFAULT_USERDATA_SIZE = (10L << 30);
+
+
+    /** Listener for installation status update. */
+    public interface OnStatusChangedListener {
+        /**
+         * This callback is called when installation status is changed, and when the
+         * client is {@link #bind} to DynamicAndroid installation service.
+         *
+         * @param status status code, also defined in {@code DynamicAndroidClient}.
+         * @param cause cause code, also defined in {@code DynamicAndroidClient}.
+         * @param progress number of bytes installed.
+         */
+        void onStatusChanged(@InstallationStatus int status, @StatusChangedCause int cause,
+                long progress);
+    }
+
+    /*
+     * Status codes
+     */
+    /** We are bound to installation service, but failed to get its status */
+    public static final int STATUS_UNKNOWN = 0;
+
+    /** Installation is not started yet. */
+    public static final int STATUS_NOT_STARTED = 1;
+
+    /** Installation is in progress. */
+    public static final int STATUS_IN_PROGRESS = 2;
+
+    /** Installation is finished but the user has not launched it. */
+    public static final int STATUS_READY = 3;
+
+    /** Device is running in Dynamic Android. */
+    public static final int STATUS_IN_USE = 4;
+
+    /*
+     * Causes
+     */
+    /** Cause is not specified. This means the status is not changed. */
+    public static final int CAUSE_NOT_SPECIFIED = 0;
+
+    /** Status changed because installation is completed. */
+    public static final int CAUSE_INSTALL_COMPLETED = 1;
+
+    /** Status changed because installation is cancelled. */
+    public static final int CAUSE_INSTALL_CANCELLED = 2;
+
+    /** Installation failed due to IOException. */
+    public static final int CAUSE_ERROR_IO = 3;
+
+    /** Installation failed because the image URL source is not supported. */
+    public static final int CAUSE_ERROR_INVALID_URL = 4;
+
+    /** Installation failed due to IPC error. */
+    public static final int CAUSE_ERROR_IPC = 5;
+
+    /** Installation failed due to unhandled exception. */
+    public static final int CAUSE_ERROR_EXCEPTION = 6;
+
+    /*
+     * IPC Messages
+     */
+    /**
+     * Message to register listener.
+     * @hide
+     */
+    public static final int MSG_REGISTER_LISTENER = 1;
+
+    /**
+     * Message to unregister listener.
+     * @hide
+     */
+    public static final int MSG_UNREGISTER_LISTENER = 2;
+
+    /**
+     * Message for status update.
+     * @hide
+     */
+    public static final int MSG_POST_STATUS = 3;
+
+    /*
+     * Messages keys
+     */
+    /**
+     * Message key, for progress update.
+     * @hide
+     */
+    public static final String KEY_INSTALLED_SIZE = "KEY_INSTALLED_SIZE";
+
+    /*
+     * Intent Actions
+     */
+    /**
+     * Intent action: start installation.
+     * @hide
+     */
+    public static final String ACTION_START_INSTALL =
+            "android.content.action.START_INSTALL";
+
+    /**
+     * Intent action: notify user if we are currently running in Dynamic Android.
+     * @hide
+     */
+    public static final String ACTION_NOTIFY_IF_IN_USE =
+            "android.content.action.NOTIFY_IF_IN_USE";
+
+    /*
+     * Intent Keys
+     */
+    /**
+     * Intent key: URL to system image.
+     * @hide
+     */
+    public static final String KEY_SYSTEM_URL = "KEY_SYSTEM_URL";
+
+    /**
+     * Intent key: Size of system image, in bytes.
+     * @hide
+     */
+    public static final String KEY_SYSTEM_SIZE = "KEY_SYSTEM_SIZE";
+
+    /**
+     * Intent key: Number of bytes to reserve for userdata.
+     * @hide
+     */
+    public static final String KEY_USERDATA_SIZE = "KEY_USERDATA_SIZE";
+
+
+    private static class IncomingHandler extends Handler {
+        private final WeakReference<DynamicAndroidClient> mWeakClient;
+
+        IncomingHandler(DynamicAndroidClient service) {
+            super(Looper.getMainLooper());
+            mWeakClient = new WeakReference<>(service);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            DynamicAndroidClient service = mWeakClient.get();
+
+            if (service != null) {
+                service.handleMessage(msg);
+            }
+        }
+    }
+
+    private class DynAndroidServiceConnection implements ServiceConnection {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            Slog.v(TAG, "DynAndroidService connected");
+
+            mService = new Messenger(service);
+
+            try {
+                Message msg = Message.obtain(null, MSG_REGISTER_LISTENER);
+                msg.replyTo = mMessenger;
+
+                mService.send(msg);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to get status from installation service");
+                mExecutor.execute(() -> {
+                    mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0);
+                });
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            Slog.v(TAG, "DynAndroidService disconnected");
+            mService = null;
+        }
+    }
+
+    private final Context mContext;
+    private final DynAndroidServiceConnection mConnection;
+    private final Messenger mMessenger;
+
+    private boolean mBound;
+    private Executor mExecutor;
+    private OnStatusChangedListener mListener;
+    private Messenger mService;
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public DynamicAndroidClient(@NonNull Context context) {
+        mContext = context;
+        mConnection = new DynAndroidServiceConnection();
+        mMessenger = new Messenger(new IncomingHandler(this));
+    }
+
+    /**
+     * This method register a listener for status change. The listener is called using
+     * the executor.
+     */
+    public void setOnStatusChangedListener(
+            @NonNull OnStatusChangedListener listener,
+            @NonNull @CallbackExecutor Executor executor) {
+        mListener = listener;
+        mExecutor = executor;
+    }
+
+    /**
+     * This method register a listener for status change. The listener is called in main
+     * thread.
+     */
+    public void setOnStatusChangedListener(
+            @NonNull OnStatusChangedListener listener) {
+        mListener = listener;
+        mExecutor = null;
+    }
+
+    /**
+     * Bind to DynamicAndroidInstallationService.
+     */
+    public void bind() {
+        Intent intent = new Intent();
+        intent.setClassName("com.android.dynandroid",
+                "com.android.dynandroid.DynamicAndroidInstallationService");
+
+        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+
+        mBound = true;
+    }
+
+    /**
+     * Unbind from DynamicAndroidInstallationService.
+     */
+    public void unbind() {
+        if (!mBound) {
+            return;
+        }
+
+        if (mService != null) {
+            try {
+                Message msg = Message.obtain(null, MSG_UNREGISTER_LISTENER);
+                msg.replyTo = mMessenger;
+                mService.send(msg);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to unregister from installation service");
+            }
+        }
+
+        // Detach our existing connection.
+        mContext.unbindService(mConnection);
+
+        mBound = false;
+    }
+
+    /**
+     * Start installing DynamicAndroid from URL with default userdata size.
+     *
+     * @param systemUrl A network URL or a file URL to system image.
+     * @param systemSize size of system image.
+     */
+    public void start(String systemUrl, long systemSize) {
+        start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
+    }
+
+    /**
+     * Start installing DynamicAndroid from URL.
+     *
+     * @param systemUrl A network URL or a file URL to system image.
+     * @param systemSize size of system image.
+     * @param userdataSize bytes reserved for userdata.
+     */
+    public void start(String systemUrl, long systemSize, long userdataSize) {
+        Intent intent = new Intent();
+
+        intent.setClassName("com.android.dynandroid",
+                "com.android.dynandroid.VerificationActivity");
+
+        intent.setAction(ACTION_START_INSTALL);
+
+        intent.putExtra(KEY_SYSTEM_URL, systemUrl);
+        intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
+        intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
+
+        mContext.startActivity(intent);
+    }
+
+    private void handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_POST_STATUS:
+                int status = msg.arg1;
+                int cause = msg.arg2;
+                // obj is non-null
+                long progress = ((Bundle) msg.obj).getLong(KEY_INSTALLED_SIZE);
+
+                if (mExecutor != null) {
+                    mExecutor.execute(() -> {
+                        mListener.onStatusChanged(status, cause, progress);
+                    });
+                } else {
+                    mListener.onStatusChanged(status, cause, progress);
+                }
+                break;
+            default:
+                // do nothing
+
+        }
+    }
+}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index b2288fc..87568e8 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -16,13 +16,15 @@
 
 package android.ddm;
 
+import android.os.Debug;
+import android.os.UserHandle;
+import android.util.Log;
+
+import dalvik.system.VMRuntime;
+
 import org.apache.harmony.dalvik.ddmc.Chunk;
 import org.apache.harmony.dalvik.ddmc.ChunkHandler;
 import org.apache.harmony.dalvik.ddmc.DdmServer;
-import android.util.Log;
-import android.os.Debug;
-import android.os.UserHandle;
-import dalvik.system.VMRuntime;
 
 import java.nio.ByteBuffer;
 
@@ -35,6 +37,8 @@
     public static final int CHUNK_WAIT = type("WAIT");
     public static final int CHUNK_FEAT = type("FEAT");
 
+    private static final int CLIENT_PROTOCOL_VERSION = 1;
+
     private static DdmHandleHello mInstance = new DdmHandleHello();
 
     private static final String[] FRAMEWORK_FEATURES = new String[] {
@@ -145,7 +149,7 @@
                             + vmFlags.length() * 2
                             + 1);
         out.order(ChunkHandler.CHUNK_ORDER);
-        out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
+        out.putInt(CLIENT_PROTOCOL_VERSION);
         out.putInt(android.os.Process.myPid());
         out.putInt(vmIdent.length());
         out.putInt(appName.length());
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1240ef8..68ac46c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -68,6 +68,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.net.Socket;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -177,10 +178,10 @@
      * The lookup key for a {@link NetworkInfo} object. Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      *
-     * @deprecated Since {@link NetworkInfo} can vary based on UID, applications
-     *             should always obtain network information through
-     *             {@link #getActiveNetworkInfo()}.
-     * @see #EXTRA_NETWORK_TYPE
+     * @deprecated The {@link NetworkInfo} object is deprecated, as many of its properties
+     *             can't accurately represent modern network characteristics.
+     *             Please obtain information about networks from the {@link NetworkCapabilities}
+     *             or {@link LinkProperties} objects instead.
      */
     @Deprecated
     public static final String EXTRA_NETWORK_INFO = "networkInfo";
@@ -189,7 +190,11 @@
      * Network type which triggered a {@link #CONNECTIVITY_ACTION} broadcast.
      *
      * @see android.content.Intent#getIntExtra(String, int)
+     * @deprecated The network type is not rich enough to represent the characteristics
+     *             of modern networks. Please use {@link NetworkCapabilities} instead,
+     *             in particular the transports.
      */
+    @Deprecated
     public static final String EXTRA_NETWORK_TYPE = "networkType";
 
     /**
@@ -1243,9 +1248,13 @@
      *        is no current default network.
      *
      * {@hide}
+     * @deprecated please use {@link #getLinkProperties(Network)} on the return
+     *             value of {@link #getActiveNetwork()} instead. In particular,
+     *             this method will return non-null LinkProperties even if the
+     *             app is blocked by policy from using this network.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 109783091)
     public LinkProperties getActiveLinkProperties() {
         try {
             return mService.getActiveLinkProperties();
@@ -1816,7 +1825,7 @@
                 @Override
                 public void handleMessage(Message message) {
                     switch (message.what) {
-                        case NetworkAgent.EVENT_PACKET_KEEPALIVE:
+                        case NetworkAgent.EVENT_SOCKET_KEEPALIVE:
                             int error = message.arg2;
                             try {
                                 if (error == SUCCESS) {
@@ -1881,7 +1890,8 @@
      * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
      *        changes. Must be extended by applications that use this API.
      *
-     * @return A {@link SocketKeepalive} object, which can be used to control this keepalive object.
+     * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+     *         given socket.
      **/
     public SocketKeepalive createSocketKeepalive(@NonNull Network network,
             @NonNull UdpEncapsulationSocket socket,
@@ -1910,6 +1920,8 @@
      * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
      *        changes. Must be extended by applications that use this API.
      *
+     * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+     *         given socket.
      * @hide
      */
     @SystemApi
@@ -1925,6 +1937,34 @@
     }
 
     /**
+     * Request that keepalives be started on a TCP socket.
+     * The socket must be established.
+     *
+     * @param network The {@link Network} the socket is on.
+     * @param socket The socket that needs to be kept alive.
+     * @param executor The executor on which callback will be invoked. This implementation assumes
+     *                 the provided {@link Executor} runs the callbacks in sequence with no
+     *                 concurrency. Failing this, no guarantee of correctness can be made. It is
+     *                 the responsibility of the caller to ensure the executor provides this
+     *                 guarantee. A simple way of creating such an executor is with the standard
+     *                 tool {@code Executors.newSingleThreadExecutor}.
+     * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
+     *        changes. Must be extended by applications that use this API.
+     *
+     * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+     *         given socket.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD)
+    public SocketKeepalive createSocketKeepalive(@NonNull Network network,
+            @NonNull Socket socket,
+            @NonNull Executor executor,
+            @NonNull Callback callback) {
+        return new TcpSocketKeepalive(mService, network, socket, executor, callback);
+    }
+
+    /**
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface. An attempt to add a route that
      * already exists is ignored, but treated as successful.
@@ -3867,6 +3907,25 @@
     }
 
     /**
+     * Requests that the system open the captive portal app with the specified extras.
+     *
+     * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
+     * corresponding permission.
+     * @param appExtras Extras to include in the app start intent.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+    public void startCaptivePortalApp(Bundle appExtras) {
+        try {
+            mService.startCaptivePortalAppInternal(appExtras);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Determine whether the device is configured to avoid bad wifi.
      * @hide
      */
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 78fafeb..92a5839 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -27,6 +27,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.ProxyInfo;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
@@ -167,6 +168,7 @@
     void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
     void setAvoidUnvalidated(in Network network);
     void startCaptivePortalApp(in Network network);
+    void startCaptivePortalAppInternal(in Bundle appExtras);
 
     boolean getAvoidBadWifi();
     int getMultipathPreference(in Network Network);
@@ -188,6 +190,9 @@
             int intervalSeconds, in Messenger messenger, in IBinder binder, String srcAddr,
             String dstAddr);
 
+    void startTcpKeepalive(in Network network, in FileDescriptor fd, int intervalSeconds,
+            in Messenger messenger, in IBinder binder);
+
     void stopKeepalive(in Network network, int slot);
 
     String getCaptivePortalServerUrl();
diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl
index a8682f9..5146585 100644
--- a/core/java/android/net/INetworkMonitorCallbacks.aidl
+++ b/core/java/android/net/INetworkMonitorCallbacks.aidl
@@ -24,7 +24,7 @@
     void onNetworkMonitorCreated(in INetworkMonitor networkMonitor);
     void notifyNetworkTested(int testResult, @nullable String redirectUrl);
     void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
-    void showProvisioningNotification(String action);
+    void showProvisioningNotification(String action, String packageName);
     void hideProvisioningNotification();
     void logCaptivePortalLoginEvent(int eventId, String packageName);
 }
\ No newline at end of file
diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
index 7436ad0..18726f7 100644
--- a/core/java/android/net/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -16,22 +16,20 @@
 
 package android.net;
 
-import static android.net.ConnectivityManager.PacketKeepalive.*;
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
 
+import android.net.SocketKeepalive.InvalidPacketException;
 import android.net.util.IpUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.system.OsConstants;
 import android.util.Log;
 
-import java.net.Inet4Address;
 import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
 
 /**
  * Represents the actual packets that are sent by the
- * {@link android.net.ConnectivityManager.PacketKeepalive} API.
+ * {@link android.net.SocketKeepalive} API.
  *
  * @hide
  */
@@ -53,8 +51,8 @@
     /** Packet data. A raw byte string of packet data, not including the link-layer header. */
     private final byte[] mPacket;
 
-    private static final int IPV4_HEADER_LENGTH = 20;
-    private static final int UDP_HEADER_LENGTH = 8;
+    protected static final int IPV4_HEADER_LENGTH = 20;
+    protected static final int UDP_HEADER_LENGTH = 8;
 
     // This should only be constructed via static factory methods, such as
     // nattKeepalivePacket
@@ -80,53 +78,10 @@
         }
     }
 
-    public static class InvalidPacketException extends Exception {
-        public final int error;
-        public InvalidPacketException(int error) {
-            this.error = error;
-        }
-    }
-
     public byte[] getPacket() {
         return mPacket.clone();
     }
 
-    public static KeepalivePacketData nattKeepalivePacket(
-            InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
-            throws InvalidPacketException {
-
-        if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
-            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
-        }
-
-        if (dstPort != NATT_PORT) {
-            throw new InvalidPacketException(ERROR_INVALID_PORT);
-        }
-
-        int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
-        ByteBuffer buf = ByteBuffer.allocate(length);
-        buf.order(ByteOrder.BIG_ENDIAN);
-        buf.putShort((short) 0x4500);             // IP version and TOS
-        buf.putShort((short) length);
-        buf.putInt(0);                            // ID, flags, offset
-        buf.put((byte) 64);                       // TTL
-        buf.put((byte) OsConstants.IPPROTO_UDP);
-        int ipChecksumOffset = buf.position();
-        buf.putShort((short) 0);                  // IP checksum
-        buf.put(srcAddress.getAddress());
-        buf.put(dstAddress.getAddress());
-        buf.putShort((short) srcPort);
-        buf.putShort((short) dstPort);
-        buf.putShort((short) (length - 20));      // UDP length
-        int udpChecksumOffset = buf.position();
-        buf.putShort((short) 0);                  // UDP checksum
-        buf.put((byte) 0xff);                     // NAT-T keepalive
-        buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
-        buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
-
-        return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
-    }
-
     /* Parcelable Implementation */
     public int describeContents() {
         return 0;
@@ -141,7 +96,7 @@
         out.writeByteArray(mPacket);
     }
 
-    private KeepalivePacketData(Parcel in) {
+    protected KeepalivePacketData(Parcel in) {
         srcAddress = NetworkUtils.numericToInetAddress(in.readString());
         dstAddress = NetworkUtils.numericToInetAddress(in.readString());
         srcPort = in.readInt();
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
new file mode 100644
index 0000000..bdb246f
--- /dev/null
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -0,0 +1,80 @@
+/*
+ * 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.net;
+
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
+
+import android.net.SocketKeepalive.InvalidPacketException;
+import android.net.util.IpUtils;
+import android.system.OsConstants;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/** @hide */
+public final class NattKeepalivePacketData extends KeepalivePacketData {
+
+    // This should only be constructed via static factory methods, such as
+    // nattKeepalivePacket
+    private NattKeepalivePacketData(InetAddress srcAddress, int srcPort,
+            InetAddress dstAddress, int dstPort, byte[] data) throws
+            InvalidPacketException {
+        super(srcAddress, srcPort, dstAddress, dstPort, data);
+    }
+
+    /**
+     * Factory method to create Nat-T keepalive packet structure.
+     */
+    public static NattKeepalivePacketData nattKeepalivePacket(
+            InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
+            throws InvalidPacketException {
+
+        if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
+            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+        }
+
+        if (dstPort != NattSocketKeepalive.NATT_PORT) {
+            throw new InvalidPacketException(ERROR_INVALID_PORT);
+        }
+
+        int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
+        ByteBuffer buf = ByteBuffer.allocate(length);
+        buf.order(ByteOrder.BIG_ENDIAN);
+        buf.putShort((short) 0x4500);             // IP version and TOS
+        buf.putShort((short) length);
+        buf.putInt(0);                            // ID, flags, offset
+        buf.put((byte) 64);                       // TTL
+        buf.put((byte) OsConstants.IPPROTO_UDP);
+        int ipChecksumOffset = buf.position();
+        buf.putShort((short) 0);                  // IP checksum
+        buf.put(srcAddress.getAddress());
+        buf.put(dstAddress.getAddress());
+        buf.putShort((short) srcPort);
+        buf.putShort((short) dstPort);
+        buf.putShort((short) (length - 20));      // UDP length
+        int udpChecksumOffset = buf.position();
+        buf.putShort((short) 0);                  // UDP checksum
+        buf.put((byte) 0xff);                     // NAT-T keepalive
+        buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
+        buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
+
+        return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
+    }
+}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 99bfc14..7bef690 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -18,7 +18,6 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.net.ConnectivityManager.PacketKeepalive;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -154,7 +153,7 @@
      *
      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
      */
-    public static final int CMD_START_PACKET_KEEPALIVE = BASE + 11;
+    public static final int CMD_START_SOCKET_KEEPALIVE = BASE + 11;
 
     /**
      * Requests that the specified keepalive packet be stopped.
@@ -163,20 +162,40 @@
      *
      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
      */
-    public static final int CMD_STOP_PACKET_KEEPALIVE = BASE + 12;
+    public static final int CMD_STOP_SOCKET_KEEPALIVE = BASE + 12;
 
     /**
-     * Sent by the NetworkAgent to ConnectivityService to provide status on a packet keepalive
-     * request. This may either be the reply to a CMD_START_PACKET_KEEPALIVE, or an asynchronous
+     * Sent by the NetworkAgent to ConnectivityService to provide status on a socket keepalive
+     * request. This may either be the reply to a CMD_START_SOCKET_KEEPALIVE, or an asynchronous
      * error notification.
      *
-     * This is also sent by KeepaliveTracker to the app's ConnectivityManager.PacketKeepalive to
-     * so that the app's PacketKeepaliveCallback methods can be called.
+     * This is also sent by KeepaliveTracker to the app's {@link SocketKeepalive},
+     * so that the app's {@link SocketKeepalive.Callback} methods can be called.
      *
      * arg1 = slot number of the keepalive
      * arg2 = error code
      */
-    public static final int EVENT_PACKET_KEEPALIVE = BASE + 13;
+    public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13;
+
+    // TODO: move the above 2 constants down so they are in order once merge conflicts are resolved
+    /**
+     * Sent by the KeepaliveTracker to NetworkAgent to add a packet filter.
+     *
+     * For TCP keepalive offloads, keepalive packets are sent by the firmware. However, because the
+     * remote site will send ACK packets in response to the keepalive packets, the firmware also
+     * needs to be configured to properly filter the ACKs to prevent the system from waking up.
+     * This does not happen with UDP, so this message is TCP-specific.
+     * arg1 = slot number of the keepalive to filter for.
+     * obj = the keepalive packet to send repeatedly.
+     */
+    public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16;
+
+    /**
+     * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
+     * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
+     * arg1 = slot number of the keepalive packet filter to remove.
+     */
+    public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
 
     /**
      * Sent by ConnectivityService to inform this network transport of signal strength thresholds
@@ -288,12 +307,12 @@
                 saveAcceptUnvalidated(msg.arg1 != 0);
                 break;
             }
-            case CMD_START_PACKET_KEEPALIVE: {
-                startPacketKeepalive(msg);
+            case CMD_START_SOCKET_KEEPALIVE: {
+                startSocketKeepalive(msg);
                 break;
             }
-            case CMD_STOP_PACKET_KEEPALIVE: {
-                stopPacketKeepalive(msg);
+            case CMD_STOP_SOCKET_KEEPALIVE: {
+                stopSocketKeepalive(msg);
                 break;
             }
 
@@ -313,6 +332,14 @@
                 preventAutomaticReconnect();
                 break;
             }
+            case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
+                addKeepalivePacketFilter(msg);
+                break;
+            }
+            case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
+                removeKeepalivePacketFilter(msg);
+                break;
+            }
         }
     }
 
@@ -443,22 +470,40 @@
     /**
      * Requests that the network hardware send the specified packet at the specified interval.
      */
-    protected void startPacketKeepalive(Message msg) {
-        onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+    protected void startSocketKeepalive(Message msg) {
+        onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
     }
 
     /**
-     * Requests that the network hardware send the specified packet at the specified interval.
+     * Requests that the network hardware stops sending keepalive packets.
      */
-    protected void stopPacketKeepalive(Message msg) {
-        onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+    protected void stopSocketKeepalive(Message msg) {
+        onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
     }
 
     /**
-     * Called by the network when a packet keepalive event occurs.
+     * Called by the network when a socket keepalive event occurs.
      */
-    public void onPacketKeepaliveEvent(int slot, int reason) {
-        queueOrSendMessage(EVENT_PACKET_KEEPALIVE, slot, reason);
+    public void onSocketKeepaliveEvent(int slot, int reason) {
+        queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, reason);
+    }
+
+    /**
+     * Called by ConnectivityService to add specific packet filter to network hardware to block
+     * ACKs matching the sent keepalive packets. Implementations that support this feature must
+     * override this method.
+     */
+    protected void addKeepalivePacketFilter(Message msg) {
+        onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+    }
+
+    /**
+     * Called by ConnectivityService to remove a packet filter installed with
+     * {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature
+     * must override this method.
+     */
+    protected void removeKeepalivePacketFilter(Message msg) {
+        onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
     }
 
     /**
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index b6cd635..ca49438 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -15,6 +15,7 @@
  */
 package android.net;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 
@@ -27,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.net.dhcp.DhcpServingParamsParcel;
 import android.net.dhcp.IDhcpServerCallbacks;
 import android.net.ip.IIpClientCallbacks;
@@ -63,9 +65,6 @@
     public static final String PERMISSION_MAINLINE_NETWORK_STACK =
             "android.permission.MAINLINE_NETWORK_STACK";
 
-    /** @hide */
-    public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack";
-
     private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
 
     @NonNull
@@ -204,7 +203,33 @@
         final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
         intent.setComponent(comp);
 
-        if (comp == null || !context.bindServiceAsUser(intent, new NetworkStackConnection(),
+        if (comp == null) {
+            Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
+            // TODO: crash/reboot system server ?
+            return;
+        }
+
+        final PackageManager pm = context.getPackageManager();
+        int uid = -1;
+        try {
+            uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.wtf("Network stack package not found", e);
+            // Fall through
+        }
+
+        if (uid != Process.NETWORK_STACK_UID) {
+            throw new SecurityException("Invalid network stack UID: " + uid);
+        }
+
+        final int hasPermission =
+                pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
+        if (hasPermission != PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
+        }
+
+        if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
                 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
             Slog.wtf(TAG,
                     "Could not bind to network stack in-process, or in app with " + intent);
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 07668a9..0ae29b1 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -71,6 +71,18 @@
             throws SocketException;
 
     /**
+     * Attaches a socket filter that drops all of incoming packets.
+     * @param fd the socket's {@link FileDescriptor}.
+     */
+    public static native void attachDropAllBPFFilter(FileDescriptor fd) throws SocketException;
+
+    /**
+     * Detaches a socket filter.
+     * @param fd the socket's {@link FileDescriptor}.
+     */
+    public static native void detachBPFFilter(FileDescriptor fd) throws SocketException;
+
+    /**
      * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
      * @param fd the socket's {@link FileDescriptor}.
      * @param ifIndex the interface index.
@@ -170,6 +182,16 @@
     private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
             FileDescriptor fd) throws IOException;
 
+
+    /**
+     * Get the tcp repair window associated with the {@code fd}.
+     *
+     * @param fd the tcp socket's {@link FileDescriptor}.
+     * @return a {@link TcpRepairWindow} object indicates tcp window size.
+     */
+    public static native TcpRepairWindow getTcpRepairWindow(FileDescriptor fd)
+            throws ErrnoException;
+
     /**
      * @see Inet4AddressUtils#intToInet4AddressHTL(int)
      * @deprecated Use either {@link Inet4AddressUtils#intToInet4AddressHTH(int)}
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index 97d50f4..07728be 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -109,13 +110,53 @@
      **/
     public static final int MAX_INTERVAL_SEC = 3600;
 
+    /**
+     * An exception that embarks an error code.
+     * @hide
+     */
+    public static class ErrorCodeException extends Exception {
+        public final int error;
+        public ErrorCodeException(final int error, final Throwable e) {
+            super(e);
+            this.error = error;
+        }
+        public ErrorCodeException(final int error) {
+            this.error = error;
+        }
+    }
+
+    /**
+     * This socket is invalid.
+     * See the error code for details, and the optional cause.
+     * @hide
+     */
+    public static class InvalidSocketException extends ErrorCodeException {
+        public InvalidSocketException(final int error, final Throwable e) {
+            super(error, e);
+        }
+        public InvalidSocketException(final int error) {
+            super(error);
+        }
+    }
+
+    /**
+     * This packet is invalid.
+     * See the error code for details.
+     * @hide
+     */
+    public static class InvalidPacketException extends ErrorCodeException {
+        public InvalidPacketException(final int error) {
+            super(error);
+        }
+    }
+
     @NonNull final IConnectivityManager mService;
     @NonNull final Network mNetwork;
     @NonNull private final Executor mExecutor;
     @NonNull private final SocketKeepalive.Callback mCallback;
     @NonNull private final Looper mLooper;
     @NonNull final Messenger mMessenger;
-    @NonNull Integer mSlot;
+    @Nullable Integer mSlot;
 
     SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
             @NonNull Executor executor, @NonNull Callback callback) {
@@ -135,7 +176,7 @@
             @Override
             public void handleMessage(Message message) {
                 switch (message.what) {
-                    case NetworkAgent.EVENT_PACKET_KEEPALIVE:
+                    case NetworkAgent.EVENT_SOCKET_KEEPALIVE:
                         final int status = message.arg2;
                         try {
                             if (status == SUCCESS) {
diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/core/java/android/net/TcpKeepalivePacketData.java
new file mode 100644
index 0000000..f07dfb6
--- /dev/null
+++ b/core/java/android/net/TcpKeepalivePacketData.java
@@ -0,0 +1,234 @@
+/*
+ * 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.net;
+
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.SocketKeepalive.InvalidPacketException;
+import android.net.util.IpUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.system.OsConstants;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+
+/**
+ * Represents the actual tcp keep alive packets which will be used for hardware offload.
+ * @hide
+ */
+public class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
+    private static final String TAG = "TcpKeepalivePacketData";
+
+     /** TCP sequence number. */
+    public final int tcpSeq;
+
+    /** TCP ACK number. */
+    public final int tcpAck;
+
+    /** TCP RCV window. */
+    public final int tcpWnd;
+
+    /** TCP RCV window scale. */
+    public final int tcpWndScale;
+
+    private static final int IPV4_HEADER_LENGTH = 20;
+    private static final int IPV6_HEADER_LENGTH = 40;
+    private static final int TCP_HEADER_LENGTH = 20;
+
+    // This should only be constructed via static factory methods, such as
+    // tcpKeepalivePacket.
+    private TcpKeepalivePacketData(TcpSocketInfo tcpDetails, byte[] data)
+            throws InvalidPacketException {
+        super(tcpDetails.srcAddress, tcpDetails.srcPort, tcpDetails.dstAddress,
+                tcpDetails.dstPort, data);
+        tcpSeq = tcpDetails.seq;
+        tcpAck = tcpDetails.ack;
+        // In the packet, the window is shifted right by the window scale.
+        tcpWnd = tcpDetails.rcvWnd;
+        tcpWndScale = tcpDetails.rcvWndScale;
+    }
+
+    /**
+     * Factory method to create tcp keepalive packet structure.
+     */
+    public static TcpKeepalivePacketData tcpKeepalivePacket(
+            TcpSocketInfo tcpDetails) throws InvalidPacketException {
+        final byte[] packet;
+        if ((tcpDetails.srcAddress instanceof Inet4Address)
+                && (tcpDetails.dstAddress instanceof Inet4Address)) {
+            packet = buildV4Packet(tcpDetails);
+        } else {
+            // TODO: support ipv6
+            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+        }
+
+        return new TcpKeepalivePacketData(tcpDetails, packet);
+    }
+
+    /**
+     * Build ipv4 tcp keepalive packet, not including the link-layer header.
+     */
+    // TODO : if this code is ever moved to the network stack, factorize constants with the ones
+    // over there.
+    private static byte[] buildV4Packet(TcpSocketInfo tcpDetails) {
+        final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
+        ByteBuffer buf = ByteBuffer.allocate(length);
+        buf.order(ByteOrder.BIG_ENDIAN);
+        // IP version and TOS. TODO : fetch this from getsockopt(SOL_IP, IP_TOS)
+        buf.putShort((short) 0x4500);
+        buf.putShort((short) length);
+        buf.putInt(0x4000);                         // ID, flags=DF, offset
+        // TODO : fetch TTL from getsockopt(SOL_IP, IP_TTL)
+        buf.put((byte) 64);
+        buf.put((byte) OsConstants.IPPROTO_TCP);
+        final int ipChecksumOffset = buf.position();
+        buf.putShort((short) 0);                    // IP checksum
+        buf.put(tcpDetails.srcAddress.getAddress());
+        buf.put(tcpDetails.dstAddress.getAddress());
+        buf.putShort((short) tcpDetails.srcPort);
+        buf.putShort((short) tcpDetails.dstPort);
+        buf.putInt(tcpDetails.seq);                 // Sequence Number
+        buf.putInt(tcpDetails.ack);                 // ACK
+        buf.putShort((short) 0x5010);               // TCP length=5, flags=ACK
+        buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale));   // Window size
+        final int tcpChecksumOffset = buf.position();
+        buf.putShort((short) 0);                    // TCP checksum
+        // URG is not set therefore the urgent pointer is not included
+        buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
+        buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
+                buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
+
+        return buf.array();
+    }
+
+    // TODO: add buildV6Packet.
+
+    /** Represents tcp/ip information. */
+    // TODO: Replace TcpSocketInfo with TcpKeepalivePacketDataParcelable.
+    public static class TcpSocketInfo {
+        public final InetAddress srcAddress;
+        public final InetAddress dstAddress;
+        public final int srcPort;
+        public final int dstPort;
+        public final int seq;
+        public final int ack;
+        public final int rcvWnd;
+        public final int rcvWndScale;
+
+        public TcpSocketInfo(InetAddress sAddr, int sPort, InetAddress dAddr,
+                int dPort, int writeSeq, int readSeq, int rWnd, int rWndScale) {
+            srcAddress = sAddr;
+            dstAddress = dAddr;
+            srcPort = sPort;
+            dstPort = dPort;
+            seq = writeSeq;
+            ack = readSeq;
+            rcvWnd = rWnd;
+            rcvWndScale = rWndScale;
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable final Object o) {
+        if (!(o instanceof TcpKeepalivePacketData)) return false;
+        final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
+        return this.srcAddress.equals(other.srcAddress)
+                && this.dstAddress.equals(other.dstAddress)
+                && this.srcPort == other.srcPort
+                && this.dstPort == other.dstPort
+                && this.tcpAck == other.tcpAck
+                && this.tcpSeq == other.tcpSeq
+                && this.tcpWnd == other.tcpWnd
+                && this.tcpWndScale == other.tcpWndScale;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(srcAddress, dstAddress, srcPort, dstPort, tcpAck, tcpSeq, tcpWnd,
+                tcpWndScale);
+    }
+
+    /* Parcelable Implementation. */
+    /* Note that this object implements parcelable (and needs to keep doing this as it inherits
+     * from a class that does), but should usually be parceled as a stable parcelable using
+     * the toStableParcelable() and fromStableParcelable() methods.
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Write to parcel. */
+    public void writeToParcel(Parcel out, int flags) {
+        super.writeToParcel(out, flags);
+        out.writeInt(tcpSeq);
+        out.writeInt(tcpAck);
+        out.writeInt(tcpWnd);
+        out.writeInt(tcpWndScale);
+    }
+
+    private TcpKeepalivePacketData(Parcel in) {
+        super(in);
+        tcpSeq = in.readInt();
+        tcpAck = in.readInt();
+        tcpWnd = in.readInt();
+        tcpWndScale = in.readInt();
+    }
+
+    /** Parcelable Creator. */
+    public static final Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
+            new Parcelable.Creator<TcpKeepalivePacketData>() {
+                public TcpKeepalivePacketData createFromParcel(Parcel in) {
+                    return new TcpKeepalivePacketData(in);
+                }
+
+                public TcpKeepalivePacketData[] newArray(int size) {
+                    return new TcpKeepalivePacketData[size];
+                }
+            };
+
+    /**
+     * Convert this TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
+     */
+    @NonNull
+    public TcpKeepalivePacketDataParcelable toStableParcelable() {
+        final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
+        parcel.srcAddress = srcAddress.getAddress();
+        parcel.srcPort = srcPort;
+        parcel.dstAddress = dstAddress.getAddress();
+        parcel.dstPort = dstPort;
+        parcel.seq = tcpSeq;
+        parcel.ack = tcpAck;
+        return parcel;
+    }
+
+    @Override
+    public String toString() {
+        return "saddr: " + srcAddress
+                + " daddr: " + dstAddress
+                + " sport: " + srcPort
+                + " dport: " + dstPort
+                + " seq: " + tcpSeq
+                + " ack: " + tcpAck
+                + " wnd: " + tcpWnd
+                + " wndScale: " + tcpWndScale;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsPart.java b/core/java/android/net/TcpKeepalivePacketDataParcelable.aidl
similarity index 74%
rename from telephony/java/android/telephony/ims/RcsPart.java
rename to core/java/android/net/TcpKeepalivePacketDataParcelable.aidl
index da50173..7329c63 100644
--- a/telephony/java/android/telephony/ims/RcsPart.java
+++ b/core/java/android/net/TcpKeepalivePacketDataParcelable.aidl
@@ -13,13 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.telephony.ims;
 
-import android.os.Parcelable;
+package android.net;
 
-/**
- * A part of a composite {@link RcsMessage}.
- * @hide - TODO(sahinc) make this public
- */
-public abstract class RcsPart implements Parcelable {
+parcelable TcpKeepalivePacketDataParcelable {
+    byte[] srcAddress;
+    int srcPort;
+    byte[] dstAddress;
+    int dstPort;
+    int seq;
+    int ack;
 }
diff --git a/core/java/android/net/TcpRepairWindow.java b/core/java/android/net/TcpRepairWindow.java
new file mode 100644
index 0000000..86034f0
--- /dev/null
+++ b/core/java/android/net/TcpRepairWindow.java
@@ -0,0 +1,45 @@
+/*
+ * 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.net;
+
+/**
+ * Corresponds to C's {@code struct tcp_repair_window} from
+ * include/uapi/linux/tcp.h
+ *
+ * @hide
+ */
+public final class TcpRepairWindow {
+    public final int sndWl1;
+    public final int sndWnd;
+    public final int maxWindow;
+    public final int rcvWnd;
+    public final int rcvWup;
+    public final int rcvWndScale;
+
+    /**
+     * Constructs an instance with the given field values.
+     */
+    public TcpRepairWindow(final int sndWl1, final int sndWnd, final int maxWindow,
+            final int rcvWnd, final int rcvWup, final int rcvWndScale) {
+        this.sndWl1 = sndWl1;
+        this.sndWnd = sndWnd;
+        this.maxWindow = maxWindow;
+        this.rcvWnd = rcvWnd;
+        this.rcvWup = rcvWup;
+        this.rcvWndScale = rcvWndScale;
+    }
+}
diff --git a/core/java/android/net/TcpSocketKeepalive.java b/core/java/android/net/TcpSocketKeepalive.java
new file mode 100644
index 0000000..8f6ee7b
--- /dev/null
+++ b/core/java/android/net/TcpSocketKeepalive.java
@@ -0,0 +1,78 @@
+/*
+ * 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.net;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.net.Socket;
+import java.util.concurrent.Executor;
+
+/** @hide */
+final class TcpSocketKeepalive extends SocketKeepalive {
+
+    private final Socket mSocket;
+
+    TcpSocketKeepalive(@NonNull IConnectivityManager service,
+            @NonNull Network network,
+            @NonNull Socket socket,
+            @NonNull Executor executor,
+            @NonNull Callback callback) {
+        super(service, network, executor, callback);
+        mSocket = socket;
+    }
+
+    /**
+     * Starts keepalives. {@code mSocket} must be a connected TCP socket.
+     *
+     * - The application must not write to or read from the socket after calling this method, until
+     *   onDataReceived, onStopped, or onError are called. If it does, the keepalive will fail
+     *   with {@link #ERROR_SOCKET_NOT_IDLE}, or {@code #ERROR_INVALID_SOCKET} if the socket
+     *   experienced an error (as in poll(2) returned POLLERR); if this happens, the data received
+     *   from the socket may be invalid, and the socket can't be recovered.
+     * - If the socket has data in the send or receive buffer, then this call will fail with
+     *   {@link #ERROR_SOCKET_NOT_IDLE} and can be retried after the data has been processed.
+     *   An app could ensure this by using an application-layer protocol where it can receive
+     *   acknowledgement that it will go into keepalive mode. It could then go into keepalive
+     *   mode after having read the acknowledgement, draining the socket.
+     */
+    @Override
+    void startImpl(int intervalSec) {
+        try {
+            final FileDescriptor fd = mSocket.getFileDescriptor$();
+            mService.startTcpKeepalive(mNetwork, fd, intervalSec, mMessenger, new Binder());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error starting packet keepalive: ", e);
+            stopLooper();
+        }
+    }
+
+    @Override
+    void stopImpl() {
+        try {
+            if (mSlot != null) {
+                mService.stopKeepalive(mNetwork, mSlot);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error stopping packet keepalive: ", e);
+            stopLooper();
+        }
+    }
+}
diff --git a/core/java/android/net/ip/IIpClient.aidl b/core/java/android/net/ip/IIpClient.aidl
index 7769ec2..a4a80e1 100644
--- a/core/java/android/net/ip/IIpClient.aidl
+++ b/core/java/android/net/ip/IIpClient.aidl
@@ -17,6 +17,7 @@
 
 import android.net.ProxyInfoParcelable;
 import android.net.ProvisioningConfigurationParcelable;
+import android.net.TcpKeepalivePacketDataParcelable;
 
 /** @hide */
 oneway interface IIpClient {
@@ -29,4 +30,6 @@
     void setTcpBufferSizes(in String tcpBufferSizes);
     void setHttpProxy(in ProxyInfoParcelable proxyInfo);
     void setMulticastFilter(boolean enabled);
+    void addKeepalivePacketFilter(int slot, in TcpKeepalivePacketDataParcelable pkt);
+    void removeKeepalivePacketFilter(int slot);
 }
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 1f33693..a851e04 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -21,13 +21,14 @@
 import android.annotation.WorkerThread;
 
 import java.util.ArrayDeque;
-import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
@@ -190,13 +191,19 @@
 public abstract class AsyncTask<Params, Progress, Result> {
     private static final String LOG_TAG = "AsyncTask";
 
-    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
-    // We want at least 2 threads and at most 4 threads in the core pool,
-    // preferring to have 1 less than the CPU count to avoid saturating
-    // the CPU with background work
-    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
-    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
-    private static final int KEEP_ALIVE_SECONDS = 30;
+    // We keep only a single pool thread around all the time.
+    // We let the pool grow to a fairly large number of threads if necessary,
+    // but let them time out quickly. In the unlikely case that we run out of threads,
+    // we fall back to a simple unbounded-queue executor.
+    // This combination ensures that:
+    // 1. We normally keep few threads (1) around.
+    // 2. We queue only after launching a significantly larger, but still bounded, set of threads.
+    // 3. We keep the total number of threads bounded, but still allow an unbounded set
+    //    of tasks to be queued.
+    private static final int CORE_POOL_SIZE = 1;
+    private static final int MAXIMUM_POOL_SIZE = 20;
+    private static final int BACKUP_POOL_SIZE = 5;
+    private static final int KEEP_ALIVE_SECONDS = 3;
 
     private static final ThreadFactory sThreadFactory = new ThreadFactory() {
         private final AtomicInteger mCount = new AtomicInteger(1);
@@ -206,8 +213,29 @@
         }
     };
 
-    private static final BlockingQueue<Runnable> sPoolWorkQueue =
-            new LinkedBlockingQueue<Runnable>(128);
+    // Used only for rejected executions.
+    // Initialization protected by sRunOnSerialPolicy lock.
+    private static ThreadPoolExecutor sBackupExecutor;
+    private static LinkedBlockingQueue<Runnable> sBackupExecutorQueue;
+
+    private static final RejectedExecutionHandler sRunOnSerialPolicy =
+            new RejectedExecutionHandler() {
+        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
+            android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size");
+            // As a last ditch fallback, run it on an executor with an unbounded queue.
+            // Create this executor lazily, hopefully almost never.
+            synchronized (this) {
+                if (sBackupExecutor == null) {
+                    sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>();
+                    sBackupExecutor = new ThreadPoolExecutor(
+                            BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS,
+                            TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory);
+                    sBackupExecutor.allowCoreThreadTimeOut(true);
+                }
+            }
+            sBackupExecutor.execute(r);
+        }
+    };
 
     /**
      * An {@link Executor} that can be used to execute tasks in parallel.
@@ -217,8 +245,8 @@
     static {
         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                 CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
-                sPoolWorkQueue, sThreadFactory);
-        threadPoolExecutor.allowCoreThreadTimeOut(true);
+                new SynchronousQueue<Runnable>(), sThreadFactory);
+        threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
         THREAD_POOL_EXECUTOR = threadPoolExecutor;
     }
 
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 3a5b8a8..27f7e22 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -24,7 +24,8 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
-import android.os.IBinder.DeathRecipient;
+
+import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
@@ -127,13 +128,16 @@
             @NonNull BugreportParams params,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BugreportCallback callback) {
-        // TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary.
+        Preconditions.checkNotNull(bugreportFd);
+        Preconditions.checkNotNull(params);
+        Preconditions.checkNotNull(executor);
+        Preconditions.checkNotNull(callback);
         DumpstateListener dsListener = new DumpstateListener(executor, callback);
         try {
             // Note: mBinder can get callingUid from the binder transaction.
             mBinder.startBugreport(-1 /* callingUid */,
                     mContext.getOpPackageName(),
-                    (bugreportFd != null ? bugreportFd.getFileDescriptor() : new FileDescriptor()),
+                    bugreportFd.getFileDescriptor(),
                     (screenshotFd != null
                             ? screenshotFd.getFileDescriptor() : new FileDescriptor()),
                     params.getMode(), dsListener);
@@ -154,8 +158,7 @@
         }
     }
 
-    private final class DumpstateListener extends IDumpstateListener.Stub
-            implements DeathRecipient {
+    private final class DumpstateListener extends IDumpstateListener.Stub {
         private final Executor mExecutor;
         private final BugreportCallback mCallback;
 
@@ -165,11 +168,6 @@
         }
 
         @Override
-        public void binderDied() {
-            // TODO(b/111441001): implement
-        }
-
-        @Override
         public void onProgress(int progress) throws RemoteException {
             final long identity = Binder.clearCallingIdentity();
             try {
diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl
deleted file mode 100644
index c07b980..0000000
--- a/core/java/android/os/ParcelFileDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/* //device/java/android/android/os/ParcelFileDescriptor.aidl
-**
-** Copyright 2007, 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.os;
-
-parcelable ParcelFileDescriptor cpp_header "binder/ParcelFileDescriptor.h";
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 8492b0c..3ee54ce 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -32,6 +32,7 @@
 import android.provider.Settings;
 import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
+import android.text.format.DateFormat;
 import android.util.Log;
 import android.view.Display;
 import android.view.WindowManager;
@@ -762,7 +763,8 @@
 
         String reasonArg = null;
         if (!TextUtils.isEmpty(reason)) {
-            reasonArg = "--reason=" + sanitizeArg(reason);
+            String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
+            reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
         }
 
         final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java
index 2593c85..c99ecb32 100644
--- a/core/java/android/os/connectivity/CellularBatteryStats.java
+++ b/core/java/android/os/connectivity/CellularBatteryStats.java
@@ -44,6 +44,7 @@
   private long[] mTimeInRatMs;
   private long[] mTimeInRxSignalStrengthLevelMs;
   private long[] mTxTimeMs;
+  private long mMonitoredRailChargeConsumedMaMs;
 
   public static final Parcelable.Creator<CellularBatteryStats> CREATOR = new
       Parcelable.Creator<CellularBatteryStats>() {
@@ -74,6 +75,7 @@
     out.writeLongArray(mTimeInRatMs);
     out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
     out.writeLongArray(mTxTimeMs);
+    out.writeLong(mMonitoredRailChargeConsumedMaMs);
   }
 
   public void readFromParcel(Parcel in) {
@@ -90,6 +92,7 @@
     in.readLongArray(mTimeInRatMs);
     in.readLongArray(mTimeInRxSignalStrengthLevelMs);
     in.readLongArray(mTxTimeMs);
+    mMonitoredRailChargeConsumedMaMs = in.readLong();
   }
 
   public long getLoggingDurationMs() {
@@ -144,6 +147,10 @@
     return mTxTimeMs;
   }
 
+  public long getMonitoredRailChargeConsumedMaMs() {
+    return mMonitoredRailChargeConsumedMaMs;
+  }
+
   public void setLoggingDurationMs(long t) {
     mLoggingDurationMs = t;
     return;
@@ -211,6 +218,11 @@
     return;
   }
 
+  public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) {
+    mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs;
+    return;
+  }
+
   public int describeContents() {
     return 0;
   }
@@ -237,6 +249,7 @@
     Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
     mTxTimeMs = new long[ModemActivityInfo.TX_POWER_LEVELS];
     Arrays.fill(mTxTimeMs, 0);
+    mMonitoredRailChargeConsumedMaMs = 0;
     return;
   }
 }
\ No newline at end of file
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index de54a8aa..f63c0adb 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -772,19 +772,6 @@
          * @param callBlockReason The reason why the call is blocked.
          * @param callScreeningAppName The call screening application name which block the call.
          * @param callScreeningComponentName The call screening component name which block the call.
-         * @param callIdPackageName The package name of the
-         *      {@link android.telecom.CallScreeningService} which provided
-         *      {@link CallIdentification}.
-         * @param callIdAppName The app name of the {@link android.telecom.CallScreeningService}
-         *                      which provided {@link CallIdentification}.
-         * @param callIdName The caller name provided by the
-         *      {@link android.telecom.CallScreeningService}.
-         * @param callIdDescription The caller description provided by the
-         *      {@link android.telecom.CallScreeningService}.
-         * @param callIdDetails The caller details provided by the
-         *      {@link android.telecom.CallScreeningService}.
-         * @param callIdCallType The caller type provided by the
-         *      {@link android.telecom.CallScreeningService}.
          *
          * @result The URI of the call log entry belonging to the user that made or received this
          *        call.  This could be of the shadow provider.  Do not return it to non-system apps,
@@ -803,37 +790,10 @@
                         number, userToBeInsertedTo, addForAllUsers));
             }
             final ContentResolver resolver = context.getContentResolver();
-            int numberPresentation = PRESENTATION_ALLOWED;
 
-            TelecomManager tm = null;
-            try {
-                tm = TelecomManager.from(context);
-            } catch (UnsupportedOperationException e) {}
+            String accountAddress = getLogAccountAddress(context, accountHandle);
 
-            String accountAddress = null;
-            if (tm != null && accountHandle != null) {
-                PhoneAccount account = tm.getPhoneAccount(accountHandle);
-                if (account != null) {
-                    Uri address = account.getSubscriptionAddress();
-                    if (address != null) {
-                        accountAddress = address.getSchemeSpecificPart();
-                    }
-                }
-            }
-
-            // Remap network specified number presentation types
-            // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
-            // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
-            // from any future radio changes.
-            // If the number field is empty set the presentation type to Unknown.
-            if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
-                numberPresentation = PRESENTATION_RESTRICTED;
-            } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
-                numberPresentation = PRESENTATION_PAYPHONE;
-            } else if (TextUtils.isEmpty(number)
-                    || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
-                numberPresentation = PRESENTATION_UNKNOWN;
-            }
+            int numberPresentation = getLogNumberPresentation(number, presentation);
             if (numberPresentation != PRESENTATION_ALLOWED) {
                 number = "";
                 if (ci != null) {
@@ -1138,8 +1098,7 @@
             if (TextUtils.isEmpty(countryIso)) {
                 return;
             }
-            final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number,
-                    getCurrentCountryIso(context));
+            final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
             if (TextUtils.isEmpty(normalizedNumber)) {
                 return;
             }
@@ -1148,6 +1107,54 @@
             resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId});
         }
 
+        /**
+         * Remap network specified number presentation types
+         * PhoneConstants.PRESENTATION_xxx to calllog number presentation types
+         * Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
+         * from any future radio changes.
+         * If the number field is empty set the presentation type to Unknown.
+         */
+        private static int getLogNumberPresentation(String number, int presentation) {
+            if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
+                return presentation;
+            }
+
+            if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
+                return presentation;
+            }
+
+            if (TextUtils.isEmpty(number)
+                    || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
+                return PRESENTATION_UNKNOWN;
+            }
+
+            return PRESENTATION_ALLOWED;
+        }
+
+        private static String getLogAccountAddress(Context context,
+                PhoneAccountHandle accountHandle) {
+            TelecomManager tm = null;
+            try {
+                tm = TelecomManager.from(context);
+            } catch (UnsupportedOperationException e) {
+                if (VERBOSE_LOG) {
+                    Log.v(LOG_TAG, "No TelecomManager found to get account address.");
+                }
+            }
+
+            String accountAddress = null;
+            if (tm != null && accountHandle != null) {
+                PhoneAccount account = tm.getPhoneAccount(accountHandle);
+                if (account != null) {
+                    Uri address = account.getSubscriptionAddress();
+                    if (address != null) {
+                        accountAddress = address.getSchemeSpecificPart();
+                    }
+                }
+            }
+            return accountAddress;
+        }
+
         private static String getCurrentCountryIso(Context context) {
             String countryIso = null;
             final CountryDetector detector = (CountryDetector) context.getSystemService(
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e904b07..abd7c89 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12862,6 +12862,19 @@
                 "user_preferred_sub2","user_preferred_sub3"};
 
         /**
+         * Which subscription is enabled for a physical slot.
+         * @hide
+         */
+        public static final String ENABLED_SUBSCRIPTION_FOR_SLOT = "enabled_subscription_for_slot";
+
+        /**
+         * Whether corresponding logical modem is enabled for a physical slot.
+         * The value 1 - enable, 0 - disable
+         * @hide
+         */
+        public static final String MODEM_STACK_ENABLED_FOR_SLOT = "modem_stack_enabled_for_slot";
+
+        /**
          * Whether to enable new contacts aggregator or not.
          * The value 1 - enable, 0 - disable
          * @hide
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index fda0a3e..15c14e7 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -30,8 +30,7 @@
 import android.icu.util.ULocale;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemProperties;
-import android.provider.Settings;
+import android.sysprop.DisplayProperties;
 import android.text.style.AbsoluteSizeSpan;
 import android.text.style.AccessibilityClickableSpan;
 import android.text.style.AccessibilityURLSpan;
@@ -2001,7 +2000,7 @@
         return ((locale != null && !locale.equals(Locale.ROOT)
                         && ULocale.forLocale(locale).isRightToLeft())
                 // If forcing into RTL layout mode, return RTL as default
-                || SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false))
+                || DisplayProperties.debug_force_rtl().orElse(false))
             ? View.LAYOUT_DIRECTION_RTL
             : View.LAYOUT_DIRECTION_LTR;
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f228773..efff6d6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -74,8 +74,8 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.Trace;
+import android.sysprop.DisplayProperties;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -790,14 +790,6 @@
     protected static final String VIEW_LOG_TAG = "View";
 
     /**
-     * When set to true, apps will draw debugging information about their layouts.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static final String DEBUG_LAYOUT_PROPERTY = "debug.layout";
-
-    /**
      * When set to true, this view will save its attribute data.
      *
      * @hide
@@ -26855,7 +26847,7 @@
         /**
          * Show where the margins, bounds and layout bounds are for each view.
          */
-        boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false);
+        boolean mDebugLayout = DisplayProperties.debug_layout().orElse(false);
 
         /**
          * Point used to compute visible regions.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fd11ef1..798401d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -70,6 +70,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.sysprop.DisplayProperties;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -6829,7 +6830,7 @@
                 }
 
                 // Layout debugging
-                boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false);
+                boolean layout = DisplayProperties.debug_layout().orElse(false);
                 if (layout != mAttachInfo.mDebugLayout) {
                     mAttachInfo.mDebugLayout = layout;
                     if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {
diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS
new file mode 100644
index 0000000..265674a
--- /dev/null
+++ b/core/java/android/view/accessibility/OWNERS
@@ -0,0 +1,3 @@
+svetoslavganov@google.com
+pweaver@google.com
+rhedjao@google.com
diff --git a/core/java/android/webkit/OWNERS b/core/java/android/webkit/OWNERS
index 00e540a..b33da57 100644
--- a/core/java/android/webkit/OWNERS
+++ b/core/java/android/webkit/OWNERS
@@ -1,3 +1,4 @@
 changwan@google.com
+ntfschr@google.com
 tobiasjs@google.com
 torne@google.com
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 1048cb4..56eb128 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -93,6 +93,11 @@
      */
     public static final int PROFILE_SYSTEM_SERVER = 1 << 14;
 
+    /*
+     * Enable using the ART app image startup cache
+     */
+    public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
+
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
     /** Default external storage should be mounted. */
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 2bb0759..d067ae7 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -315,9 +315,14 @@
         }
     }
 
-    private class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger {
+    private static class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger {
 
         private final MetricsLogger mMetricsLogger = new MetricsLogger();
+        private static HiddenApiUsageLogger sInstance = new HiddenApiUsageLogger();
+
+        public static HiddenApiUsageLogger getInstance() {
+            return HiddenApiUsageLogger.sInstance;
+        }
 
         public void hiddenApiUsed(String packageName, String signature,
                 int accessMethod, boolean accessDenied) {
@@ -351,7 +356,7 @@
     private void handleHiddenApiAccessLogSampleRate(int samplingRate) {
         try {
             ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate);
-            ZygoteInit.setHiddenApiUsageLogger(new HiddenApiUsageLogger());
+            ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance());
             mSocketOutStream.writeInt(0);
         } catch (IOException ioe) {
             throw new IllegalStateException("Error writing to command socket", ioe);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9f23797..e132abd 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -41,6 +41,7 @@
 import android.system.StructCapUserData;
 import android.system.StructCapUserHeader;
 import android.text.Hyphenator;
+import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -84,6 +85,8 @@
 
     private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+    private static final String PROPERTY_USE_APP_IMAGE_STARTUP_CACHE =
+            "persist.device_config.runtime_native.use_app_image_startup_cache";
 
     private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
     private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
@@ -705,6 +708,13 @@
                 parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
             }
 
+            String use_app_image_cache = SystemProperties.get(
+                    PROPERTY_USE_APP_IMAGE_STARTUP_CACHE, "");
+            // Property defaults to true currently.
+            if (!TextUtils.isEmpty(use_app_image_cache) && !use_app_image_cache.equals("false")) {
+                parsedArgs.mRuntimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
+            }
+
             /* Request to fork the system server process */
             pid = Zygote.forkSystemServer(
                     parsedArgs.mUid, parsedArgs.mGid,
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c81a77d..08a5789 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -278,8 +278,8 @@
         "libsoundtrigger",
         "libminikin",
         "libprocessgroup",
-        "libnativebridge",
-        "libnativeloader",
+        "libnativebridge_lazy",
+        "libnativeloader_lazy",
         "libmemunreachable",
         "libhidlbase",
         "libhidltransport",
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 86342c4..774c224 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -3,7 +3,7 @@
 per-file *Camera*,*camera* = shuzhenwang@google.com, yinchiayeh@google.com, zhijunhe@google.com
 
 # Connectivity
-per-file android_net_* = ek@google.com, lorenzo@google.com, satk@google.com
+per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
 
 # Zygote
 per-file com_android_inernal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 7eddcfe..cfb2dd1 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -29,6 +29,7 @@
 #include <net/if.h>
 #include <linux/filter.h>
 #include <linux/if_arp.h>
+#include <linux/tcp.h>
 #include <netinet/ether.h>
 #include <netinet/icmp6.h>
 #include <netinet/ip.h>
@@ -226,6 +227,34 @@
     }
 }
 
+static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
+{
+    struct sock_filter filter_code[] = {
+        // Reject all.
+        BPF_STMT(BPF_RET | BPF_K, 0)
+    };
+    struct sock_fprog filter = {
+        sizeof(filter_code) / sizeof(filter_code[0]),
+        filter_code,
+    };
+
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+    }
+}
+
+static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
+{
+    int dummy = 0;
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &dummy, sizeof(dummy)) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(SO_DETACH_FILTER): %s", strerror(errno));
+    }
+
+}
 static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
         jint ifIndex)
 {
@@ -458,6 +487,41 @@
     return answer;
 }
 
+static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
+    if (javaFd == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return NULL;
+    }
+
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    struct tcp_repair_window trw = {};
+    socklen_t size = sizeof(trw);
+
+    // Obtain the parameters of the TCP repair window.
+    int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size);
+    if (rc == -1) {
+      throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
+      return NULL;
+    }
+
+    struct tcp_info tcpinfo = {};
+    socklen_t tcpinfo_size = sizeof(tcp_info);
+
+    // Obtain the window scale from the tcp info structure. This contains a scale factor that
+    // should be applied to the window size.
+    rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size);
+    if (rc == -1) {
+      throwErrnoException(env, "getsockopt : TCP_INFO", errno);
+      return NULL;
+    }
+
+    jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow");
+    jmethodID ctor = env->GetMethodID(class_TcpRepairWindow, "<init>", "(IIIIII)V");
+
+    return env->NewObject(class_TcpRepairWindow, ctor, trw.snd_wl1, trw.snd_wnd, trw.max_window,
+            trw.rcv_wnd, trw.rcv_wup, tcpinfo.tcpi_rcv_wscale);
+}
+
 // ----------------------------------------------------------------------------
 
 /*
@@ -475,6 +539,9 @@
     { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
     { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
     { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
+    { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
+    { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
+    { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
     { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
     { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
     { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto
index a88a06c..b4f3d1e 100644
--- a/core/proto/android/bluetooth/enums.proto
+++ b/core/proto/android/bluetooth/enums.proto
@@ -110,3 +110,31 @@
     UNBOND_REASON_REMOTE_AUTH_CANCELED = 8;
     UNBOND_REASON_REMOVED = 9;
 }
+
+enum SocketTypeEnum {
+    SOCKET_TYPE_UNKNOWN = 0;
+    SOCKET_TYPE_RFCOMM = 1;
+    SOCKET_TYPE_SCO = 2;
+    SOCKET_TYPE_L2CAP_BREDR = 3;
+    SOCKET_TYPE_L2CAP_LE = 4;
+}
+
+enum SocketConnectionstateEnum {
+    SOCKET_CONNECTION_STATE_UNKNOWN = 0;
+    // Socket acts as a server waiting for connection
+    SOCKET_CONNECTION_STATE_LISTENING = 1;
+    // Socket acts as a client trying to connect
+    SOCKET_CONNECTION_STATE_CONNECTING = 2;
+    // Socket is connected
+    SOCKET_CONNECTION_STATE_CONNECTED = 3;
+    // Socket tries to disconnect from remote
+    SOCKET_CONNECTION_STATE_DISCONNECTING = 4;
+    // This socket is closed
+    SOCKET_CONNECTION_STATE_DISCONNECTED = 5;
+}
+
+enum SocketRoleEnum {
+    SOCKET_ROLE_UNKNOWN = 0;
+    SOCKET_ROLE_LISTEN = 1;
+    SOCKET_ROLE_CONNECTION = 2;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9a7e8dd..7590675 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -481,6 +481,7 @@
     <protected-broadcast android:name="android.security.action.TRUST_STORE_CHANGED" />
     <protected-broadcast android:name="android.security.action.KEYCHAIN_CHANGED" />
     <protected-broadcast android:name="android.security.action.KEY_ACCESS_CHANGED" />
+    <protected-broadcast android:name="android.telecom.action.NUISANCE_CALL_STATUS_CHANGED" />
     <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED" />
     <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED" />
     <protected-broadcast android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 212c723..94ba172 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -505,7 +505,9 @@
                     Settings.Global.OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION,
                     Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
                     Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
-                    Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS);
+                    Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
+                    Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT,
+                    Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT);
     private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9a148e4..4b247c6 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -158,6 +158,7 @@
         <permission name="android.permission.WRITE_APN_SETTINGS"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
         <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
+        <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
         <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
         <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
     </privapp-permissions>
@@ -201,21 +202,14 @@
 
     <privapp-permissions package="com.android.mainline.networkstack">
         <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
-        <permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/>
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
         <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
         <permission name="android.permission.CONTROL_VPN"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
-        <permission name="android.permission.MANAGE_IPSEC_TUNNELS"/>
-        <permission name="android.permission.MANAGE_NETWORK_POLICY"/>
         <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/>
         <permission name="android.permission.MANAGE_USB"/>
-        <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/>
-        <permission name="android.permission.NETWORK_SETTINGS"/>
-        <permission name="android.permission.NETWORK_STACK" />
-        <permission name="android.permission.NET_TUNNELING"/>
         <permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
-        <permission name="android.permission.PEERS_MAC_ADDRESS"/>
         <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
         <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
@@ -253,6 +247,7 @@
         <permission name="android.permission.CHANGE_LOWPAN_STATE"/>
         <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
         <permission name="android.permission.CLEAR_APP_CACHE"/>
+        <permission name="android.permission.ACCESS_INSTANT_APPS" />
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
         <permission name="android.permission.DELETE_CACHE_FILES"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index ba72e93..0b9ae5c 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -23,7 +23,7 @@
 
 class FrameMetricsObserver : public VirtualLightRefBase {
 public:
-    virtual void notify(const int64_t* buffer);
+    virtual void notify(const int64_t* buffer) = 0;
 };
 
 };  // namespace uirenderer
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index ac55bfa..e4d3591 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -27,8 +27,8 @@
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <!-- Signature permission defined in NetworkStackStub -->
     <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
-    <!-- Launch captive portal app as specific user -->
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <!-- Send latency broadcast as current user -->
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.NETWORK_STACK" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <application
diff --git a/packages/NetworkStack/OWNERS b/packages/NetworkStack/OWNERS
new file mode 100644
index 0000000..a395465
--- /dev/null
+++ b/packages/NetworkStack/OWNERS
@@ -0,0 +1,5 @@
+codewiz@google.com
+jchalard@google.com
+lorenzo@google.com
+reminv@google.com
+satk@google.com
diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
index 4fa7d64..923f162 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
@@ -23,6 +23,7 @@
 import static android.system.OsConstants.ETH_P_IP;
 import static android.system.OsConstants.ETH_P_IPV6;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_RAW;
 
@@ -38,6 +39,7 @@
 import android.content.IntentFilter;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.TcpKeepalivePacketDataParcelable;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
 import android.net.apf.ApfGenerator.Register;
 import android.net.ip.IpClient.IpClientCallbacksWrapper;
@@ -55,6 +57,7 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.Pair;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -149,7 +152,9 @@
         DROPPED_IPV6_NON_ICMP_MULTICAST,
         DROPPED_802_3_FRAME,
         DROPPED_ETHERTYPE_BLACKLISTED,
-        DROPPED_ARP_REPLY_SPA_NO_HOST;
+        DROPPED_ARP_REPLY_SPA_NO_HOST,
+        DROPPED_IPV4_KEEPALIVE_ACK,
+        DROPPED_IPV6_KEEPALIVE_ACK;
 
         // Returns the negative byte offset from the end of the APF data segment for
         // a given counter.
@@ -285,6 +290,7 @@
     private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
     private static final int IPV4_ANY_HOST_ADDRESS = 0;
     private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
+    private static final int IPV4_HEADER_LEN = 20; // Without options
 
     // Traffic class and Flow label are not byte aligned. Luckily we
     // don't care about either value so we'll consider bytes 1-3 of the
@@ -305,6 +311,8 @@
     private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
     private static final int UDP_HEADER_LEN = 8;
 
+    private static final int TCP_HEADER_SIZE_OFFSET = 12;
+
     private static final int DHCP_CLIENT_PORT = 68;
     // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
     private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
@@ -788,7 +796,7 @@
 
         boolean isExpired() {
             // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
-            // have to calculte the filter lifetime specially as a fraction of 0 is still 0.
+            // have to calculate the filter lifetime specially as a fraction of 0 is still 0.
             return currentLifetime() <= 0;
         }
 
@@ -847,11 +855,147 @@
         }
     }
 
+    // A class to hold keepalive ack information.
+    private abstract static class TcpKeepaliveAck {
+        // Note that the offset starts from IP header.
+        // These must be added ether header length when generating program.
+        static final int IP_HEADER_OFFSET = 0;
+
+        protected static class TcpKeepaliveAckData {
+            public final byte[] srcAddress;
+            public final int srcPort;
+            public final byte[] dstAddress;
+            public final int dstPort;
+            public final int seq;
+            public final int ack;
+            // Create the characteristics of the ack packet from the sent keepalive packet.
+            TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+                srcAddress = sentKeepalivePacket.dstAddress;
+                srcPort = sentKeepalivePacket.dstPort;
+                dstAddress = sentKeepalivePacket.srcAddress;
+                dstPort = sentKeepalivePacket.srcPort;
+                seq = sentKeepalivePacket.ack;
+                ack = sentKeepalivePacket.seq + 1;
+            }
+        }
+
+        protected final TcpKeepaliveAckData mPacket;
+        protected final byte[] mSrcDstAddr;
+
+        TcpKeepaliveAck(final TcpKeepaliveAckData packet, final byte[] srcDstAddr) {
+            mPacket = packet;
+            mSrcDstAddr = srcDstAddr;
+        }
+
+        static byte[] concatArrays(final byte[]... arr) {
+            int size = 0;
+            for (byte[] a : arr) {
+                size += a.length;
+            }
+            final byte[] result = new byte[size];
+            int offset = 0;
+            for (byte[] a : arr) {
+                System.arraycopy(a, 0, result, offset, a.length);
+                offset += a.length;
+            }
+            return result;
+        }
+
+        public String toString() {
+            return String.format("%s(%d) -> %s(%d), seq=%d, ack=%d",
+                    mPacket.srcAddress,
+                    mPacket.srcPort,
+                    mPacket.dstAddress,
+                    mPacket.dstPort,
+                    mPacket.seq,
+                    mPacket.ack);
+        }
+
+        // Append a filter for this keepalive ack to {@code gen}.
+        // Jump to drop if it matches the keepalive ack.
+        // Jump to the next filter if packet doesn't match the keepalive ack.
+        abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
+    }
+
+    private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
+        private static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
+        private static final int IPV4_TCP_SRC_PORT_OFFSET = 0;
+        private static final int IPV4_TCP_DST_PORT_OFFSET = 2;
+        private static final int IPV4_TCP_SEQ_OFFSET = 4;
+        private static final int IPV4_TCP_ACK_OFFSET = 8;
+
+        TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+            this(new TcpKeepaliveAckData(sentKeepalivePacket));
+        }
+        TcpKeepaliveAckV4(final TcpKeepaliveAckData packet) {
+            super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
+        }
+
+        @Override
+        void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+            final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked();
+            gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
+            gen.addJumpIfR0NotEquals(IPPROTO_TCP, nextFilterLabel);
+            gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
+            gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
+
+            // Pass the packet if it's not zero-sized :
+            // Load the IP header size into R1
+            gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+            // Load the TCP header size into R0 (it's indexed by R1)
+            gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET);
+            // Size offset is in the top nibble, but it must be multiplied by 4, and the two
+            // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2.
+            gen.addRightShift(2);
+            // R0 += R1 -> R0 contains TCP + IP headers lenght
+            gen.addAddR1();
+            // Add the Ethernet header length to R0.
+            gen.addLoadImmediate(Register.R1, ETH_HEADER_LEN);
+            gen.addAddR1();
+            // Compare total length of headers to the size of the packet.
+            gen.addLoadFromMemory(Register.R1, gen.PACKET_SIZE_MEMORY_SLOT);
+            gen.addNeg(Register.R0);
+            gen.addAddR1();
+            gen.addJumpIfR0NotEquals(0, nextFilterLabel);
+
+            // Add IPv4 header length
+            gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+            gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SRC_PORT_OFFSET);
+            gen.addJumpIfR0NotEquals(mPacket.srcPort, nextFilterLabel);
+            gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_DST_PORT_OFFSET);
+            gen.addJumpIfR0NotEquals(mPacket.dstPort, nextFilterLabel);
+            gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SEQ_OFFSET);
+            gen.addJumpIfR0NotEquals(mPacket.seq, nextFilterLabel);
+            gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_ACK_OFFSET);
+            gen.addJumpIfR0NotEquals(mPacket.ack, nextFilterLabel);
+
+            maybeSetupCounter(gen, Counter.DROPPED_IPV4_KEEPALIVE_ACK);
+            gen.addJump(mCountAndDropLabel);
+            gen.defineLabel(nextFilterLabel);
+        }
+    }
+
+    private class TcpKeepaliveAckV6 extends TcpKeepaliveAck {
+        TcpKeepaliveAckV6(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+            this(new TcpKeepaliveAckData(sentKeepalivePacket));
+        }
+        TcpKeepaliveAckV6(final TcpKeepaliveAckData packet) {
+            super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
+        }
+
+        @Override
+        void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+            throw new UnsupportedOperationException("IPv6 Keepalive is not supported yet");
+        }
+    }
+
     // Maximum number of RAs to filter for.
     private static final int MAX_RAS = 10;
 
     @GuardedBy("this")
-    private ArrayList<Ra> mRas = new ArrayList<Ra>();
+    private ArrayList<Ra> mRas = new ArrayList<>();
+    @GuardedBy("this")
+    private SparseArray<TcpKeepaliveAck> mKeepaliveAcks = new SparseArray<>();
 
     // There is always some marginal benefit to updating the installed APF program when an RA is
     // seen because we can extend the program's lifetime slightly, but there is some cost to
@@ -980,6 +1124,8 @@
         //     drop
         //   if it's IPv4 broadcast:
         //     drop
+        // if keepalive ack
+        //   drop
         // pass
 
         if (mMulticastFilter) {
@@ -1023,6 +1169,9 @@
                 gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
             }
 
+            // If any keepalive filters,
+            generateKeepaliveFilter(gen);
+
             // If L2 broadcast packet, drop.
             // TODO: can we invert this condition to fall through to the common pass case below?
             maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST);
@@ -1030,6 +1179,8 @@
             gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
             maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
             gen.addJump(mCountAndDropLabel);
+        } else {
+            generateKeepaliveFilter(gen);
         }
 
         // Otherwise, pass
@@ -1037,6 +1188,13 @@
         gen.addJump(mCountAndPassLabel);
     }
 
+    private void generateKeepaliveFilter(ApfGenerator gen) throws IllegalInstructionException {
+        // Drop IPv4 Keepalive acks
+        for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
+            final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
+            if (ack instanceof TcpKeepaliveAckV4) ack.generateFilterLocked(gen);
+        }
+    }
 
     /**
      * Generate filter code to process IPv6 packets. Execution of this code ends in either the
@@ -1057,6 +1215,8 @@
         //   drop
         // if it's ICMPv6 NA to ff02::1:
         //   drop
+        // if keepalive ack
+        //   drop
 
         gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
 
@@ -1112,6 +1272,12 @@
         maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
         gen.addJump(mCountAndDropLabel);
         gen.defineLabel(skipUnsolicitedMulticastNALabel);
+
+        // Drop IPv6 Keepalive acks
+        for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
+            final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
+            if (ack instanceof TcpKeepaliveAckV6) ack.generateFilterLocked(gen);
+        }
     }
 
     /**
@@ -1489,6 +1655,36 @@
         installNewProgramLocked();
     }
 
+    /**
+     * Add keepalive ack packet filter.
+     * This will add a filter to drop acks to the keepalive packet passed as an argument.
+     *
+     * @param slot The index used to access the filter.
+     * @param sentKeepalivePacket The attributes of the sent keepalive packet.
+     */
+    public synchronized void addKeepalivePacketFilter(final int slot,
+            final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+        log("Adding keepalive ack(" + slot + ")");
+        if (null != mKeepaliveAcks.get(slot)) {
+            throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied");
+        }
+        final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6;
+        mKeepaliveAcks.put(slot, (ipVersion == 4)
+                ? new TcpKeepaliveAckV4(sentKeepalivePacket)
+                : new TcpKeepaliveAckV6(sentKeepalivePacket));
+        installNewProgramLocked();
+    }
+
+    /**
+     * Remove keepalive packet filter.
+     *
+     * @param slot The index used to access the filter.
+     */
+    public synchronized void removeKeepalivePacketFilter(int slot) {
+        mKeepaliveAcks.remove(slot);
+        installNewProgramLocked();
+    }
+
     static public long counterValue(byte[] data, Counter counter)
             throws ArrayIndexOutOfBoundsException {
         // Follow the same wrap-around addressing scheme of the interpreter.
@@ -1541,6 +1737,17 @@
         }
         pw.decreaseIndent();
 
+        pw.println("Keepalive filter:");
+        pw.increaseIndent();
+        for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
+            final TcpKeepaliveAck keepaliveAck = mKeepaliveAcks.valueAt(i);
+            pw.print("Slot ");
+            pw.print(mKeepaliveAcks.keyAt(i));
+            pw.print(" : ");
+            pw.println(keepaliveAck);
+        }
+        pw.decreaseIndent();
+
         if (DBG) {
             pw.println("Last program:");
             pw.increaseIndent();
diff --git a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
index 87a1b5e..809327a 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
@@ -476,7 +476,7 @@
 
     /**
      * Add an instruction to the end of the program to load 16-bits from the packet into
-     * {@code register}. The offset of the loaded 16-bits from the begining of the packet is
+     * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is
      * the sum of {@code offset} and the value in register R1.
      */
     public ApfGenerator addLoad16Indexed(Register register, int offset) {
@@ -488,7 +488,7 @@
 
     /**
      * Add an instruction to the end of the program to load 32-bits from the packet into
-     * {@code register}. The offset of the loaded 32-bits from the begining of the packet is
+     * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is
      * the sum of {@code offset} and the value in register R1.
      */
     public ApfGenerator addLoad32Indexed(Register register, int offset) {
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index 12fe8c5..9e59912 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -23,6 +23,7 @@
 
 import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.DhcpResults;
@@ -34,6 +35,7 @@
 import android.net.ProxyInfo;
 import android.net.ProxyInfoParcelable;
 import android.net.RouteInfo;
+import android.net.TcpKeepalivePacketDataParcelable;
 import android.net.apf.ApfCapabilities;
 import android.net.apf.ApfFilter;
 import android.net.dhcp.DhcpClient;
@@ -292,6 +294,8 @@
     private static final int EVENT_PROVISIONING_TIMEOUT           = 10;
     private static final int EVENT_DHCPACTION_TIMEOUT             = 11;
     private static final int EVENT_READ_PACKET_FILTER_COMPLETE    = 12;
+    private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13;
+    private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14;
 
     // Internal commands to use instead of trying to call transitionTo() inside
     // a given State's enter() method. Calling transitionTo() from enter/exit
@@ -522,6 +526,16 @@
             checkNetworkStackCallingPermission();
             IpClient.this.setMulticastFilter(enabled);
         }
+        @Override
+        public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
+            checkNetworkStackCallingPermission();
+            IpClient.this.addKeepalivePacketFilter(slot, pkt);
+        }
+        @Override
+        public void removeKeepalivePacketFilter(int slot) {
+            checkNetworkStackCallingPermission();
+            IpClient.this.removeKeepalivePacketFilter(slot);
+        }
     }
 
     public String getInterfaceName() {
@@ -644,6 +658,22 @@
     }
 
     /**
+     * Called by WifiStateMachine to add keepalive packet filter before setting up
+     * keepalive offload.
+     */
+    public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) {
+        sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt);
+    }
+
+    /**
+     * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive
+     * offload.
+     */
+    public void removeKeepalivePacketFilter(int slot) {
+        sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */);
+    }
+
+    /**
      * Dump logs of this IpClient.
      */
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -1512,6 +1542,23 @@
                     break;
                 }
 
+                case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: {
+                    final int slot = msg.arg1;
+                    if (mApfFilter != null) {
+                        mApfFilter.addKeepalivePacketFilter(slot,
+                                (TcpKeepalivePacketDataParcelable) msg.obj);
+                    }
+                    break;
+                }
+
+                case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: {
+                    final int slot = msg.arg1;
+                    if (mApfFilter != null) {
+                        mApfFilter.removeKeepalivePacketFilter(slot);
+                    }
+                    break;
+                }
+
                 case EVENT_DHCPACTION_TIMEOUT:
                     stopDhcpAction();
                     break;
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index dbffa6d..2e72d82 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -61,6 +61,7 @@
 import android.net.util.Stopwatch;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.os.Bundle;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -499,7 +500,7 @@
 
     private void showProvisioningNotification(String action) {
         try {
-            mCallback.showProvisioningNotification(action);
+            mCallback.showProvisioningNotification(action, mContext.getPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Error showing provisioning notification", e);
         }
@@ -674,11 +675,11 @@
         public boolean processMessage(Message message) {
             switch (message.what) {
                 case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
-                    final Intent intent = new Intent(
-                            ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+                    final Bundle appExtras = new Bundle();
                     // OneAddressPerFamilyNetwork is not parcelable across processes.
-                    intent.putExtra(ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
-                    intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
+                    appExtras.putParcelable(
+                            ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
+                    appExtras.putParcelable(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
                             new CaptivePortal(new ICaptivePortal.Stub() {
                                 @Override
                                 public void appResponse(int response) {
@@ -700,16 +701,14 @@
                                 }
                             }));
                     final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
-                    intent.putExtra(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
+                    appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
                     if (probeRes.probeSpec != null) {
                         final String encodedSpec = probeRes.probeSpec.getEncodedSpec();
-                        intent.putExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
+                        appExtras.putString(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
                     }
-                    intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
+                    appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
                             mCaptivePortalUserAgent);
-                    intent.setFlags(
-                            Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
-                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                    mCm.startCaptivePortalApp(appExtras);
                     return HANDLED;
                 default:
                     return NOT_HANDLED;
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
index 45fa2dc..4a09b3e 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -49,6 +49,7 @@
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
+        "libjsoncpp",
         "liblog",
         "liblzma",
         "libnativehelper",
diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index f76e412..a4a1000 100644
--- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -39,13 +39,14 @@
 import android.content.Context;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.SocketKeepalive;
+import android.net.TcpKeepalivePacketData;
+import android.net.TcpKeepalivePacketData.TcpSocketInfo;
 import android.net.apf.ApfFilter.ApfConfiguration;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
 import android.net.apf.ApfGenerator.Register;
 import android.net.ip.IIpClientCallbacks;
-import android.net.ip.IpClient;
 import android.net.ip.IpClient.IpClientCallbacksWrapper;
-import android.net.ip.IpClientCallbacks;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.RaEvent;
 import android.net.util.InterfaceParams;
@@ -1003,15 +1004,31 @@
     private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
             {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
 
+    private static final int IPV4_HEADER_LEN = 20;
     private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
+    private static final int IPV4_TOTAL_LENGTH_OFFSET  = ETH_HEADER_LEN + 2;
     private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
+    private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
     private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
+    private static final int IPV4_TCP_HEADER_LEN = 20;
+    private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
+    private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0;
+    private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2;
+    private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
+    private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
+    private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
     private static final byte[] IPV4_BROADCAST_ADDRESS =
             {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
 
     private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
     private static final int IPV6_HEADER_LEN = 40;
+    private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
     private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
+    private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+    private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0;
+    private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2;
+    private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4;
+    private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8;
     // The IPv6 all nodes address ff02::1
     private static final byte[] IPV6_ALL_NODES_ADDRESS =
             { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
@@ -1491,6 +1508,200 @@
         return packet.array();
     }
 
+    private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 5};
+    private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 6};
+    private static final byte[] IPV4_ANOTHER_ADDR = {10, 0 , 0, 7};
+    private static final byte[] IPV6_KEEPALIVE_SRC_ADDR =
+            {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf1};
+    private static final byte[] IPV6_KEEPALIVE_DST_ADDR =
+            {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf2};
+    private static final byte[] IPV6_ANOTHER_ADDR =
+            {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf5};
+
+    @Test
+    public void testApfFilterKeepaliveAck() throws Exception {
+        final MockIpClientCallback cb = new MockIpClientCallback();
+        final ApfConfiguration config = getDefaultConfig();
+        config.multicastFilter = DROP_MULTICAST;
+        config.ieee802_3Filter = DROP_802_3_FRAMES;
+        final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
+        byte[] program;
+        final int srcPort = 12345;
+        final int dstPort = 54321;
+        final int seqNum = 2123456789;
+        final int ackNum = 1234567890;
+        final int anotherSrcPort = 23456;
+        final int anotherDstPort = 65432;
+        final int anotherSeqNum = 2123456780;
+        final int anotherAckNum = 1123456789;
+        final int slot1 = 1;
+        final int slot2 = 2;
+        final int window = 14480;
+        final int windowScale = 4;
+
+        // src: 10.0.0.5, port: 12345
+        // dst: 10.0.0.6, port: 54321
+        InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
+        InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
+
+        final TcpSocketInfo v4Tsi = new TcpSocketInfo(
+                srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
+        final TcpKeepalivePacketData ipv4TcpKeepalivePacket =
+                TcpKeepalivePacketData.tcpKeepalivePacket(v4Tsi);
+
+        apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
+        program = cb.getApfProgram();
+
+        // Verify IPv4 keepalive ack packet is dropped
+        // src: 10.0.0.6, port: 54321
+        // dst: 10.0.0.5, port: 12345
+        assertDrop(program,
+                ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+                        dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
+        // Verify IPv4 non-keepalive ack packet from the same source address is passed
+        assertPass(program,
+                ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+                        dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
+        assertPass(program,
+                ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+                        dstPort, srcPort, ackNum, seqNum + 1, 10 /* dataLength */));
+        // Verify IPv4 packet from another address is passed
+        assertPass(program,
+                ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+                        anotherDstPort, anotherSeqNum, anotherAckNum));
+
+        // Remove IPv4 keepalive filter
+        apfFilter.removeKeepalivePacketFilter(slot1);
+
+        try {
+            // src: 2404:0:0:0:0:0:faf1, port: 12345
+            // dst: 2404:0:0:0:0:0:faf2, port: 54321
+            srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR);
+            dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR);
+            final TcpSocketInfo v6Tsi = new TcpSocketInfo(
+                    srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale);
+            final TcpKeepalivePacketData ipv6TcpKeepalivePacket =
+                    TcpKeepalivePacketData.tcpKeepalivePacket(v6Tsi);
+            apfFilter.addKeepalivePacketFilter(slot1, ipv6TcpKeepalivePacket.toStableParcelable());
+            program = cb.getApfProgram();
+
+            // Verify IPv6 keepalive ack packet is dropped
+            // src: 2404:0:0:0:0:0:faf2, port: 54321
+            // dst: 2404:0:0:0:0:0:faf1, port: 12345
+            assertDrop(program,
+                    ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+                            dstPort, srcPort, ackNum, seqNum + 1));
+            // Verify IPv6 non-keepalive ack packet from the same source address is passed
+            assertPass(program,
+                    ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+                            dstPort, srcPort, ackNum + 100, seqNum));
+            // Verify IPv6 packet from another address is passed
+            assertPass(program,
+                    ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+                            anotherDstPort, anotherSeqNum, anotherAckNum));
+
+            // Remove IPv6 keepalive filter
+            apfFilter.removeKeepalivePacketFilter(slot1);
+
+            // Verify multiple filters
+            apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable());
+            apfFilter.addKeepalivePacketFilter(slot2, ipv6TcpKeepalivePacket.toStableParcelable());
+            program = cb.getApfProgram();
+
+            // Verify IPv4 keepalive ack packet is dropped
+            // src: 10.0.0.6, port: 54321
+            // dst: 10.0.0.5, port: 12345
+            assertDrop(program,
+                    ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+                            dstPort, srcPort, ackNum, seqNum + 1));
+            // Verify IPv4 non-keepalive ack packet from the same source address is passed
+            assertPass(program,
+                    ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+                            dstPort, srcPort, ackNum + 100, seqNum));
+            // Verify IPv4 packet from another address is passed
+            assertPass(program,
+                    ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+                            anotherDstPort, anotherSeqNum, anotherAckNum));
+
+            // Verify IPv6 keepalive ack packet is dropped
+            // src: 2404:0:0:0:0:0:faf2, port: 54321
+            // dst: 2404:0:0:0:0:0:faf1, port: 12345
+            assertDrop(program,
+                    ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+                            dstPort, srcPort, ackNum, seqNum + 1));
+            // Verify IPv6 non-keepalive ack packet from the same source address is passed
+            assertPass(program,
+                    ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+                            dstPort, srcPort, ackNum + 100, seqNum));
+            // Verify IPv6 packet from another address is passed
+            assertPass(program,
+                    ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+                            anotherDstPort, anotherSeqNum, anotherAckNum));
+
+            // Remove keepalive filters
+            apfFilter.removeKeepalivePacketFilter(slot1);
+            apfFilter.removeKeepalivePacketFilter(slot2);
+        } catch (SocketKeepalive.InvalidPacketException e) {
+            // TODO: support V6 packets
+        }
+
+        program = cb.getApfProgram();
+
+        // Verify IPv4, IPv6 packets are passed
+        assertPass(program,
+                ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+                        dstPort, srcPort, ackNum, seqNum + 1));
+        assertPass(program,
+                ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+                        dstPort, srcPort, ackNum, seqNum + 1));
+        assertPass(program,
+                ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort,
+                        dstPort, anotherSeqNum, anotherAckNum));
+        assertPass(program,
+                ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort,
+                        dstPort, anotherSeqNum, anotherAckNum));
+
+        apfFilter.shutdown();
+    }
+
+    private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
+            int dport, int seq, int ack) {
+        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+        packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
+        packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
+        put(packet, IPV4_SRC_ADDR_OFFSET, sip);
+        put(packet, IPV4_DEST_ADDR_OFFSET, tip);
+        packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport);
+        packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport);
+        packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq);
+        packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack);
+        return packet.array();
+    }
+
+    private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
+            int dport, int seq, int ack, int dataLength) {
+        final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
+
+        ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack));
+        packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
+        // TCP header length 5, reserved 3 bits, NS=0
+        packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50);
+        return packet.array();
+    }
+
+    private static byte[] ipv6Packet(byte[] sip, byte[] tip, int sport,
+            int dport, int seq, int ack) {
+        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+        packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6);
+        put(packet, IPV6_SRC_ADDR_OFFSET, sip);
+        put(packet, IPV6_DEST_ADDR_OFFSET, tip);
+        packet.putShort(IPV6_TCP_SRC_PORT_OFFSET, (short) sport);
+        packet.putShort(IPV6_TCP_DEST_PORT_OFFSET, (short) dport);
+        packet.putInt(IPV6_TCP_SEQ_NUM_OFFSET, seq);
+        packet.putInt(IPV6_TCP_ACK_NUM_OFFSET, ack);
+        return packet.array();
+    }
+
     // Verify that the last program pushed to the IpClient.Callback properly filters the
     // given packet for the given lifetime.
     private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index d11bb64..b98b0f7 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -482,7 +482,7 @@
         nm.notifyNetworkConnected();
 
         verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
-                .showProvisioningNotification(any());
+                .showProvisioningNotification(any(), any());
 
         // Check that startCaptivePortalApp sends the expected intent.
         nm.launchCaptivePortalApp();
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 4791517..0cae894 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -22,4 +22,20 @@
 
 }
 
+// NOTE: Keep this module in sync with ./common.mk
+java_defaults {
+    name: "SettingsLibDefaults",
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.lifecycle_lifecycle-common",
+        "androidx.legacy_legacy-support-v4",
+        "androidx.lifecycle_lifecycle-runtime",
+        "androidx.recyclerview_recyclerview",
+        "androidx.preference_preference",
+        "androidx.appcompat_appcompat",
+        "androidx.legacy_legacy-preference-v14",
+        "SettingsLib",
+    ],
+}
+
 // For the test package.
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index 834b83b..8c309ff 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -12,6 +12,11 @@
 #
 #   include frameworks/base/packages/SettingsLib/common.mk
 #
+# During the conversion to Soong bluprint files, the equivalent
+# functionality is provided by adding
+#   defaults: ["SettingsLibDefaults"],
+# to the corresponding module.
+# NOTE: keep this file and ./Android.bp in sync.
 
 LOCAL_STATIC_JAVA_LIBRARIES += \
     androidx.annotation_annotation \
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 76a216d..23e13c2 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -191,9 +191,9 @@
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec">HD audio</string>
 
     <!-- Bluetooth settings.  The user-visible string that is used whenever referring to the Hearing Aid profile. -->
-    <string name="bluetooth_profile_hearing_aid">Hearing Aid</string>
+    <string name="bluetooth_profile_hearing_aid">Hearing Aids</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the Hearing Aid checkbox preference when Hearing Aid is connected. -->
-    <string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aid</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aids</string>
 
     <!-- Bluetooth settings.  Connection options screen.  The summary for the A2DP checkbox preference when A2DP is connected. -->
     <string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
@@ -233,7 +233,7 @@
          will set the HID profile as preferred. -->
     <string name="bluetooth_hid_profile_summary_use_for">Use for input</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the Hearing Aid checkbox preference that describes how checking it will set the Hearing Aid profile as preferred. -->
-    <string name="bluetooth_hearing_aid_profile_summary_use_for">Use for Hearing Aid</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for">Use for Hearing Aids</string>
 
     <!-- Button text for accepting an incoming pairing request. [CHAR LIMIT=20] -->
     <string name="bluetooth_pairing_accept">Pair</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9f3401e..6cd2f45 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.bluetooth;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHearingAid;
@@ -28,7 +29,7 @@
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Log;
-import android.bluetooth.BluetoothAdapter;
+
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settingslib.R;
@@ -205,7 +206,7 @@
         // This is to ensure all the profiles are disconnected as some CK/Hs do not
         // disconnect  PBAP connection when HF connection is brought down
         PbapServerProfile PbapProfile = mProfileManager.getPbapProfile();
-        if (PbapProfile.getConnectionStatus(mDevice) == BluetoothProfile.STATE_CONNECTED)
+        if (PbapProfile != null && isConnectedProfile(PbapProfile))
         {
             PbapProfile.disconnect(mDevice);
         }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 9306219..b277666 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -78,6 +78,7 @@
     <uses-permission android:name="android.permission.MOVE_PACKAGE" />
     <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
     <uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
+    <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" />
     <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
     <uses-permission android:name="android.permission.DELETE_PACKAGES" />
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index be46d2c..be7403a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -99,7 +99,6 @@
             Log.d(TAG, "Connecting to HVAC service");
             Dependency.get(HvacController.class).connectToCarService();
         }
-        mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
         if (!mDeviceIsProvisioned) {
@@ -117,7 +116,7 @@
 
     /**
      * Remove all content from navbars and rebuild them. Used to allow for different nav bars
-     * before and after the device is provisioned
+     * before and after the device is provisioned. Also for change of density and font size.
      */
     private void restartNavBars() {
         mCarFacetButtonController.removeAll();
@@ -216,6 +215,7 @@
     protected void makeStatusBarView() {
         super.makeStatusBarView();
 
+        mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
         mNotificationPanelBackground = getDefaultWallpaper();
         mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
 
@@ -513,6 +513,7 @@
     @Override
     public void onDensityOrFontScaleChanged() {
         super.onDensityOrFontScaleChanged();
+        restartNavBars();
         // Need to update the background on density changed in case the change was due to night
         // mode.
         mNotificationPanelBackground = getDefaultWallpaper();
diff --git a/sax/tests/saxtests/Android.bp b/sax/tests/saxtests/Android.bp
new file mode 100644
index 0000000..5889f76
--- /dev/null
+++ b/sax/tests/saxtests/Android.bp
@@ -0,0 +1,11 @@
+android_test {
+    name: "FrameworksSaxTests",
+    // Include all test java files.
+    srcs: ["src/**/*.java"],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: ["junit"],
+    platform_apis: true,
+}
diff --git a/sax/tests/saxtests/Android.mk b/sax/tests/saxtests/Android.mk
deleted file mode 100644
index c4517a9..0000000
--- a/sax/tests/saxtests/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-LOCAL_PACKAGE_NAME := FrameworksSaxTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
-
diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS
new file mode 100644
index 0000000..265674a
--- /dev/null
+++ b/services/accessibility/OWNERS
@@ -0,0 +1,3 @@
+svetoslavganov@google.com
+pweaver@google.com
+rhedjao@google.com
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 08e4903..af2bbfb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -38,7 +38,6 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.uidRulesToString;
-import static android.net.NetworkStack.NETWORKSTACK_PACKAGE_NAME;
 import static android.net.shared.NetworkMonitorUtils.isValidationRequired;
 import static android.net.shared.NetworkParcelableUtil.toStableParcelable;
 import static android.os.Process.INVALID_UID;
@@ -60,7 +59,6 @@
 import android.database.ContentObserver;
 import android.net.ConnectionInfo;
 import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.IConnectivityManager;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
@@ -93,6 +91,7 @@
 import android.net.PrivateDnsConfigParcel;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
+import android.net.SocketKeepalive;
 import android.net.UidRange;
 import android.net.Uri;
 import android.net.VpnService;
@@ -1832,14 +1831,20 @@
                 "ConnectivityService");
     }
 
-    private void enforceAnyPermissionOf(String... permissions) {
+    private boolean checkAnyPermissionOf(String... permissions) {
         for (String permission : permissions) {
             if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
-                return;
+                return true;
             }
         }
-        throw new SecurityException(
-            "Requires one of the following permissions: " + String.join(", ", permissions) + ".");
+        return false;
+    }
+
+    private void enforceAnyPermissionOf(String... permissions) {
+        if (!checkAnyPermissionOf(permissions)) {
+            throw new SecurityException("Requires one of the following permissions: "
+                    + String.join(", ", permissions) + ".");
+        }
     }
 
     private void enforceInternetPermission() {
@@ -1859,19 +1864,22 @@
     }
 
     private void enforceSettingsPermission() {
-        mContext.enforceCallingOrSelfPermission(
+        enforceAnyPermissionOf(
                 android.Manifest.permission.NETWORK_SETTINGS,
-                "ConnectivityService");
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
     private boolean checkSettingsPermission() {
-        return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.NETWORK_SETTINGS);
+        return checkAnyPermissionOf(
+                android.Manifest.permission.NETWORK_SETTINGS,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
     private boolean checkSettingsPermission(int pid, int uid) {
         return PERMISSION_GRANTED == mContext.checkPermission(
-                android.Manifest.permission.NETWORK_SETTINGS, pid, uid);
+                android.Manifest.permission.NETWORK_SETTINGS, pid, uid)
+                || PERMISSION_GRANTED == mContext.checkPermission(
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid);
     }
 
     private void enforceTetherAccessPermission() {
@@ -1881,9 +1889,9 @@
     }
 
     private void enforceConnectivityInternalPermission() {
-        mContext.enforceCallingOrSelfPermission(
+        enforceAnyPermissionOf(
                 android.Manifest.permission.CONNECTIVITY_INTERNAL,
-                "ConnectivityService");
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
     private void enforceControlAlwaysOnVpnPermission() {
@@ -1894,20 +1902,16 @@
 
     private void enforceNetworkStackSettingsOrSetup() {
         enforceAnyPermissionOf(
-            android.Manifest.permission.NETWORK_SETTINGS,
-            android.Manifest.permission.NETWORK_SETUP_WIZARD,
-            android.Manifest.permission.NETWORK_STACK);
-    }
-
-    private void enforceNetworkStackPermission() {
-        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.NETWORK_SETTINGS,
+                android.Manifest.permission.NETWORK_SETUP_WIZARD,
                 android.Manifest.permission.NETWORK_STACK,
-                "ConnectivityService");
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
     private boolean checkNetworkStackPermission() {
-        return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.NETWORK_STACK);
+        return checkAnyPermissionOf(
+                android.Manifest.permission.NETWORK_STACK,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
     private void enforceConnectivityRestrictedNetworksPermission() {
@@ -2487,8 +2491,8 @@
                     nai.networkMisc.acceptUnvalidated = msg.arg1 == 1;
                     break;
                 }
-                case NetworkAgent.EVENT_PACKET_KEEPALIVE: {
-                    mKeepaliveTracker.handleEventPacketKeepalive(nai, msg);
+                case NetworkAgent.EVENT_SOCKET_KEEPALIVE: {
+                    mKeepaliveTracker.handleEventSocketKeepalive(nai, msg);
                     break;
                 }
             }
@@ -2661,9 +2665,9 @@
         }
 
         @Override
-        public void showProvisioningNotification(String action) {
+        public void showProvisioningNotification(String action, String packageName) {
             final Intent intent = new Intent(action);
-            intent.setPackage(NETWORKSTACK_PACKAGE_NAME);
+            intent.setPackage(packageName);
 
             final PendingIntent pendingIntent;
             // Only the system server can register notifications with package "android"
@@ -2853,8 +2857,7 @@
         // sending all CALLBACK_LOST messages (for requests, not listens) at the end
         // of rematchAllNetworksAndRequests
         notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
-        mKeepaliveTracker.handleStopAllKeepalives(nai,
-                ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
+        mKeepaliveTracker.handleStopAllKeepalives(nai, SocketKeepalive.ERROR_INVALID_NETWORK);
         for (String iface : nai.linkProperties.getAllInterfaceNames()) {
             // Disable wakeup packet monitoring for each interface.
             wakeupModifyInterface(iface, nai.networkCapabilities, false);
@@ -3231,6 +3234,25 @@
         });
     }
 
+    /**
+     * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this
+     * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself.
+     * @param appExtras Bundle to use as intent extras for the captive portal application.
+     *                  Must be treated as opaque to avoid preventing the captive portal app to
+     *                  update its arguments.
+     */
+    @Override
+    public void startCaptivePortalAppInternal(Bundle appExtras) {
+        mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+
+        final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+        appIntent.putExtras(appExtras);
+        appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        Binder.withCleanCallingIdentity(() ->
+                mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
+    }
+
     public boolean avoidBadWifi() {
         return mMultinetworkPolicyTracker.getAvoidBadWifi();
     }
@@ -3446,12 +3468,12 @@
                     break;
                 }
                 // Sent by KeepaliveTracker to process an app request on the state machine thread.
-                case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
+                case NetworkAgent.CMD_START_SOCKET_KEEPALIVE: {
                     mKeepaliveTracker.handleStartKeepalive(msg);
                     break;
                 }
                 // Sent by KeepaliveTracker to process an app request on the state machine thread.
-                case NetworkAgent.CMD_STOP_PACKET_KEEPALIVE: {
+                case NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE: {
                     NetworkAgentInfo nai = getNetworkAgentInfoForNetwork((Network) msg.obj);
                     int slot = msg.arg1;
                     int reason = msg.arg2;
@@ -6315,7 +6337,7 @@
         mKeepaliveTracker.startNattKeepalive(
                 getNetworkAgentInfoForNetwork(network),
                 intervalSeconds, messenger, binder,
-                srcAddr, srcPort, dstAddr, ConnectivityManager.PacketKeepalive.NATT_PORT);
+                srcAddr, srcPort, dstAddr, NattSocketKeepalive.NATT_PORT);
     }
 
     @Override
@@ -6330,9 +6352,17 @@
     }
 
     @Override
+    public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds,
+            Messenger messenger, IBinder binder) {
+        enforceKeepalivePermission();
+        mKeepaliveTracker.startTcpKeepalive(
+                getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, messenger, binder);
+    }
+
+    @Override
     public void stopKeepalive(Network network, int slot) {
         mHandler.sendMessage(mHandler.obtainMessage(
-                NetworkAgent.CMD_STOP_PACKET_KEEPALIVE, slot, PacketKeepalive.SUCCESS, network));
+                NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE, slot, SocketKeepalive.SUCCESS, network));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 43af36f..3b5c9f5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -26,6 +26,7 @@
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -246,7 +247,10 @@
     private PreciseDataConnectionState mPreciseDataConnectionState =
                 new PreciseDataConnectionState();
 
-    static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
+    // Nothing here yet, but putting it here in case we want to add more in the future.
+    static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
+
+    static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_CELL_LOCATION
                     | PhoneStateListener.LISTEN_CELL_INFO;
 
@@ -637,8 +641,14 @@
                     if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
                         try {
                             if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
-                            r.callback.onServiceStateChanged(
-                                    new ServiceState(mServiceState[phoneId]));
+                            ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                r.callback.onServiceStateChanged(rawSs);
+                            } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false));
+                            } else {
+                                r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true));
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -673,7 +683,7 @@
                         try {
                             if (DBG_LOC) log("listen: mCellLocation = "
                                     + mCellLocation[phoneId]);
-                            if (checkLocationAccess(r)) {
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                                 r.callback.onCellLocationChanged(
                                         new Bundle(mCellLocation[phoneId]));
                             }
@@ -722,7 +732,7 @@
                         try {
                             if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
                                     + mCellInfo.get(phoneId));
-                            if (checkLocationAccess(r)) {
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                                 r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
                             }
                         } catch (RemoteException ex) {
@@ -1009,13 +1019,22 @@
                     }
                     if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) &&
                             idMatch(r.subId, subId, phoneId)) {
+
                         try {
+                            ServiceState stateToSend;
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                stateToSend = new ServiceState(state);
+                            } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                stateToSend = state.sanitizeLocationInfo(false);
+                            } else {
+                                stateToSend = state.sanitizeLocationInfo(true);
+                            }
                             if (DBG) {
                                 log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
                                         + " state=" + state);
                             }
-                            r.callback.onServiceStateChanged(new ServiceState(state));
+                            r.callback.onServiceStateChanged(stateToSend);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -1198,7 +1217,7 @@
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
                             idMatch(r.subId, subId, phoneId) &&
-                            checkLocationAccess(r)) {
+                            checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1500,7 +1519,7 @@
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
                             idMatch(r.subId, subId, phoneId) &&
-                            checkLocationAccess(r)) {
+                            checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellLocation: cellLocation=" + cellLocation
@@ -2109,12 +2128,35 @@
 
     private boolean checkListenerPermission(
             int events, int subId, String callingPackage, String message) {
+        LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
+                new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                .setCallingPackage(callingPackage)
+                .setMethod(message + " events: " + events)
+                .setCallingPid(Binder.getCallingPid())
+                .setCallingUid(Binder.getCallingUid());
+
+        boolean shouldCheckLocationPermissions = false;
         if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
-            if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(),
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                return false;
+            locationQueryBuilder.setMinSdkVersionForCoarse(0);
+            shouldCheckLocationPermissions = true;
+        }
+
+        if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) {
+            // Everything that requires fine location started in Q. So far...
+            locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q);
+            shouldCheckLocationPermissions = true;
+        }
+
+        if (shouldCheckLocationPermissions) {
+            LocationAccessPolicy.LocationPermissionResult result =
+                    LocationAccessPolicy.checkLocationPermission(
+                            mContext, locationQueryBuilder.build());
+            switch (result) {
+                case DENIED_HARD:
+                    throw new SecurityException("Unable to listen for events " + events + " due to "
+                            + "insufficient location permissions.");
+                case DENIED_SOFT:
+                    return false;
             }
         }
 
@@ -2229,15 +2271,38 @@
         }
     }
 
-    private boolean checkLocationAccess(Record r) {
-        long token = Binder.clearCallingIdentity();
-        try {
-            return LocationAccessPolicy.canAccessCellLocation(mContext,
-                    r.callingPackage, r.callerUid, r.callerPid,
-                    /*throwOnDeniedPermission*/ false);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+    private boolean checkFineLocationAccess(Record r, int minSdk) {
+        LocationAccessPolicy.LocationPermissionQuery query =
+                new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                        .setCallingPackage(r.callingPackage)
+                        .setCallingPid(r.callerPid)
+                        .setCallingUid(r.callerUid)
+                        .setMethod("TelephonyRegistry push")
+                        .setMinSdkVersionForFine(minSdk)
+                        .build();
+
+        return Binder.withCleanCallingIdentity(() -> {
+            LocationAccessPolicy.LocationPermissionResult locationResult =
+                    LocationAccessPolicy.checkLocationPermission(mContext, query);
+            return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+        });
+    }
+
+    private boolean checkCoarseLocationAccess(Record r, int minSdk) {
+        LocationAccessPolicy.LocationPermissionQuery query =
+                new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                        .setCallingPackage(r.callingPackage)
+                        .setCallingPid(r.callerPid)
+                        .setCallingUid(r.callerUid)
+                        .setMethod("TelephonyRegistry push")
+                        .setMinSdkVersionForCoarse(minSdk)
+                        .build();
+
+        return Binder.withCleanCallingIdentity(() -> {
+            LocationAccessPolicy.LocationPermissionResult locationResult =
+                    LocationAccessPolicy.checkLocationPermission(mContext, query);
+            return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+        });
     }
 
     private void checkPossibleMissNotify(Record r, int phoneId) {
@@ -2287,7 +2352,7 @@
                     log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
                             + mCellInfo.get(phoneId));
                 }
-                if (checkLocationAccess(r)) {
+                if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                     r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
                 }
             } catch (RemoteException ex) {
@@ -2337,7 +2402,7 @@
             try {
                 if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
                         + mCellLocation[phoneId]);
-                if (checkLocationAccess(r)) {
+                if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                     r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
                 }
             } catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8e80c74..06d1ca6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -365,6 +365,7 @@
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
+import android.sysprop.DisplayProperties;
 import android.sysprop.VoldProperties;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
@@ -7435,6 +7436,7 @@
         // next app record if we are emulating process with anonymous threads.
         ProcessRecord app;
         long startTime = SystemClock.uptimeMillis();
+        long bindApplicationTimeMillis;
         if (pid != MY_PID && pid >= 0) {
             synchronized (mPidsSelfLocked) {
                 app = mPidsSelfLocked.get(pid);
@@ -7665,6 +7667,7 @@
             }
 
             checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
+            bindApplicationTimeMillis = SystemClock.elapsedRealtime();
             mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
             if (app.isolatedEntryPoint != null) {
                 // This is an isolated process which should just call an entry point instead of
@@ -7783,6 +7786,18 @@
             checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
         }
 
+        StatsLog.write(
+                StatsLog.PROCESS_START_TIME,
+                app.info.uid,
+                app.pid,
+                app.info.packageName,
+                StatsLog.PROCESS_START_TIME__TYPE__COLD,
+                app.startTime,
+                (int) (bindApplicationTimeMillis - app.startTime),
+                (int) (SystemClock.elapsedRealtime() - app.startTime),
+                app.hostingType,
+                (app.hostingNameStr != null ? app.hostingNameStr : ""));
+
         return true;
     }
 
@@ -14937,8 +14952,8 @@
                 mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK_ONLY);
         mHiddenApiBlacklist.registerObserver();
 
-        // Transfer any global setting for forcing RTL layout, into a System Property
-        SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+        // Transfer any global setting for forcing RTL layout, into a Display Property
+        DisplayProperties.debug_force_rtl(forceRtl);
 
         final Configuration configuration = new Configuration();
         Settings.System.getConfiguration(resolver, configuration);
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 1559ba8..6cff57d 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -16,26 +16,32 @@
 
 package com.android.server.connectivity;
 
-// TODO: Clean up imports and remove references of PacketKeepalive constants.
-
-import static android.net.ConnectivityManager.PacketKeepalive.ERROR_INVALID_INTERVAL;
-import static android.net.ConnectivityManager.PacketKeepalive.ERROR_INVALID_IP_ADDRESS;
-import static android.net.ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK;
-import static android.net.ConnectivityManager.PacketKeepalive.MIN_INTERVAL;
-import static android.net.ConnectivityManager.PacketKeepalive.NATT_PORT;
-import static android.net.ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
-import static android.net.ConnectivityManager.PacketKeepalive.SUCCESS;
-import static android.net.NetworkAgent.CMD_START_PACKET_KEEPALIVE;
-import static android.net.NetworkAgent.CMD_STOP_PACKET_KEEPALIVE;
-import static android.net.NetworkAgent.EVENT_PACKET_KEEPALIVE;
+import static android.net.NattSocketKeepalive.NATT_PORT;
+import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER;
+import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER;
+import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
+import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE;
+import static android.net.NetworkAgent.EVENT_SOCKET_KEEPALIVE;
+import static android.net.SocketKeepalive.BINDER_DIED;
+import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL;
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
 import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.MAX_INTERVAL_SEC;
+import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
+import static android.net.SocketKeepalive.NO_KEEPALIVE;
+import static android.net.SocketKeepalive.SUCCESS;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.KeepalivePacketData;
+import android.net.NattKeepalivePacketData;
 import android.net.NetworkAgent;
 import android.net.NetworkUtils;
+import android.net.SocketKeepalive.InvalidPacketException;
+import android.net.SocketKeepalive.InvalidSocketException;
+import android.net.TcpKeepalivePacketData;
+import android.net.TcpKeepalivePacketData.TcpSocketInfo;
 import android.net.util.IpUtils;
 import android.os.Binder;
 import android.os.Handler;
@@ -60,11 +66,11 @@
 import java.util.HashMap;
 
 /**
- * Manages packet keepalive requests.
+ * Manages socket keepalive requests.
  *
  * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all
  * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its
- * methods must be called only from the ConnectivityService handler thread.
+ * handle* methods must be called only from the ConnectivityService handler thread.
  */
 public class KeepaliveTracker {
 
@@ -77,38 +83,54 @@
     private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
             new HashMap<> ();
     private final Handler mConnectivityServiceHandler;
+    @NonNull
+    private final TcpKeepaliveController mTcpController;
 
     public KeepaliveTracker(Handler handler) {
         mConnectivityServiceHandler = handler;
+        mTcpController = new TcpKeepaliveController(handler);
     }
 
     /**
-     * Tracks information about a packet keepalive.
+     * Tracks information about a socket keepalive.
      *
      * All information about this keepalive is known at construction time except the slot number,
      * which is only returned when the hardware has successfully started the keepalive.
      */
     class KeepaliveInfo implements IBinder.DeathRecipient {
-        // Bookkeping data.
+        // Bookkeeping data.
         private final Messenger mMessenger;
         private final IBinder mBinder;
         private final int mUid;
         private final int mPid;
         private final NetworkAgentInfo mNai;
+        private final int mType;
+        private final FileDescriptor mFd;
 
-        /** Keepalive slot. A small integer that identifies this keepalive among the ones handled
-          * by this network. */
-        private int mSlot = PacketKeepalive.NO_KEEPALIVE;
+        public static final int TYPE_NATT = 1;
+        public static final int TYPE_TCP = 2;
+
+        // Keepalive slot. A small integer that identifies this keepalive among the ones handled
+        // by this network.
+        private int mSlot = NO_KEEPALIVE;
 
         // Packet data.
         private final KeepalivePacketData mPacket;
         private final int mInterval;
 
-        // Whether the keepalive is started or not.
-        public boolean isStarted;
+        // Whether the keepalive is started or not. The initial state is NOT_STARTED.
+        private static final int NOT_STARTED = 1;
+        private static final int STARTING = 2;
+        private static final int STARTED = 3;
+        private int mStartedState = NOT_STARTED;
 
-        public KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai,
-                KeepalivePacketData packet, int interval) {
+        KeepaliveInfo(@NonNull Messenger messenger,
+                @NonNull IBinder binder,
+                @NonNull NetworkAgentInfo nai,
+                @NonNull KeepalivePacketData packet,
+                int interval,
+                int type,
+                @NonNull FileDescriptor fd) {
             mMessenger = messenger;
             mBinder = binder;
             mPid = Binder.getCallingPid();
@@ -117,6 +139,8 @@
             mNai = nai;
             mPacket = packet;
             mInterval = interval;
+            mType = type;
+            mFd = fd;
 
             try {
                 mBinder.linkToDeath(this, 0);
@@ -129,32 +153,40 @@
             return mNai;
         }
 
-        public String toString() {
-            return new StringBuffer("KeepaliveInfo [")
-                    .append(" network=").append(mNai.network)
-                    .append(" isStarted=").append(isStarted)
-                    .append(" ")
-                    .append(IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort))
-                    .append("->")
-                    .append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort))
-                    .append(" interval=" + mInterval)
-                    .append(" packetData=" + HexDump.toHexString(mPacket.getPacket()))
-                    .append(" uid=").append(mUid).append(" pid=").append(mPid)
-                    .append(" ]")
-                    .toString();
+        private String startedStateString(final int state) {
+            switch (state) {
+                case NOT_STARTED : return "NOT_STARTED";
+                case STARTING : return "STARTING";
+                case STARTED : return "STARTED";
+            }
+            throw new IllegalArgumentException("Unknown state");
         }
 
-        /** Sends a message back to the application via its PacketKeepalive.Callback. */
+        public String toString() {
+            return "KeepaliveInfo ["
+                    + " network=" + mNai.network
+                    + " startedState=" + startedStateString(mStartedState)
+                    + " "
+                    + IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort)
+                    + "->"
+                    + IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)
+                    + " interval=" + mInterval
+                    + " uid=" + mUid + " pid=" + mPid
+                    + " packetData=" + HexDump.toHexString(mPacket.getPacket())
+                    + " ]";
+        }
+
+        /** Sends a message back to the application via its SocketKeepalive.Callback. */
         void notifyMessenger(int slot, int err) {
+            if (DBG) {
+                Log.d(TAG, "notify keepalive " + mSlot + " on " + mNai.network + " for " + err);
+            }
             KeepaliveTracker.this.notifyMessenger(mMessenger, slot, err);
         }
 
         /** Called when the application process is killed. */
         public void binderDied() {
-            // Not called from ConnectivityService handler thread, so send it a message.
-            mConnectivityServiceHandler.obtainMessage(
-                    NetworkAgent.CMD_STOP_PACKET_KEEPALIVE,
-                    mSlot, PacketKeepalive.BINDER_DIED, mNai.network).sendToTarget();
+            stop(BINDER_DIED);
         }
 
         void unlinkDeathRecipient() {
@@ -181,7 +213,10 @@
         }
 
         private int checkInterval() {
-            return mInterval >= MIN_INTERVAL ? SUCCESS : ERROR_INVALID_INTERVAL;
+            if (mInterval < MIN_INTERVAL_SEC || mInterval > MAX_INTERVAL_SEC) {
+                return ERROR_INVALID_INTERVAL;
+            }
+            return SUCCESS;
         }
 
         private int isValid() {
@@ -198,7 +233,26 @@
             int error = isValid();
             if (error == SUCCESS) {
                 Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
-                mNai.asyncChannel.sendMessage(CMD_START_PACKET_KEEPALIVE, slot, mInterval, mPacket);
+                switch (mType) {
+                    case TYPE_NATT:
+                        mNai.asyncChannel
+                                .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
+                        break;
+                    case TYPE_TCP:
+                        mTcpController.startSocketMonitor(mFd, this, mSlot);
+                        mNai.asyncChannel
+                                .sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */,
+                                        mPacket);
+                        // TODO: check result from apf and notify of failure as needed.
+                        mNai.asyncChannel
+                                .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
+                        break;
+                    default:
+                        Log.wtf(TAG, "Starting keepalive with unknown type: " + mType);
+                        handleStopKeepalive(mNai, mSlot, error);
+                        return;
+                }
+                mStartedState = STARTING;
             } else {
                 handleStopKeepalive(mNai, mSlot, error);
                 return;
@@ -212,20 +266,32 @@
                     Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
                 }
             }
-            if (isStarted) {
+            if (NOT_STARTED != mStartedState) {
                 Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name());
-                mNai.asyncChannel.sendMessage(CMD_STOP_PACKET_KEEPALIVE, mSlot);
+                if (mType == TYPE_NATT) {
+                    mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
+                } else if (mType == TYPE_TCP) {
+                    mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
+                    mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, mSlot);
+                    mTcpController.stopSocketMonitor(mSlot);
+                } else {
+                    Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
+                }
             }
             // TODO: at the moment we unconditionally return failure here. In cases where the
             // NetworkAgent is alive, should we ask it to reply, so it can return failure?
             notifyMessenger(mSlot, reason);
             unlinkDeathRecipient();
         }
+
+        void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
+            handleStopKeepalive(mNai, mSlot, socketKeepaliveReason);
+        }
     }
 
     void notifyMessenger(Messenger messenger, int slot, int err) {
         Message message = Message.obtain();
-        message.what = EVENT_PACKET_KEEPALIVE;
+        message.what = EVENT_SOCKET_KEEPALIVE;
         message.arg1 = slot;
         message.arg2 = err;
         message.obj = null;
@@ -310,7 +376,7 @@
     }
 
     /** Handle keepalive events from lower layer. */
-    public void handleEventPacketKeepalive(@NonNull NetworkAgentInfo nai,
+    public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai,
             @NonNull Message message) {
         int slot = message.arg1;
         int reason = message.arg2;
@@ -324,20 +390,38 @@
             return;
         }
 
-        if (reason == SUCCESS && !ki.isStarted) {
+        // This can be called in a number of situations :
+        // - startedState is STARTING.
+        //   - reason is SUCCESS => go to STARTED.
+        //   - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive.
+        // - startedState is STARTED.
+        //   - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive.
+        //   - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive.
+        // The control is not supposed to ever come here if the state is NOT_STARTED. This is
+        // because in NOT_STARTED state, the code will switch to STARTING before sending messages
+        // to start, and the only way to NOT_STARTED is this function, through the edges outlined
+        // above : in all cases, keepalive gets stopped and can't restart without going into
+        // STARTING as messages are ordered. This also depends on the hardware processing the
+        // messages in order.
+        // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an
+        // option.
+        if (reason == SUCCESS && KeepaliveInfo.STARTING == ki.mStartedState) {
             // Keepalive successfully started.
             if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
-            ki.isStarted = true;
+            ki.mStartedState = KeepaliveInfo.STARTED;
             ki.notifyMessenger(slot, reason);
         } else {
             // Keepalive successfully stopped, or error.
-            ki.isStarted = false;
+            ki.mStartedState = KeepaliveInfo.NOT_STARTED;
             if (reason == SUCCESS) {
+                // The message indicated success stopping : don't call handleStopKeepalive.
                 if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name());
             } else {
+                // The message indicated some error trying to start or during the course of
+                // keepalive : do call handleStopKeepalive.
+                handleStopKeepalive(nai, slot, reason);
                 if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason);
             }
-            handleStopKeepalive(nai, slot, reason);
         }
     }
 
@@ -369,16 +453,55 @@
 
         KeepalivePacketData packet;
         try {
-            packet = KeepalivePacketData.nattKeepalivePacket(
+            packet = NattKeepalivePacketData.nattKeepalivePacket(
                     srcAddress, srcPort, dstAddress, NATT_PORT);
-        } catch (KeepalivePacketData.InvalidPacketException e) {
+        } catch (InvalidPacketException e) {
             notifyMessenger(messenger, NO_KEEPALIVE, e.error);
             return;
         }
-        KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds);
-        Log.d(TAG, "Created keepalive: " + ki.toString());
+        KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds,
+                KeepaliveInfo.TYPE_NATT, null);
         mConnectivityServiceHandler.obtainMessage(
-                NetworkAgent.CMD_START_PACKET_KEEPALIVE, ki).sendToTarget();
+                NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
+    }
+
+    /**
+     * Called by ConnectivityService to start TCP keepalive on a file descriptor.
+     *
+     * In order to offload keepalive for application correctly, sequence number, ack number and
+     * other fields are needed to form the keepalive packet. Thus, this function synchronously
+     * puts the socket into repair mode to get the necessary information. After the socket has been
+     * put into repair mode, the application cannot access the socket until reverted to normal.
+     *
+     * See {@link android.net.SocketKeepalive}.
+     **/
+    public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
+            @NonNull FileDescriptor fd,
+            int intervalSeconds,
+            @NonNull Messenger messenger,
+            @NonNull IBinder binder) {
+        if (nai == null) {
+            notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK);
+            return;
+        }
+
+        TcpKeepalivePacketData packet = null;
+        try {
+            TcpSocketInfo tsi = TcpKeepaliveController.switchToRepairMode(fd);
+            packet = TcpKeepalivePacketData.tcpKeepalivePacket(tsi);
+        } catch (InvalidPacketException | InvalidSocketException e) {
+            try {
+                TcpKeepaliveController.switchOutOfRepairMode(fd);
+            } catch (ErrnoException e1) {
+                Log.e(TAG, "Couldn't move fd out of repair mode after failure to start keepalive");
+            }
+            notifyMessenger(messenger, NO_KEEPALIVE, e.error);
+            return;
+        }
+        KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds,
+                KeepaliveInfo.TYPE_TCP, fd);
+        Log.d(TAG, "Created keepalive: " + ki.toString());
+        mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
     }
 
    /**
@@ -432,7 +555,7 @@
     }
 
     public void dump(IndentingPrintWriter pw) {
-        pw.println("Packet keepalives:");
+        pw.println("Socket keepalives:");
         pw.increaseIndent();
         for (NetworkAgentInfo nai : mKeepalives.keySet()) {
             pw.println(nai.name());
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
new file mode 100644
index 0000000..8a9ac23
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
@@ -0,0 +1,316 @@
+/*
+ * 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.connectivity;
+
+import static android.net.SocketKeepalive.DATA_RECEIVED;
+import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.ERROR_SOCKET_NOT_IDLE;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.system.OsConstants.FIONREAD;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.TIOCOUTQ;
+
+import android.annotation.NonNull;
+import android.net.NetworkUtils;
+import android.net.SocketKeepalive.InvalidSocketException;
+import android.net.TcpKeepalivePacketData.TcpSocketInfo;
+import android.net.TcpRepairWindow;
+import android.os.Handler;
+import android.os.MessageQueue;
+import android.os.Messenger;
+import android.system.ErrnoException;
+import android.system.Int32Ref;
+import android.system.Os;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo;
+
+import java.io.FileDescriptor;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+
+/**
+ * Manage tcp socket which offloads tcp keepalive.
+ *
+ * The input socket will be changed to repair mode and the application
+ * will not have permission to read/write data. If the application wants
+ * to write data, it must stop tcp keepalive offload to leave repair mode
+ * first. If a remote packet arrives, repair mode will be turned off and
+ * offload will be stopped. The application will receive a callback to know
+ * it can start reading data.
+ *
+ * {start,stop}SocketMonitor are thread-safe, but care must be taken in the
+ * order in which they are called. Please note that while calling
+ * {@link #startSocketMonitor(FileDescriptor, Messenger, int)} multiple times
+ * with either the same slot or the same FileDescriptor without stopping it in
+ * between will result in an exception, calling {@link #stopSocketMonitor(int)}
+ * multiple times with the same int is explicitly a no-op.
+ * Please also note that switching the socket to repair mode is not synchronized
+ * with either of these operations and has to be done in an orderly fashion
+ * with stopSocketMonitor. Take care in calling these in the right order.
+ * @hide
+ */
+public class TcpKeepaliveController {
+    private static final String TAG = "TcpKeepaliveController";
+    private static final boolean DBG = false;
+
+    private final MessageQueue mFdHandlerQueue;
+
+    private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+
+    // Reference include/uapi/linux/tcp.h
+    private static final int TCP_REPAIR = 19;
+    private static final int TCP_REPAIR_QUEUE = 20;
+    private static final int TCP_QUEUE_SEQ = 21;
+    private static final int TCP_NO_QUEUE = 0;
+    private static final int TCP_RECV_QUEUE = 1;
+    private static final int TCP_SEND_QUEUE = 2;
+    private static final int TCP_REPAIR_OFF = 0;
+    private static final int TCP_REPAIR_ON = 1;
+    // Reference include/uapi/linux/sockios.h
+    private static final int SIOCINQ = FIONREAD;
+    private static final int SIOCOUTQ = TIOCOUTQ;
+
+    /**
+     * Keeps track of packet listeners.
+     * Key: slot number of keepalive offload.
+     * Value: {@link FileDescriptor} being listened to.
+     */
+    @GuardedBy("mListeners")
+    private final SparseArray<FileDescriptor> mListeners = new SparseArray<>();
+
+    public TcpKeepaliveController(final Handler connectivityServiceHandler) {
+        mFdHandlerQueue = connectivityServiceHandler.getLooper().getQueue();
+    }
+
+    /**
+     * Switch the tcp socket to repair mode and query tcp socket information.
+     *
+     * @param fd the fd of socket on which to use keepalive offload
+     * @return a {@link TcpKeepalivePacketData#TcpSocketInfo} object for current
+     * tcp/ip information.
+     */
+    // TODO : make this private. It's far too confusing that this gets called from outside
+    // at a time that nobody can understand.
+    public static TcpSocketInfo switchToRepairMode(FileDescriptor fd)
+            throws InvalidSocketException {
+        if (DBG) Log.i(TAG, "switchToRepairMode to start tcp keepalive : " + fd);
+        final SocketAddress srcSockAddr;
+        final SocketAddress dstSockAddr;
+        final InetAddress srcAddress;
+        final InetAddress dstAddress;
+        final int srcPort;
+        final int dstPort;
+        int seq;
+        final int ack;
+        final TcpRepairWindow trw;
+
+        // Query source address and port.
+        try {
+            srcSockAddr = Os.getsockname(fd);
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Get sockname fail: ", e);
+            throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+        }
+        if (srcSockAddr instanceof InetSocketAddress) {
+            srcAddress = getAddress((InetSocketAddress) srcSockAddr);
+            srcPort = getPort((InetSocketAddress) srcSockAddr);
+        } else {
+            Log.e(TAG, "Invalid or mismatched SocketAddress");
+            throw new InvalidSocketException(ERROR_INVALID_SOCKET);
+        }
+        // Query destination address and port.
+        try {
+            dstSockAddr = Os.getpeername(fd);
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Get peername fail: ", e);
+            throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+        }
+        if (dstSockAddr instanceof InetSocketAddress) {
+            dstAddress = getAddress((InetSocketAddress) dstSockAddr);
+            dstPort = getPort((InetSocketAddress) dstSockAddr);
+        } else {
+            Log.e(TAG, "Invalid or mismatched peer SocketAddress");
+            throw new InvalidSocketException(ERROR_INVALID_SOCKET);
+        }
+
+        // Query sequence and ack number
+        dropAllIncomingPackets(fd, true);
+        try {
+            // Enter tcp repair mode.
+            Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_ON);
+            // Check if socket is idle.
+            if (!isSocketIdle(fd)) {
+                throw new InvalidSocketException(ERROR_SOCKET_NOT_IDLE);
+            }
+            // Query write sequence number from SEND_QUEUE.
+            Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_SEND_QUEUE);
+            seq = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+            // Query read sequence number from RECV_QUEUE.
+            Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_RECV_QUEUE);
+            ack = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+            // Switch to NO_QUEUE to prevent illegal socket read/write in repair mode.
+            Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_NO_QUEUE);
+            // Finally, check if socket is still idle. TODO : this check needs to move to
+            // after starting polling to prevent a race.
+            if (!isSocketIdle(fd)) {
+                throw new InvalidSocketException(ERROR_INVALID_SOCKET);
+            }
+
+            // Query tcp window size.
+            trw = NetworkUtils.getTcpRepairWindow(fd);
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Exception reading TCP state from socket", e);
+            try {
+                Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_OFF);
+            } catch (ErrnoException ex) {
+                Log.e(TAG, "Exception while turning off repair mode due to exception", ex);
+            }
+            throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+        } finally {
+            dropAllIncomingPackets(fd, false);
+        }
+
+        // Keepalive sequence number is last sequence number - 1. If it couldn't be retrieved,
+        // then it must be set to -1, so decrement in all cases.
+        seq = seq - 1;
+
+        return new TcpSocketInfo(srcAddress, srcPort, dstAddress, dstPort, seq, ack, trw.rcvWnd,
+                trw.rcvWndScale);
+    }
+
+    /**
+     * Switch the tcp socket out of repair mode.
+     *
+     * @param fd the fd of socket to switch back to normal.
+     */
+    // TODO : make this private.
+    public static void switchOutOfRepairMode(@NonNull final FileDescriptor fd)
+            throws ErrnoException {
+        Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_OFF);
+    }
+
+    /**
+     * Start monitoring incoming packets.
+     *
+     * @param fd socket fd to monitor.
+     * @param messenger a callback to notify socket status.
+     * @param slot keepalive slot.
+     */
+    public void startSocketMonitor(@NonNull final FileDescriptor fd,
+            @NonNull final KeepaliveInfo ki, final int slot) {
+        synchronized (mListeners) {
+            if (null != mListeners.get(slot)) {
+                throw new IllegalArgumentException("This slot is already taken");
+            }
+            for (int i = 0; i < mListeners.size(); ++i) {
+                if (fd.equals(mListeners.valueAt(i))) {
+                    throw new IllegalArgumentException("This fd is already registered");
+                }
+            }
+            mFdHandlerQueue.addOnFileDescriptorEventListener(fd, FD_EVENTS, (readyFd, events) -> {
+                // This can't be called twice because the queue guarantees that once the listener
+                // is unregistered it can't be called again, even for a message that arrived
+                // before it was unregistered.
+                final int reason;
+                if (0 != (events & EVENT_ERROR)) {
+                    reason = ERROR_INVALID_SOCKET;
+                } else {
+                    reason = DATA_RECEIVED;
+                }
+                ki.onFileDescriptorInitiatedStop(reason);
+                // The listener returns the new set of events to listen to. Because 0 means no
+                // event, the listener gets unregistered.
+                return 0;
+            });
+            mListeners.put(slot, fd);
+        }
+    }
+
+    /** Stop socket monitor */
+    // This slot may have been stopped automatically already because the socket received data,
+    // was closed on the other end or otherwise suffered some error. In this case, this function
+    // is a no-op.
+    public void stopSocketMonitor(final int slot) {
+        final FileDescriptor fd;
+        synchronized (mListeners) {
+            fd = mListeners.get(slot);
+            if (null == fd) return;
+            mListeners.remove(slot);
+        }
+        mFdHandlerQueue.removeOnFileDescriptorEventListener(fd);
+        try {
+            if (DBG) Log.d(TAG, "Moving socket out of repair mode for stop : " + fd);
+            switchOutOfRepairMode(fd);
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Cannot switch socket out of repair mode", e);
+            // Well, there is not much to do here to recover
+        }
+    }
+
+    private static InetAddress getAddress(InetSocketAddress inetAddr) {
+        return inetAddr.getAddress();
+    }
+
+    private static int getPort(InetSocketAddress inetAddr) {
+        return inetAddr.getPort();
+    }
+
+    private static boolean isSocketIdle(FileDescriptor fd) throws ErrnoException {
+        return isReceiveQueueEmpty(fd) && isSendQueueEmpty(fd);
+    }
+
+    private static boolean isReceiveQueueEmpty(FileDescriptor fd)
+            throws ErrnoException {
+        Int32Ref result = new Int32Ref(-1);
+        Os.ioctlInt(fd, SIOCINQ, result);
+        if (result.value != 0) {
+            Log.e(TAG, "Read queue has data");
+            return false;
+        }
+        return true;
+    }
+
+    private static boolean isSendQueueEmpty(FileDescriptor fd)
+            throws ErrnoException {
+        Int32Ref result = new Int32Ref(-1);
+        Os.ioctlInt(fd, SIOCOUTQ, result);
+        if (result.value != 0) {
+            Log.e(TAG, "Write queue has data");
+            return false;
+        }
+        return true;
+    }
+
+    private static void dropAllIncomingPackets(FileDescriptor fd, boolean enable)
+            throws InvalidSocketException {
+        try {
+            if (enable) {
+                NetworkUtils.attachDropAllBPFFilter(fd);
+            } else {
+                NetworkUtils.detachBPFFilter(fd);
+            }
+        } catch (SocketException e) {
+            Log.e(TAG, "Socket Exception: ", e);
+            throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index a14fd17..19bdc09 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -96,6 +96,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.MessageUtils;
@@ -180,6 +181,7 @@
     // into a single coherent structure.
     private final HashSet<IpServer> mForwardedDownstreams;
     private final VersionedBroadcastListener mCarrierConfigChange;
+    private final VersionedBroadcastListener mDefaultSubscriptionChange;
     private final TetheringDependencies mDeps;
     private final EntitlementManager mEntitlementMgr;
 
@@ -232,6 +234,15 @@
                     mEntitlementMgr.reevaluateSimCardProvisioning();
                 });
 
+        filter = new IntentFilter();
+        filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        mDefaultSubscriptionChange = new VersionedBroadcastListener(
+                "DefaultSubscriptionChangeListener", mContext, smHandler, filter,
+                (Intent ignored) -> {
+                    mLog.log("OBSERVED default data subscription change");
+                    updateConfiguration();
+                    mEntitlementMgr.reevaluateSimCardProvisioning();
+                });
         mStateReceiver = new StateReceiver();
 
         // Load tethering configuration.
@@ -242,6 +253,7 @@
 
     private void startStateMachineUpdaters() {
         mCarrierConfigChange.startListening();
+        mDefaultSubscriptionChange.startListening();
 
         final Handler handler = mTetherMasterSM.getHandler();
         IntentFilter filter = new IntentFilter();
@@ -270,7 +282,8 @@
 
     // NOTE: This is always invoked on the mLooper thread.
     private void updateConfiguration() {
-        mConfig = new TetheringConfiguration(mContext, mLog);
+        final int subId = mDeps.getDefaultDataSubscriptionId();
+        mConfig = new TetheringConfiguration(mContext, mLog, subId);
         mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
         mEntitlementMgr.updateConfiguration(mConfig);
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 1e6bb04..8a46ff1 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -26,8 +26,8 @@
 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_usb_regexs;
 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 com.android.internal.R.bool.config_tether_upstream_automatic;
 import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
@@ -38,6 +38,7 @@
 import android.net.ConnectivityManager;
 import android.net.util.SharedLog;
 import android.provider.Settings;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
@@ -100,29 +101,34 @@
     public final String[] provisioningApp;
     public final String provisioningAppNoUi;
 
-    public TetheringConfiguration(Context ctx, SharedLog log) {
+    public final int subId;
+
+    public TetheringConfiguration(Context ctx, SharedLog log, int id) {
         final SharedLog configLog = log.forSubComponent("config");
 
-        tetherableUsbRegexs = getResourceStringArray(ctx, config_tether_usb_regexs);
+        subId = id;
+        Resources res = getResources(ctx, subId);
+
+        tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs);
         // TODO: Evaluate deleting this altogether now that Wi-Fi always passes
         // us an interface name. Careful consideration needs to be given to
         // implications for Settings and for provisioning checks.
-        tetherableWifiRegexs = getResourceStringArray(ctx, config_tether_wifi_regexs);
-        tetherableBluetoothRegexs = getResourceStringArray(ctx, config_tether_bluetooth_regexs);
+        tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs);
+        tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
 
         dunCheck = checkDunRequired(ctx);
         configLog.log("DUN check returned: " + dunCheckString(dunCheck));
 
-        chooseUpstreamAutomatically = getResourceBoolean(ctx, config_tether_upstream_automatic);
-        preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
+        chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic);
+        preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, dunCheck);
         isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
 
-        legacyDhcpRanges = getLegacyDhcpRanges(ctx);
+        legacyDhcpRanges = getLegacyDhcpRanges(res);
         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
         enableLegacyDhcpServer = getEnableLegacyDhcpServer(ctx);
 
-        provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app);
-        provisioningAppNoUi = getProvisioningAppNoUi(ctx);
+        provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
+        provisioningAppNoUi = getProvisioningAppNoUi(res);
 
         configLog.log(toString());
     }
@@ -144,6 +150,9 @@
     }
 
     public void dump(PrintWriter pw) {
+        pw.print("subId: ");
+        pw.println(subId);
+
         dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
         dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
         dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
@@ -169,6 +178,7 @@
 
     public String toString() {
         final StringJoiner sj = new StringJoiner(" ");
+        sj.add(String.format("subId:%d", subId));
         sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
         sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
         sj.add(String.format("tetherableBluetoothRegexs:%s",
@@ -235,8 +245,8 @@
         }
     }
 
-    private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
-        final int ifaceTypes[] = ctx.getResources().getIntArray(config_tether_upstream_types);
+    private static Collection<Integer> getUpstreamIfaceTypes(Resources res, int dunCheck) {
+        final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types);
         final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
         for (int i : ifaceTypes) {
             switch (i) {
@@ -286,33 +296,33 @@
         return false;
     }
 
-    private static String[] getLegacyDhcpRanges(Context ctx) {
-        final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range);
+    private static String[] getLegacyDhcpRanges(Resources res) {
+        final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range);
         if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
             return fromResource;
         }
         return copy(LEGACY_DHCP_DEFAULT_RANGE);
     }
 
-    private static String getProvisioningAppNoUi(Context ctx) {
+    private static String getProvisioningAppNoUi(Resources res) {
         try {
-            return ctx.getResources().getString(config_mobile_hotspot_provision_app_no_ui);
+            return res.getString(config_mobile_hotspot_provision_app_no_ui);
         } catch (Resources.NotFoundException e) {
             return "";
         }
     }
 
-    private static boolean getResourceBoolean(Context ctx, int resId) {
+    private static boolean getResourceBoolean(Resources res, int resId) {
         try {
-            return ctx.getResources().getBoolean(resId);
+            return res.getBoolean(resId);
         } catch (Resources.NotFoundException e404) {
             return false;
         }
     }
 
-    private static String[] getResourceStringArray(Context ctx, int resId) {
+    private static String[] getResourceStringArray(Resources res, int resId) {
         try {
-            final String[] strArray = ctx.getResources().getStringArray(resId);
+            final String[] strArray = res.getStringArray(resId);
             return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
         } catch (Resources.NotFoundException e404) {
             return EMPTY_STRING_ARRAY;
@@ -325,6 +335,19 @@
         return intVal != 0;
     }
 
+    private Resources getResources(Context ctx, int subId) {
+        if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return getResourcesForSubIdWrapper(ctx, subId);
+        } else {
+            return ctx.getResources();
+        }
+    }
+
+    @VisibleForTesting
+    protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
+        return SubscriptionManager.getResourcesForSubId(ctx, subId);
+    }
+
     private static String[] copy(String[] strarray) {
         return Arrays.copyOf(strarray, strarray.length);
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 6d6f81e..3fddac1 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -21,6 +21,7 @@
 import android.net.ip.IpServer;
 import android.net.util.SharedLog;
 import android.os.Handler;
+import android.telephony.SubscriptionManager;
 
 import com.android.internal.util.StateMachine;
 import com.android.server.connectivity.MockableSystemProperties;
@@ -85,4 +86,11 @@
             SharedLog log, MockableSystemProperties systemProperties) {
         return new EntitlementManager(ctx, target, log, systemProperties);
     }
+
+    /**
+     * Get default data subscription id to build TetheringConfiguration.
+     */
+    public int getDefaultDataSubscriptionId() {
+        return SubscriptionManager.getDefaultDataSubscriptionId();
+    }
 }
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index f736056..1dada92 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -17,8 +17,10 @@
 package com.android.server.os;
 
 import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.BugreportParams;
 import android.os.IDumpstate;
@@ -28,26 +30,29 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserManager;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
 import java.io.FileDescriptor;
 
 // TODO(b/111441001):
-// 1. Handle the case where another bugreport is in progress
-// 2. Make everything threadsafe
-// 3. Pass validation & other errors on listener
+// Intercept onFinished() & implement death recipient here and shutdown
+// bugreportd service.
 
 /**
  * Implementation of the service that provides a privileged API to capture and consume bugreports.
  *
- * <p>Delegates the actualy generation to a native implementation of {@code Dumpstate}.
+ * <p>Delegates the actualy generation to a native implementation of {@code IDumpstate}.
  */
 class BugreportManagerServiceImpl extends IDumpstate.Stub {
     private static final String TAG = "BugreportManagerService";
     private static final String BUGREPORT_SERVICE = "bugreportd";
     private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
 
-    private IDumpstate mDs = null;
+    private final Object mLock = new Object();
     private final Context mContext;
     private final AppOpsManager mAppOps;
 
@@ -59,43 +64,44 @@
     @Override
     @RequiresPermission(android.Manifest.permission.DUMP)
     public IDumpstateToken setListener(String name, IDumpstateListener listener,
-            boolean getSectionDetails) throws RemoteException {
-        // TODO(b/111441001): Figure out if lazy setting of listener should be allowed
-        // and if so how to handle it.
+            boolean getSectionDetails) {
         throw new UnsupportedOperationException("setListener is not allowed on this service");
     }
 
-    // TODO(b/111441001): Intercept onFinished here in system server and shutdown
-    // the bugreportd service.
     @Override
     @RequiresPermission(android.Manifest.permission.DUMP)
     public void startBugreport(int callingUidUnused, String callingPackage,
             FileDescriptor bugreportFd, FileDescriptor screenshotFd,
-            int bugreportMode, IDumpstateListener listener) throws RemoteException {
-        int callingUid = Binder.getCallingUid();
-        // TODO(b/111441001): validate all arguments & ensure primary user
-        validate(bugreportMode);
+            int bugreportMode, IDumpstateListener listener) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
+        Preconditions.checkNotNull(callingPackage);
+        Preconditions.checkNotNull(bugreportFd);
+        Preconditions.checkNotNull(listener);
+        validateBugreportMode(bugreportMode);
+        ensureIsPrimaryUser();
 
+        int callingUid = Binder.getCallingUid();
         mAppOps.checkPackage(callingUid, callingPackage);
-        mDs = getDumpstateService();
-        if (mDs == null) {
-            Slog.w(TAG, "Unable to get bugreport service");
-            // TODO(b/111441001): pass error on listener
-            return;
+
+        synchronized (mLock) {
+            startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd,
+                    bugreportMode, listener);
         }
-        mDs.startBugreport(callingUid, callingPackage,
-                bugreportFd, screenshotFd, bugreportMode, listener);
     }
 
     @Override
     @RequiresPermission(android.Manifest.permission.DUMP)
-    public void cancelBugreport() throws RemoteException {
-        // This tells init to cancel bugreportd service.
-        SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
-        mDs = null;
+    public void cancelBugreport() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
+        // This tells init to cancel bugreportd service. Note that this is achieved through setting
+        // a system property which is not thread-safe. So the lock here offers thread-safety only
+        // among callers of the API.
+        synchronized (mLock) {
+            SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
+        }
     }
 
-    private boolean validate(@BugreportParams.BugreportMode int mode) {
+    private void validateBugreportMode(@BugreportParams.BugreportMode int mode) {
         if (mode != BugreportParams.BUGREPORT_MODE_FULL
                 && mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE
                 && mode != BugreportParams.BUGREPORT_MODE_REMOTE
@@ -103,9 +109,66 @@
                 && mode != BugreportParams.BUGREPORT_MODE_TELEPHONY
                 && mode != BugreportParams.BUGREPORT_MODE_WIFI) {
             Slog.w(TAG, "Unknown bugreport mode: " + mode);
-            return false;
+            throw new IllegalArgumentException("Unknown bugreport mode: " + mode);
         }
-        return true;
+    }
+
+    /**
+     * Validates that the current user is the primary user.
+     *
+     * @throws IllegalArgumentException if the current user is not the primary user
+     */
+    private void ensureIsPrimaryUser() {
+        UserInfo currentUser = null;
+        try {
+            currentUser = ActivityManager.getService().getCurrentUser();
+        } catch (RemoteException e) {
+            // Impossible to get RemoteException for an in-process call.
+        }
+
+        UserInfo primaryUser = UserManager.get(mContext).getPrimaryUser();
+        if (currentUser == null) {
+            logAndThrow("No current user. Only primary user is allowed to take bugreports.");
+        }
+        if (primaryUser == null) {
+            logAndThrow("No primary user. Only primary user is allowed to take bugreports.");
+        }
+        if (primaryUser.id != currentUser.id) {
+            logAndThrow("Current user not primary user. Only primary user"
+                    + " is allowed to take bugreports.");
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void startBugreportLocked(int callingUid, String callingPackage,
+            FileDescriptor bugreportFd, FileDescriptor screenshotFd,
+            int bugreportMode, IDumpstateListener listener) {
+        if (isDumpstateBinderServiceRunningLocked()) {
+            Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport"
+                    + " while another one is currently in progress.");
+            // TODO(b/111441001): Use a new error code; add this to the documentation of the API.
+            reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+            return;
+        }
+
+        IDumpstate ds = startAndGetDumpstateBinderServiceLocked();
+        if (ds == null) {
+            Slog.w(TAG, "Unable to get bugreport service");
+            reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+            return;
+        }
+        try {
+            ds.startBugreport(callingUid, callingPackage,
+                    bugreportFd, screenshotFd, bugreportMode, listener);
+        } catch (RemoteException e) {
+            reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean isDumpstateBinderServiceRunningLocked() {
+        IDumpstate ds = IDumpstate.Stub.asInterface(ServiceManager.getService("dumpstate"));
+        return ds != null;
     }
 
     /*
@@ -115,8 +178,12 @@
      * <p>Generating bugreports requires root privileges. To limit the footprint
      * of the root access, the actual generation in Dumpstate binary is accessed as a
      * oneshot service 'bugreport'.
+     *
+     * <p>Note that starting the service is achieved through setting a system property, which is
+     * not thread-safe. So the lock here offers thread-safety only among callers of the API.
      */
-    private IDumpstate getDumpstateService() {
+    @GuardedBy("mLock")
+    private IDumpstate startAndGetDumpstateBinderServiceLocked() {
         // Start bugreport service.
         SystemProperties.set("ctl.start", BUGREPORT_SERVICE);
 
@@ -145,4 +212,18 @@
         }
         return ds;
     }
+
+    private void reportError(IDumpstateListener listener, int errorCode) {
+        try {
+            listener.onError(errorCode);
+        } catch (RemoteException e) {
+            // Something went wrong in binder or app process. There's nothing to do here.
+            Slog.w(TAG, "onError() transaction threw RemoteException: " + e.getMessage());
+        }
+    }
+
+    private void logAndThrow(String message) {
+        Slog.w(TAG, message);
+        throw new IllegalArgumentException(message);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index a8be07d7..08dea7f 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -480,8 +480,10 @@
             final String apkPath = pkg.baseCodePath;
             final ApplicationInfo appInfo = pkg.applicationInfo;
             final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
-            if (appInfo.isPrivilegedApp()) {
+            if (appInfo.isPrivilegedApp() || appInfo.isDefaultToDeviceProtectedStorage()) {
                 // Privileged apps prefer to load trusted code so they don't use compiled views.
+                // Also disable the view compiler for protected storage apps since there are
+                // selinux permissions required for writing to user_de.
                 return false;
             }
             Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
deleted file mode 100644
index 5f71b0b..0000000
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ /dev/null
@@ -1,67 +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 com.android.server.timezonedetector;
-
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemService;
-import android.app.timezonedetector.ITimeZoneDetectorService;
-import android.content.Context;
-import android.util.Slog;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
-    private static final String TAG = "timezonedetector.TimeZoneDetectorService";
-
-    public static class Lifecycle extends SystemService {
-
-        public Lifecycle(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void onStart() {
-            TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext());
-            // Publish the binder service so it can be accessed from other (appropriately
-            // permissioned) processes.
-            publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
-        }
-    }
-
-    private final Context mContext;
-
-    private static TimeZoneDetectorService create(Context context) {
-        return new TimeZoneDetectorService(context);
-    }
-
-    public TimeZoneDetectorService(Context context) {
-        mContext = context;
-    }
-
-    @Override
-    public void stubbedCall() {
-        // Empty call for initial tests.
-        Slog.d(TAG, "stubbedCall() called");
-        // TODO: Remove when there are real methods.
-    }
-
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-        // TODO: Implement when there is state.
-    }
-}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d5e59c8..c30babd 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -128,6 +128,8 @@
 
     private final WatchLogHandler mWatchLogHandler;
 
+    private IBinder.DeathRecipient mDeathRecipient;
+
     public TvInputManagerService(Context context) {
         super(context);
 
@@ -672,6 +674,7 @@
                 if (sessionToken == userState.mainSessionToken) {
                     setMainLocked(sessionToken, false, callingUid, userId);
                 }
+                sessionState.session.asBinder().unlinkToDeath(sessionState, 0);
                 sessionState.session.release();
             }
         } catch (RemoteException | SessionNotFoundException e) {
@@ -707,6 +710,7 @@
             clientState.sessionTokens.remove(sessionToken);
             if (clientState.isEmpty()) {
                 userState.clientStateMap.remove(sessionState.client.asBinder());
+                sessionState.client.asBinder().unlinkToDeath(clientState, 0);
             }
         }
 
@@ -1000,17 +1004,19 @@
                 synchronized (mLock) {
                     final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     userState.callbackSet.add(callback);
-                    try {
-                        callback.asBinder().linkToDeath(new IBinder.DeathRecipient() {
-                            @Override
-                            public void binderDied() {
-                                synchronized (mLock) {
-                                    if (userState.callbackSet != null) {
-                                        userState.callbackSet.remove(callback);
-                                    }
+                    mDeathRecipient = new IBinder.DeathRecipient() {
+                        @Override
+                        public void binderDied() {
+                            synchronized (mLock) {
+                                if (userState.callbackSet != null) {
+                                    userState.callbackSet.remove(callback);
                                 }
                             }
-                        }, 0);
+                        }
+                    };
+
+                    try {
+                        callback.asBinder().linkToDeath(mDeathRecipient, 0);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "client process has already died", e);
                     }
@@ -1029,6 +1035,7 @@
                 synchronized (mLock) {
                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     userState.callbackSet.remove(callback);
+                    callback.asBinder().unlinkToDeath(mDeathRecipient, 0);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 5f2a0e8..6735ab4 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -120,6 +120,7 @@
         "android.frameworks.schedulerservice@1.0",
         "android.frameworks.sensorservice@1.0",
         "android.system.suspend@1.0",
+        "suspend_control_aidl_interface-cpp",
     ],
 
     static_libs: [
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 0ff60e4..8f6cafb 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -30,8 +30,8 @@
 
 #include <android/hardware/power/1.0/IPower.h>
 #include <android/hardware/power/1.1/IPower.h>
-#include <android/system/suspend/1.0/ISystemSuspend.h>
-#include <android/system/suspend/1.0/ISystemSuspendCallback.h>
+#include <android/system/suspend/BnSuspendCallback.h>
+#include <android/system/suspend/ISuspendControlService.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <jni.h>
 
@@ -44,14 +44,14 @@
 
 using android::hardware::Return;
 using android::hardware::Void;
+using android::system::suspend::BnSuspendCallback;
 using android::hardware::power::V1_0::PowerStatePlatformSleepState;
 using android::hardware::power::V1_0::PowerStateVoter;
 using android::hardware::power::V1_0::Status;
 using android::hardware::power::V1_1::PowerStateSubsystem;
 using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
 using android::hardware::hidl_vec;
-using android::system::suspend::V1_0::ISystemSuspend;
-using android::system::suspend::V1_0::ISystemSuspendCallback;
+using android::system::suspend::ISuspendControlService;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
@@ -66,7 +66,7 @@
 extern sp<IPowerV1_0> getPowerHalV1_0();
 extern sp<IPowerV1_1> getPowerHalV1_1();
 extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
-extern sp<ISystemSuspend> getSuspendHal();
+extern sp<ISuspendControlService> getSuspendControl();
 
 // Java methods used in getLowPowerStats
 static jmethodID jgetAndUpdatePlatformState = NULL;
@@ -74,17 +74,17 @@
 static jmethodID jputVoter = NULL;
 static jmethodID jputState = NULL;
 
-class WakeupCallback : public ISystemSuspendCallback {
-public:
-    Return<void> notifyWakeup(bool success) override {
-        ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
+class WakeupCallback : public BnSuspendCallback {
+   public:
+    binder::Status notifyWakeup(bool success) override {
+        ALOGI("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
         int ret = sem_post(&wakeup_sem);
         if (ret < 0) {
             char buf[80];
             strerror_r(errno, buf, sizeof(buf));
             ALOGE("Error posting wakeup sem: %s\n", buf);
         }
-        return Void();
+        return binder::Status::ok();
     }
 };
 
@@ -107,9 +107,12 @@
             jniThrowException(env, "java/lang/IllegalStateException", buf);
             return -1;
         }
-        ALOGV("Registering callback...");
-        sp<ISystemSuspend> suspendHal = getSuspendHal();
-        suspendHal->registerCallback(new WakeupCallback());
+        sp<ISuspendControlService> suspendControl = getSuspendControl();
+        bool isRegistered = false;
+        suspendControl->registerCallback(new WakeupCallback(), &isRegistered);
+        if (!isRegistered) {
+            ALOGE("Failed to register wakeup callback");
+        }
     }
 
     // Wait for wakeup.
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 0c9b5f4..9be728b 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -20,6 +20,7 @@
 
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/system/suspend/1.0/ISystemSuspend.h>
+#include <android/system/suspend/ISuspendControlService.h>
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
 
@@ -30,13 +31,14 @@
 #include <android-base/chrono_utils.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <hidl/ServiceManagement.h>
 #include <utils/Timers.h>
 #include <utils/misc.h>
 #include <utils/String8.h>
 #include <utils/Log.h>
-#include <hardware/power.h>
-#include <hardware_legacy/power.h>
-#include <hidl/ServiceManagement.h>
 
 #include "com_android_server_power_PowerManagerService.h"
 
@@ -48,6 +50,7 @@
 using android::system::suspend::V1_0::ISystemSuspend;
 using android::system::suspend::V1_0::IWakeLock;
 using android::system::suspend::V1_0::WakeLockType;
+using android::system::suspend::ISuspendControlService;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
@@ -176,6 +179,7 @@
 }
 
 static sp<ISystemSuspend> gSuspendHal = nullptr;
+static sp<ISuspendControlService> gSuspendControl = nullptr;
 static sp<IWakeLock> gSuspendBlocker = nullptr;
 static std::mutex gSuspendMutex;
 
@@ -191,18 +195,33 @@
     return gSuspendHal;
 }
 
+sp<ISuspendControlService> getSuspendControl() {
+    static std::once_flag suspendControlFlag;
+    std::call_once(suspendControlFlag, [](){
+        while(gSuspendControl == nullptr) {
+            sp<IBinder> control =
+                    defaultServiceManager()->getService(String16("suspend_control"));
+            if (control != nullptr) {
+                gSuspendControl = interface_cast<ISuspendControlService>(control);
+            }
+        }
+    });
+    return gSuspendControl;
+}
+
 void enableAutoSuspend() {
     static bool enabled = false;
-
-    std::lock_guard<std::mutex> lock(gSuspendMutex);
     if (!enabled) {
-        sp<ISystemSuspend> suspendHal = getSuspendHal();
-        suspendHal->enableAutosuspend();
-        enabled = true;
+        sp<ISuspendControlService> suspendControl = getSuspendControl();
+        suspendControl->enableAutosuspend(&enabled);
     }
-    if (gSuspendBlocker) {
-        gSuspendBlocker->release();
-        gSuspendBlocker.clear();
+
+    {
+        std::lock_guard<std::mutex> lock(gSuspendMutex);
+        if (gSuspendBlocker) {
+            gSuspendBlocker->release();
+            gSuspendBlocker.clear();
+        }
     }
 }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 75cd82e..2338fff 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -238,8 +238,6 @@
             "com.android.internal.car.CarServiceHelperService";
     private static final String TIME_DETECTOR_SERVICE_CLASS =
             "com.android.server.timedetector.TimeDetectorService$Lifecycle";
-    private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
-            "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
@@ -1316,14 +1314,6 @@
                     reportWtf("starting StartTimeDetectorService service", e);
                 }
                 traceEnd();
-
-                traceBeginAndSlog("StartTimeZoneDetectorService");
-                try {
-                    mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting StartTimeZoneDetectorService service", e);
-                }
-                traceEnd();
             }
 
             if (!isWatch) {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
deleted file mode 100644
index 19d31cf..0000000
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ /dev/null
@@ -1,45 +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 com.android.server.timezonedetector;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Unit tests for the {@link TimeZoneDetectorService}.
- */
-@RunWith(AndroidJUnit4.class)
-public class TimeZoneDetectorServiceTest {
-
-    private TimeZoneDetectorService mTimeZoneDetectorService;
-
-    @Before
-    public void setUp() {
-        final Context context = InstrumentationRegistry.getContext();
-        mTimeZoneDetectorService = new TimeZoneDetectorService(context);
-    }
-
-    @Test
-    public void testStubbedCall() {
-        mTimeZoneDetectorService.stubbedCall();
-    }
-}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7ea61e3..da682c6 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -69,6 +69,7 @@
 import android.provider.Settings;
 import android.service.usb.UsbDeviceManagerProto;
 import android.service.usb.UsbHandlerProto;
+import android.sysprop.AdbProperties;
 import android.sysprop.VoldProperties;
 import android.util.Pair;
 import android.util.Slog;
@@ -285,7 +286,7 @@
         }
         mControlFds.put(UsbManager.FUNCTION_PTP, ptpFd);
 
-        boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
+        boolean secureAdbEnabled = AdbProperties.secure().orElse(false);
         boolean dataEncrypted = "1".equals(VoldProperties.decrypt().orElse(""));
         if (secureAdbEnabled && !dataEncrypted) {
             mDebuggingManager = new UsbDebuggingManager(context);
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 37caeb2..f5b4308 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -58,8 +58,6 @@
         "util.cc",
         "layout_validation.cc",
     ],
-    // b/123880763, clang-tidy analyzer has segmentation fault with dex_builder.cc
-    tidy_checks: ["-clang-analyzer-*"],
     host_supported: true,
 }
 
diff --git a/startop/view_compiler/OWNERS b/startop/view_compiler/OWNERS
new file mode 100644
index 0000000..e5aead9
--- /dev/null
+++ b/startop/view_compiler/OWNERS
@@ -0,0 +1,2 @@
+eholk@google.com
+mathieuc@google.com
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 4c1a0dc7..6047e8c 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -426,7 +426,7 @@
     // Some of the registers don't fit in the four bit short form of the invoke
     // instruction, so we need to do an invoke/range. To do this, we need to
     // first move all the arguments into contiguous temporary registers.
-    std::array<Value, kMaxArgs> scratch{GetScratchRegisters<kMaxArgs>()};
+    std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
 
     const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id());
     CHECK(prototype.has_value());
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 826ad82..818ebd9 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.app.Service;
@@ -32,6 +33,9 @@
 import com.android.internal.telecom.ICallScreeningAdapter;
 import com.android.internal.telecom.ICallScreeningService;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * This service can be implemented by the default dialer (see
  * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
@@ -88,6 +92,128 @@
  * </pre>
  */
 public abstract class CallScreeningService extends Service {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            prefix = { "CALL_DURATION_" },
+            value = {CALL_DURATION_VERY_SHORT, CALL_DURATION_SHORT, CALL_DURATION_MEDIUM,
+                    CALL_DURATION_LONG})
+    public @interface CallDuration {}
+
+    /**
+     * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+     * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+     * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}).  The
+     * {@link CallScreeningService} can use this as a signal for training nuisance detection
+     * algorithms.  The call duration is reported in coarse grained buckets to minimize exposure of
+     * identifying call log information to the {@link CallScreeningService}.
+     * <p>
+     * Indicates the call was < 3 seconds in duration.
+     */
+    public static final int CALL_DURATION_VERY_SHORT = 1;
+
+    /**
+     * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+     * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+     * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}).  The
+     * {@link CallScreeningService} can use this as a signal for training nuisance detection
+     * algorithms.  The call duration is reported in coarse grained buckets to minimize exposure of
+     * identifying call log information to the {@link CallScreeningService}.
+     * <p>
+     * Indicates the call was greater than 3 seconds, but less than 60 seconds in duration.
+     */
+    public static final int CALL_DURATION_SHORT = 2;
+
+    /**
+     * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+     * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+     * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}).  The
+     * {@link CallScreeningService} can use this as a signal for training nuisance detection
+     * algorithms.  The call duration is reported in coarse grained buckets to minimize exposure of
+     * identifying call log information to the {@link CallScreeningService}.
+     * <p>
+     * Indicates the call was greater than 60 seconds, but less than 120 seconds in duration.
+     */
+    public static final int CALL_DURATION_MEDIUM = 3;
+
+    /**
+     * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+     * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+     * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}).  The
+     * {@link CallScreeningService} can use this as a signal for training nuisance detection
+     * algorithms.  The call duration is reported in coarse grained buckets to minimize exposure of
+     * identifying call log information to the {@link CallScreeningService}.
+     * <p>
+     * Indicates the call was greater than 120 seconds.
+     */
+    public static final int CALL_DURATION_LONG = 4;
+
+    /**
+     * Telecom sends this intent to the {@link CallScreeningService} which the user has chosen to
+     * fill the call screening role when the user indicates through the default dialer whether a
+     * call is a nuisance call or not (see
+     * {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}).
+     * <p>
+     * The following extra values are provided for the call:
+     * <ol>
+     *     <li>{@link #EXTRA_CALL_HANDLE} - the handle of the call.</li>
+     *     <li>{@link #EXTRA_IS_NUISANCE} - {@code true} if the user reported the call as a nuisance
+     *     call, {@code false} otherwise.</li>
+     *     <li>{@link #EXTRA_CALL_TYPE} - reports the type of call (incoming, rejected, missed,
+     *     blocked).</li>
+     *     <li>{@link #EXTRA_CALL_DURATION} - the duration of the call (see
+     *     {@link #EXTRA_CALL_DURATION} for valid values).</li>
+     * </ol>
+     * <p>
+     * {@link CallScreeningService} implementations which want to track whether the user reports
+     * calls are nuisance calls should use declare a broadcast receiver in their manifest for this
+     * intent.
+     * <p>
+     * Note: Only {@link CallScreeningService} implementations which have provided
+     * {@link CallIdentification} information for calls at some point will receive this intent.
+     */
+    public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED =
+            "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED";
+
+    /**
+     * Extra used to provide the handle of the call for
+     * {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}.  The call handle is reported as a
+     * {@link Uri}.
+     */
+    public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE";
+
+    /**
+     * Boolean extra used to indicate whether the user reported a call as a nuisance call.
+     * When {@code true}, the user reported that a call was a nuisance call, {@code false}
+     * otherwise.  Sent with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}.
+     */
+    public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE";
+
+    /**
+     * Integer extra used with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report the type of
+     * call. Valid values are:
+     * <UL>
+     *   <li>{@link android.provider.CallLog.Calls#MISSED_TYPE}</li>
+     *   <li>{@link android.provider.CallLog.Calls#INCOMING_TYPE}</li>
+     *   <li>{@link android.provider.CallLog.Calls#BLOCKED_TYPE}</li>
+     *   <li>{@link android.provider.CallLog.Calls#REJECTED_TYPE}</li>
+     * </UL>
+     */
+    public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE";
+
+    /**
+     * Integer extra used to with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report how long
+     * the call lasted.  Valid values are:
+     * <UL>
+     *     <LI>{@link #CALL_DURATION_VERY_SHORT}</LI>
+     *     <LI>{@link #CALL_DURATION_SHORT}</LI>
+     *     <LI>{@link #CALL_DURATION_MEDIUM}</LI>
+     *     <LI>{@link #CALL_DURATION_LONG}</LI>
+     * </UL>
+     */
+    public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java
index 20b04eb..6317770 100644
--- a/telecomm/java/android/telecom/ConferenceParticipant.java
+++ b/telecomm/java/android/telecom/ConferenceParticipant.java
@@ -19,6 +19,10 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConstants;
 
 /**
  * Parcelable representation of a participant's state in a conference call.
@@ -27,6 +31,11 @@
 public class ConferenceParticipant implements Parcelable {
 
     /**
+     * RFC5767 states that a SIP URI with an unknown number should use an address of
+     * {@code anonymous@anonymous.invalid}.  E.g. the host name is anonymous.invalid.
+     */
+    private static final String ANONYMOUS_INVALID_HOST = "anonymous.invalid";
+    /**
      * The conference participant's handle (e.g., phone number).
      */
     private final Uri mHandle;
@@ -50,6 +59,16 @@
     private final int mState;
 
     /**
+     * The connect time of the participant.
+     */
+    private long mConnectTime;
+
+    /**
+     * The connect elapsed time of the participant.
+     */
+    private long mConnectElapsedTime;
+
+    /**
      * Creates an instance of {@code ConferenceParticipant}.
      *
      * @param handle      The conference participant's handle (e.g., phone number).
@@ -92,6 +111,54 @@
     }
 
     /**
+     * Determines the number presentation for a conference participant.  Per RFC5767, if the host
+     * name contains {@code anonymous.invalid} we can assume that there is no valid caller ID
+     * information for the caller, otherwise we'll assume that the URI can be shown.
+     *
+     * @return The number presentation.
+     */
+    @VisibleForTesting
+    public int getParticipantPresentation() {
+        Uri address = getHandle();
+        if (address == null) {
+            return PhoneConstants.PRESENTATION_RESTRICTED;
+        }
+
+        String number = address.getSchemeSpecificPart();
+        // If no number, bail early and set restricted presentation.
+        if (TextUtils.isEmpty(number)) {
+            return PhoneConstants.PRESENTATION_RESTRICTED;
+        }
+        // Per RFC3261, the host name portion can also potentially include extra information:
+        // E.g. sip:anonymous1@anonymous.invalid;legid=1
+        // In this case, hostName will be anonymous.invalid and there is an extra parameter for
+        // legid=1.
+        // Parameters are optional, and the address (e.g. test@test.com) will always be the first
+        // part, with any parameters coming afterwards.
+        String [] hostParts = number.split("[;]");
+        String addressPart = hostParts[0];
+
+        // Get the number portion from the address part.
+        // This will typically be formatted similar to: 6505551212@test.com
+        String [] numberParts = addressPart.split("[@]");
+
+        // If we can't parse the host name out of the URI, then there is probably other data
+        // present, and is likely a valid SIP URI.
+        if (numberParts.length != 2) {
+            return PhoneConstants.PRESENTATION_ALLOWED;
+        }
+        String hostName = numberParts[1];
+
+        // If the hostname portion of the SIP URI is the invalid host string, presentation is
+        // restricted.
+        if (hostName.equals(ANONYMOUS_INVALID_HOST)) {
+            return PhoneConstants.PRESENTATION_RESTRICTED;
+        }
+
+        return PhoneConstants.PRESENTATION_ALLOWED;
+    }
+
+    /**
      * Writes the {@code ConferenceParticipant} to a parcel.
      *
      * @param dest The Parcel in which the object should be written.
@@ -121,6 +188,10 @@
         sb.append(Log.pii(mEndpoint));
         sb.append(" State: ");
         sb.append(Connection.stateToString(mState));
+        sb.append(" ConnectTime: ");
+        sb.append(getConnectTime());
+        sb.append(" ConnectElapsedTime: ");
+        sb.append(getConnectElapsedTime());
         sb.append("]");
         return sb.toString();
     }
@@ -155,4 +226,26 @@
     public int getState() {
         return mState;
     }
+
+    /**
+     * The connect time of the participant to the conference.
+     */
+    public long getConnectTime() {
+        return mConnectTime;
+    }
+
+    public void setConnectTime(long connectTime) {
+        this.mConnectTime = connectTime;
+    }
+
+    /**
+     * The connect elpased time of the participant to the conference.
+     */
+    public long getConnectElapsedTime() {
+        return mConnectElapsedTime;
+    }
+
+    public void setConnectElapsedTime(long connectElapsedTime) {
+        mConnectElapsedTime = connectElapsedTime;
+    }
 }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 05d5a13..bd0d4ae 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -16,10 +16,6 @@
 
 package android.telecom;
 
-import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.IVideoCallback;
-import com.android.internal.telecom.IVideoProvider;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -43,6 +39,10 @@
 import android.util.ArraySet;
 import android.view.Surface;
 
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.IVideoCallback;
+import com.android.internal.telecom.IVideoProvider;
+
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 1de67a5..5a97c94 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,9 +16,9 @@
 
 package android.telecom;
 
+import android.media.ToneGenerator;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.media.ToneGenerator;
 import android.text.TextUtils;
 
 import java.util.Objects;
@@ -91,6 +91,12 @@
      */
     public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
 
+    /**
+     * Reason code, which indicates that the conference call is simulating single party conference.
+     * @hide
+     */
+    public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
+
     private int mDisconnectCode;
     private CharSequence mDisconnectLabel;
     private CharSequence mDisconnectDescription;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 94ed7c3..268e70f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1976,6 +1976,33 @@
     }
 
     /**
+     * Called by the default dialer to report to Telecom when the user has marked a previous
+     * incoming call as a nuisance call or not.
+     * <p>
+     * Where the user has chosen a {@link CallScreeningService} to fill the call screening role,
+     * Telecom will notify that {@link CallScreeningService} of the user's report.
+     * <p>
+     * Requires that the caller is the default dialer app.
+     *
+     * @param handle The phone number of an incoming call which the user is reporting as either a
+     *               nuisance of non-nuisance call.
+     * @param isNuisanceCall {@code true} if the user is reporting the call as a nuisance call,
+     *                       {@code false} if the user is reporting the call as a non-nuisance call.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public void reportNuisanceCallStatus(@NonNull Uri handle, boolean isNuisanceCall) {
+        ITelecomService service = getTelecomService();
+        if (service != null) {
+            try {
+                service.reportNuisanceCallStatus(handle, isNuisanceCall,
+                        mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
+            }
+        }
+    }
+
+    /**
      * Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
      * @param intent The {@link Intent#ACTION_CALL} intent to handle.
      * @hide
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index e1d5c17..5030f90 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -285,6 +285,8 @@
      */
     boolean isInEmergencyCall();
 
+    oneway void reportNuisanceCallStatus(in Uri address, boolean isNuisance, String callingPackage);
+
     /**
      * @see TelecomServiceImpl#handleCallIntent
      */
@@ -299,4 +301,5 @@
     void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
 
     void setTestAutoModeApp(String packageName);
+
 }
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index fb8f3e7..51c8f65 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2054,6 +2054,643 @@
     }
 
     /**
+     * Columns for the "rcs_*" tables used by {@link android.telephony.ims.RcsMessageStore} classes.
+     *
+     * @hide - not meant for public use
+     */
+    public interface RcsColumns {
+        /**
+         * The authority for the content provider
+         */
+        String AUTHORITY = "rcs";
+
+        /**
+         * The URI to start building upon to use {@link com.android.providers.telephony.RcsProvider}
+         */
+        Uri CONTENT_AND_AUTHORITY = Uri.parse("content://" + AUTHORITY);
+
+        /**
+         * The value to be used whenever a transaction that expects an integer to be returned
+         * failed.
+         */
+        int TRANSACTION_FAILED = Integer.MIN_VALUE;
+
+        /**
+         * The value that denotes a timestamp was not set before (e.g. a message that is not
+         * delivered yet will not have a DELIVERED_TIMESTAMP)
+         */
+        long TIMESTAMP_NOT_SET = 0;
+
+        /**
+         * The table that {@link android.telephony.ims.RcsThread} gets persisted to
+         */
+        interface RcsThreadColumns {
+            /**
+             * The path that should be used for referring to
+             * {@link android.telephony.ims.RcsThread}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String RCS_THREAD_URI_PART = "thread";
+
+            /**
+             * The URI to query or modify {@link android.telephony.ims.RcsThread} via the content
+             * provider.
+             */
+            Uri RCS_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, RCS_THREAD_URI_PART);
+
+            /**
+             * The unique identifier of an {@link android.telephony.ims.RcsThread}
+             */
+            String RCS_THREAD_ID_COLUMN = "rcs_thread_id";
+        }
+
+        /**
+         * The table that {@link android.telephony.ims.Rcs1To1Thread} gets persisted to
+         */
+        interface Rcs1To1ThreadColumns extends RcsThreadColumns {
+            /**
+             * The path that should be used for referring to
+             * {@link android.telephony.ims.Rcs1To1Thread}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String RCS_1_TO_1_THREAD_URI_PART = "p2p_thread";
+
+            /**
+             * The URI to query or modify {@link android.telephony.ims.Rcs1To1Thread}s via the
+             * content provider
+             */
+            Uri RCS_1_TO_1_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+                    RCS_1_TO_1_THREAD_URI_PART);
+
+            /**
+             * The SMS/MMS thread to fallback to in case of an RCS outage
+             */
+            String FALLBACK_THREAD_ID_COLUMN = "rcs_fallback_thread_id";
+        }
+
+        /**
+         * The table that {@link android.telephony.ims.RcsGroupThread} gets persisted to
+         */
+        interface RcsGroupThreadColumns extends RcsThreadColumns {
+            /**
+             * The path that should be used for referring to
+             * {@link android.telephony.ims.RcsGroupThread}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String RCS_GROUP_THREAD_URI_PART = "group_thread";
+
+            /**
+             * The URI to query or modify {@link android.telephony.ims.RcsGroupThread}s via the
+             * content provider
+             */
+            Uri RCS_GROUP_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+                    RCS_GROUP_THREAD_URI_PART);
+
+            /**
+             * The owner/admin of the {@link android.telephony.ims.RcsGroupThread}
+             */
+            String OWNER_PARTICIPANT_COLUMN = "owner_participant";
+
+            /**
+             * The user visible name of the group
+             */
+            String GROUP_NAME_COLUMN = "group_name";
+
+            /**
+             * The user visible icon of the group
+             */
+            String GROUP_ICON_COLUMN = "group_icon";
+
+            /**
+             * The RCS conference URI for this group
+             */
+            String CONFERENCE_URI_COLUMN = "conference_uri";
+        }
+
+        /**
+         * The view that enables polling from all types of RCS threads at once
+         */
+        interface RcsUnifiedThreadColumns extends RcsThreadColumns, Rcs1To1ThreadColumns,
+                RcsGroupThreadColumns {
+            /**
+             * The type of this {@link android.telephony.ims.RcsThread}
+             */
+            String THREAD_TYPE_COLUMN = "thread_type";
+
+            /**
+             * Integer returned as a result from a database query that denotes the thread is 1 to 1
+             */
+            int THREAD_TYPE_1_TO_1 = 0;
+
+            /**
+             * Integer returned as a result from a database query that denotes the thread is 1 to 1
+             */
+            int THREAD_TYPE_GROUP = 1;
+        }
+
+        /**
+         * The table that {@link android.telephony.ims.RcsParticipant} gets persisted to
+         */
+        interface RcsParticipantColumns {
+            /**
+             * The path that should be used for referring to
+             * {@link android.telephony.ims.RcsParticipant}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String RCS_PARTICIPANT_URI_PART = "participant";
+
+            /**
+             * The URI to query or modify {@link android.telephony.ims.RcsParticipant}s via the
+             * content provider
+             */
+            Uri RCS_PARTICIPANT_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+                    RCS_PARTICIPANT_URI_PART);
+
+            /**
+             * The unique identifier of the entry in the database
+             */
+            String RCS_PARTICIPANT_ID_COLUMN = "rcs_participant_id";
+
+            /**
+             * A foreign key on canonical_address table, also used by SMS/MMS
+             */
+            String CANONICAL_ADDRESS_ID_COLUMN = "canonical_address_id";
+
+            /**
+             * The user visible RCS alias for this participant.
+             */
+            String RCS_ALIAS_COLUMN = "rcs_alias";
+        }
+
+        /**
+         * Additional constants to enable access to {@link android.telephony.ims.RcsParticipant}
+         * related data
+         */
+        interface RcsParticipantHelpers extends RcsParticipantColumns {
+            /**
+             * The view that unifies "rcs_participant" and "canonical_addresses" tables for easy
+             * access to participant address.
+             */
+            String RCS_PARTICIPANT_WITH_ADDRESS_VIEW = "rcs_participant_with_address_view";
+
+            /**
+             * The view that unifies "rcs_participant", "canonical_addresses" and
+             * "rcs_thread_participant" junction table to get full information on participants that
+             * contribute to threads.
+             */
+            String RCS_PARTICIPANT_WITH_THREAD_VIEW = "rcs_participant_with_thread_view";
+        }
+
+        /**
+         * The table that {@link android.telephony.ims.RcsMessage} gets persisted to
+         */
+        interface RcsMessageColumns {
+            /**
+             * Denotes the type of this message (i.e.
+             * {@link android.telephony.ims.RcsIncomingMessage} or
+             * {@link android.telephony.ims.RcsOutgoingMessage}
+             */
+            String MESSAGE_TYPE_COLUMN = "rcs_message_type";
+
+            /**
+             * The unique identifier for the message in the database - i.e. the primary key.
+             */
+            String MESSAGE_ID_COLUMN = "rcs_message_row_id";
+
+            /**
+             * The globally unique RCS identifier for the message. Please see 4.4.5.2 - GSMA
+             * RCC.53 (RCS Device API 1.6 Specification)
+             */
+            String GLOBAL_ID_COLUMN = "rcs_message_global_id";
+
+            /**
+             * The subscription where this message was sent from/to.
+             */
+            String SUB_ID_COLUMN = "sub_id";
+
+            /**
+             * The sending status of the message.
+             * @see android.telephony.ims.RcsMessage.RcsMessageStatus
+             */
+            String STATUS_COLUMN = "status";
+
+            /**
+             * The creation timestamp of the message.
+             */
+            String ORIGINATION_TIMESTAMP_COLUMN = "origination_timestamp";
+
+            /**
+             * The text content of the message.
+             */
+            String MESSAGE_TEXT_COLUMN = "rcs_text";
+
+            /**
+             * The latitude content of the message, if it contains a location.
+             */
+            String LATITUDE_COLUMN = "latitude";
+
+            /**
+             * The longitude content of the message, if it contains a location.
+             */
+            String LONGITUDE_COLUMN = "longitude";
+        }
+
+        /**
+         * The table that additional information of {@link android.telephony.ims.RcsIncomingMessage}
+         * gets persisted to.
+         */
+        interface RcsIncomingMessageColumns extends RcsMessageColumns {
+            /**
+             The path that should be used for referring to
+             * {@link android.telephony.ims.RcsIncomingMessage}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String INCOMING_MESSAGE_URI_PART = "incoming_message";
+
+            /**
+             * The URI to query incoming messages through
+             * {@link com.android.providers.telephony.RcsProvider}
+             */
+            Uri INCOMING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+                    INCOMING_MESSAGE_URI_PART);
+
+            /**
+             * The ID of the {@link android.telephony.ims.RcsParticipant} that sent this message
+             */
+            String SENDER_PARTICIPANT_ID_COLUMN = "sender_participant";
+
+            /**
+             * The timestamp of arrival for this message.
+             */
+            String ARRIVAL_TIMESTAMP_COLUMN = "arrival_timestamp";
+
+            /**
+             * The time when the recipient has read this message.
+             */
+            String SEEN_TIMESTAMP_COLUMN = "seen_timestamp";
+        }
+
+        /**
+         * The table that additional information of {@link android.telephony.ims.RcsOutgoingMessage}
+         * gets persisted to.
+         */
+        interface RcsOutgoingMessageColumns extends RcsMessageColumns {
+            /**
+             * The path that should be used for referring to
+             * {@link android.telephony.ims.RcsOutgoingMessage}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String OUTGOING_MESSAGE_URI_PART = "outgoing_message";
+
+            /**
+             * The URI to query or modify {@link android.telephony.ims.RcsOutgoingMessage}s via the
+             * content provider
+             */
+            Uri OUTGOING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+                    OUTGOING_MESSAGE_URI_PART);
+        }
+
+        /**
+         * The delivery information of an {@link android.telephony.ims.RcsOutgoingMessage}
+         */
+        interface RcsMessageDeliveryColumns extends RcsOutgoingMessageColumns {
+            /**
+             * The path that should be used for referring to
+             * {@link android.telephony.ims.RcsOutgoingMessageDelivery}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String DELIVERY_URI_PART = "delivery";
+
+            /**
+             * The timestamp of delivery of this message.
+             */
+            String DELIVERED_TIMESTAMP_COLUMN = "delivered_timestamp";
+
+            /**
+             * The time when the recipient has read this message.
+             */
+            String SEEN_TIMESTAMP_COLUMN = "seen_timestamp";
+        }
+
+        /**
+         * The views that allow querying {@link android.telephony.ims.RcsIncomingMessage} and
+         * {@link android.telephony.ims.RcsOutgoingMessage} at the same time.
+         */
+        interface RcsUnifiedMessageColumns extends RcsIncomingMessageColumns,
+                RcsOutgoingMessageColumns {
+            /**
+             * The path that is used to query all {@link android.telephony.ims.RcsMessage} in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String UNIFIED_MESSAGE_URI_PART = "message";
+
+            /**
+             * The URI to query all types of {@link android.telephony.ims.RcsMessage}s
+             */
+            Uri UNIFIED_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+                    UNIFIED_MESSAGE_URI_PART);
+
+            /**
+             * The name of the view that unites rcs_message and rcs_incoming_message tables.
+             */
+            String UNIFIED_INCOMING_MESSAGE_VIEW = "unified_incoming_message_view";
+
+            /**
+             * The name of the view that unites rcs_message and rcs_outgoing_message tables.
+             */
+            String UNIFIED_OUTGOING_MESSAGE_VIEW = "unified_outgoing_message_view";
+
+            /**
+             * The column that shows from which table the message entry came from.
+             */
+            String MESSAGE_TYPE_COLUMN = "message_type";
+
+            /**
+             * Integer returned as a result from a database query that denotes that the message is
+             * an incoming message
+             */
+            int MESSAGE_TYPE_INCOMING = 1;
+
+            /**
+             * Integer returned as a result from a database query that denotes that the message is
+             * an outgoing message
+             */
+            int MESSAGE_TYPE_OUTGOING = 0;
+        }
+
+        /**
+         * The table that {@link android.telephony.ims.RcsFileTransferPart} gets persisted to.
+         */
+        interface RcsFileTransferColumns {
+            /**
+             * The path that should be used for referring to
+             * {@link android.telephony.ims.RcsFileTransferPart}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String FILE_TRANSFER_URI_PART = "file_transfer";
+
+            /**
+             * The URI to query or modify {@link android.telephony.ims.RcsFileTransferPart}s via the
+             * content provider
+             */
+            Uri FILE_TRANSFER_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+                    FILE_TRANSFER_URI_PART);
+
+            /**
+             * The globally unique file transfer ID for this RCS file transfer.
+             */
+            String FILE_TRANSFER_ID_COLUMN = "rcs_file_transfer_id";
+
+            /**
+             * The RCS session ID for this file transfer. The ID is implementation dependent but
+             * should be unique.
+             */
+            String SESSION_ID_COLUMN = "session_id";
+
+            /**
+             * The URI that points to the content of this file transfer
+             */
+            String CONTENT_URI_COLUMN = "content_uri";
+
+            /**
+             * The file type of this file transfer in bytes. The validity of types is not enforced
+             * in {@link android.telephony.ims.RcsMessageStore} APIs.
+             */
+            String CONTENT_TYPE_COLUMN = "content_type";
+
+            /**
+             * The size of the file transfer in bytes.
+             */
+            String FILE_SIZE_COLUMN = "file_size";
+
+            /**
+             * Number of bytes that was successfully transmitted for this file transfer
+             */
+            String SUCCESSFULLY_TRANSFERRED_BYTES = "transfer_offset";
+
+            /**
+             * The status of this file transfer
+             * @see android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus
+             */
+            String TRANSFER_STATUS_COLUMN = "transfer_status";
+
+            /**
+             * The on-screen width of the file transfer, if it contains multi-media
+             */
+            String WIDTH_COLUMN = "width";
+
+            /**
+             * The on-screen height of the file transfer, if it contains multi-media
+             */
+            String HEIGHT_COLUMN = "height";
+
+            /**
+             * The duration of the content in milliseconds if this file transfer contains
+             * multi-media
+             */
+            String DURATION_MILLIS_COLUMN = "duration";
+
+            /**
+             * The URI to the preview of the content of this file transfer
+             */
+            String PREVIEW_URI_COLUMN = "preview_uri";
+
+            /**
+             * The type of the preview of the content of this file transfer. The validity of types
+             * is not enforced in {@link android.telephony.ims.RcsMessageStore} APIs.
+             */
+            String PREVIEW_TYPE_COLUMN = "preview_type";
+        }
+
+        /**
+         * The table that holds the information for
+         * {@link android.telephony.ims.RcsGroupThreadEvent} and its subclasses.
+         */
+        interface RcsThreadEventColumns {
+            /**
+             * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+             * refer to participant joined events (example URI:
+             * {@code content://rcs/group_thread/3/participant_joined_event})
+             */
+            String PARTICIPANT_JOINED_URI_PART = "participant_joined_event";
+
+            /**
+             * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+             * refer to participant left events. (example URI:
+             * {@code content://rcs/group_thread/3/participant_left_event/4})
+             */
+            String PARTICIPANT_LEFT_URI_PART = "participant_left_event";
+
+            /**
+             * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+             * refer to name changed events. (example URI:
+             * {@code content://rcs/group_thread/3/name_changed_event})
+             */
+            String NAME_CHANGED_URI_PART = "name_changed_event";
+
+            /**
+             * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+             * refer to icon changed events. (example URI:
+             * {@code content://rcs/group_thread/3/icon_changed_event})
+             */
+            String ICON_CHANGED_URI_PART = "icon_changed_event";
+
+            /**
+             * The unique ID of this event in the database, i.e. the primary key
+             */
+            String EVENT_ID_COLUMN = "event_id";
+
+            /**
+             * The type of this event
+             *
+             * @see RcsEventTypes
+             */
+            String EVENT_TYPE_COLUMN = "event_type";
+
+            /**
+             * The timestamp in milliseconds of when this event happened
+             */
+            String TIMESTAMP_COLUMN = "origination_timestamp";
+
+            /**
+             * The participant that generated this event
+             */
+            String SOURCE_PARTICIPANT_ID_COLUMN = "source_participant";
+
+            /**
+             * The receiving participant of this event if this was an
+             * {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent} or
+             * {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent}
+             */
+            String DESTINATION_PARTICIPANT_ID_COLUMN = "destination_participant";
+
+            /**
+             * The URI for the new icon of the group thread if this was an
+             * {@link android.telephony.ims.RcsGroupThreadIconChangedEvent}
+             */
+            String NEW_ICON_URI_COLUMN = "new_icon_uri";
+
+            /**
+             * The URI for the new name of the group thread if this was an
+             * {@link android.telephony.ims.RcsGroupThreadNameChangedEvent}
+             */
+            String NEW_NAME_COLUMN = "new_name";
+        }
+
+        /**
+         * The table that {@link android.telephony.ims.RcsParticipantAliasChangedEvent} gets
+         * persisted to
+         */
+        interface RcsParticipantEventColumns {
+            /**
+             * The path that should be used for referring to
+             * {@link android.telephony.ims.RcsParticipantAliasChangedEvent}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String ALIAS_CHANGE_EVENT_URI_PART = "alias_change_event";
+
+            /**
+             * The new alias of the participant
+             */
+            String NEW_ALIAS_COLUMN = "new_alias";
+        }
+
+        /**
+         * These values are used in {@link com.android.providers.telephony.RcsProvider} to determine
+         * what kind of event is present in the storage.
+         */
+        interface RcsEventTypes {
+            /**
+             * Integer constant that is stored in the
+             * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+             * is of type {@link android.telephony.ims.RcsParticipantAliasChangedEvent}
+             */
+            int PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE = 1;
+
+            /**
+             * Integer constant that is stored in the
+             * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+             * is of type {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent}
+             */
+            int PARTICIPANT_JOINED_EVENT_TYPE = 2;
+
+            /**
+             * Integer constant that is stored in the
+             * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+             * is of type {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent}
+             */
+            int PARTICIPANT_LEFT_EVENT_TYPE = 4;
+
+            /**
+             * Integer constant that is stored in the
+             * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+             * is of type {@link android.telephony.ims.RcsGroupThreadIconChangedEvent}
+             */
+            int ICON_CHANGED_EVENT_TYPE = 8;
+
+            /**
+             * Integer constant that is stored in the
+             * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+             * is of type {@link android.telephony.ims.RcsGroupThreadNameChangedEvent}
+             */
+            int NAME_CHANGED_EVENT_TYPE = 16;
+        }
+
+        /**
+         * The view that allows unified querying across all events
+         */
+        interface RcsUnifiedEventHelper extends RcsParticipantEventColumns, RcsThreadEventColumns {
+            /**
+             * The path that should be used for referring to
+             * {@link android.telephony.ims.RcsEvent}s in
+             * {@link com.android.providers.telephony.RcsProvider} URIs.
+             */
+            String RCS_EVENT_QUERY_URI_PATH = "event";
+
+            /**
+             * The URI to query {@link android.telephony.ims.RcsEvent}s via the content provider.
+             */
+            Uri RCS_EVENT_QUERY_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+                    RCS_EVENT_QUERY_URI_PATH);
+        }
+
+        /**
+         * Allows RCS specific canonical address handling.
+         */
+        interface RcsCanonicalAddressHelper {
+            /**
+             * Returns the canonical address ID for a canonical address, if now row exists, this
+             * will add a row and return its ID. This helper works against the same table used by
+             * the SMS and MMS threads, but is accessible only by the phone process for use by RCS
+             * message storage.
+             *
+             * @throws IllegalArgumentException if unable to retrieve or create the canonical
+             *                                  address entry.
+             */
+            static long getOrCreateCanonicalAddressId(
+                    ContentResolver contentResolver, String canonicalAddress) {
+
+                Uri.Builder uriBuilder = CONTENT_AND_AUTHORITY.buildUpon();
+                uriBuilder.appendPath("canonical-address");
+                uriBuilder.appendQueryParameter("address", canonicalAddress);
+                Uri uri = uriBuilder.build();
+
+                try (Cursor cursor = contentResolver.query(uri, null, null, null)) {
+                    if (cursor != null && cursor.moveToFirst()) {
+                        return cursor.getLong(cursor.getColumnIndex(CanonicalAddressesColumns._ID));
+                    } else {
+                        Rlog.e(TAG, "getOrCreateCanonicalAddressId returned no rows");
+                    }
+                }
+
+                Rlog.e(TAG, "getOrCreateCanonicalAddressId failed");
+                throw new IllegalArgumentException(
+                        "Unable to find or allocate a canonical address ID");
+            }
+        }
+    }
+
+    /**
      * Contains all MMS messages.
      */
     public static final class Mms implements BaseMmsColumns {
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 9c64cf6..75165af 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -54,8 +54,15 @@
      */
     @SystemApi
     public static final class TransportType {
+        /**
+         * Invalid transport type.
+         * @hide
+         */
+        public static final int INVALID = -1;
+
         /** Wireless Wide Area Networks (i.e. Cellular) */
         public static final int WWAN = 1;
+
         /** Wireless Local Area Networks (i.e. Wifi) */
         public static final int WLAN = 2;
 
@@ -65,6 +72,7 @@
         /** @hide */
         public static String toString(int type) {
             switch (type) {
+                case INVALID: return "INVALID";
                 case WWAN: return "WWAN";
                 case WLAN: return "WLAN";
                 default: return Integer.toString(type);
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index 4da79b3..b407b2a 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -114,7 +114,7 @@
         in.readStringList(mMccMncs);
     }
 
-    public AvailableNetworkInfo(int subId, int priority, ArrayList<String> mccMncs) {
+    public AvailableNetworkInfo(int subId, int priority, List<String> mccMncs) {
         mSubId = subId;
         mPriority = priority;
         mMccMncs = new ArrayList<String>(mccMncs);
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index a4cce9c..0d4f09f 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -117,9 +117,9 @@
 
         CallAttributes s = (CallAttributes) o;
 
-        return (mPreciseCallState == s.mPreciseCallState
+        return (Objects.equals(mPreciseCallState, s.mPreciseCallState)
                 && mNetworkType == s.mNetworkType
-                && mCallQuality == s.mCallQuality);
+                && Objects.equals(mCallQuality, s.mCallQuality));
     }
 
     /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 190e82b..5c5d856 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2371,6 +2371,14 @@
             "support_emergency_dialer_shortcut_bool";
 
     /**
+     * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable
+     * this feature.
+     * @hide
+     */
+    public static final String KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL =
+            "ascii_7_bit_support_for_long_message_bool";
+
+    /**
      * Controls RSRP threshold at which OpportunisticNetworkService will decide whether
      * the opportunistic network is good enough for internet data.
      */
@@ -2780,6 +2788,7 @@
         sDefaults.putBoolean(KEY_CALL_WAITING_OVER_UT_WARNING_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL, true);
+        sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
         sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index 37847ae..d47b55c 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -27,6 +27,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Contains the list of carrier restrictions.
@@ -93,6 +94,9 @@
             value = {CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED, CARRIER_RESTRICTION_DEFAULT_ALLOWED})
     public @interface CarrierRestrictionDefault {}
 
+    /* Wild character for comparison */
+    private static final char WILD_CHARACTER = '?';
+
     private List<CarrierIdentifier> mAllowedCarriers;
     private List<CarrierIdentifier> mExcludedCarriers;
     @CarrierRestrictionDefault
@@ -166,6 +170,124 @@
     }
 
     /**
+     * Tests an array of carriers with the carrier restriction configuration. The list of carrier
+     * ids passed as argument does not need to be the same as currently present in the device.
+     *
+     * @param carrierIds list of {@link CarrierIdentifier}, one for each SIM slot on the device
+     * @return a list of boolean with the same size as input, indicating if each
+     * {@link CarrierIdentifier} is allowed or not.
+     */
+    public List<Boolean> isCarrierIdentifiersAllowed(@NonNull List<CarrierIdentifier> carrierIds) {
+        ArrayList<Boolean> result = new ArrayList<>(carrierIds.size());
+
+        // First calculate the result for each slot independently
+        for (int i = 0; i < carrierIds.size(); i++) {
+            boolean inAllowedList = isCarrierIdInList(carrierIds.get(i), mAllowedCarriers);
+            boolean inExcludedList = isCarrierIdInList(carrierIds.get(i), mExcludedCarriers);
+            if (mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED) {
+                result.add((inAllowedList && !inExcludedList) ? true : false);
+            } else {
+                result.add((inExcludedList && !inAllowedList) ? false : true);
+            }
+        }
+        // Apply the multi-slot policy, if needed.
+        if (mMultiSimPolicy == MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT) {
+            for (boolean b : result) {
+                if (b) {
+                    result.replaceAll(x -> true);
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Indicates if a certain carrier {@code id} is present inside a {@code list}
+     *
+     * @return true if the carrier {@code id} is present, false otherwise
+     */
+    private static boolean isCarrierIdInList(CarrierIdentifier id, List<CarrierIdentifier> list) {
+        for (CarrierIdentifier listItem : list) {
+            // Compare MCC and MNC
+            if (!patternMatch(id.getMcc(), listItem.getMcc())
+                    || !patternMatch(id.getMnc(), listItem.getMnc())) {
+                continue;
+            }
+
+            // Compare SPN. Comparison is on the complete strings, case insensitive and with wild
+            // characters.
+            String listItemValue = convertNullToEmpty(listItem.getSpn());
+            String idValue = convertNullToEmpty(id.getSpn());
+            if (!listItemValue.isEmpty()) {
+                if (!patternMatch(idValue, listItemValue)) {
+                    continue;
+                }
+            }
+
+            // The IMSI of the configuration can be shorter than actual IMSI in the SIM card.
+            listItemValue = convertNullToEmpty(listItem.getImsi());
+            idValue = convertNullToEmpty(id.getImsi());
+            if (!patternMatch(
+                    idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
+                    listItemValue)) {
+                continue;
+            }
+
+            // The GID1 of the configuration can be shorter than actual GID1 in the SIM card.
+            listItemValue = convertNullToEmpty(listItem.getGid1());
+            idValue = convertNullToEmpty(id.getGid1());
+            if (!patternMatch(
+                    idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
+                    listItemValue)) {
+                continue;
+            }
+
+            // The GID2 of the configuration can be shorter than actual GID2 in the SIM card.
+            listItemValue = convertNullToEmpty(listItem.getGid2());
+            idValue = convertNullToEmpty(id.getGid2());
+            if (!patternMatch(
+                    idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
+                    listItemValue)) {
+                continue;
+            }
+
+            // Valid match was found in the list
+            return true;
+        }
+        return false;
+    }
+
+    private static String convertNullToEmpty(String value) {
+        return Objects.toString(value, "");
+    }
+
+    /**
+     * Performs a case insensitive string comparison against a given pattern. The character '?'
+     * is used in the pattern as wild character in the comparison. The string must have the same
+     * length as the pattern.
+     *
+     * @param str string to match
+     * @param pattern string containing the pattern
+     * @return true in case of match, false otherwise
+     */
+    private static boolean patternMatch(String str, String pattern) {
+        if (str.length() != pattern.length()) {
+            return false;
+        }
+        String lowerCaseStr = str.toLowerCase();
+        String lowerCasePattern = pattern.toLowerCase();
+
+        for (int i = 0; i < lowerCasePattern.length(); i++) {
+            if (lowerCasePattern.charAt(i) != lowerCaseStr.charAt(i)
+                    && lowerCasePattern.charAt(i) != WILD_CHARACTER) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * {@link Parcelable#writeToParcel}
      */
     @Override
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 6b1b84c..856f081 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -30,6 +30,7 @@
     private final int mNrArfcn;
     private final int mPci;
     private final int mTac;
+    private final long mNci;
 
     /**
      *
@@ -44,11 +45,12 @@
      * @hide
      */
     public CellIdentityNr(int pci, int tac, int nrArfcn,  String mccStr, String mncStr,
-            String alphal, String alphas) {
+            long nci, String alphal, String alphas) {
         super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
         mPci = pci;
         mTac = tac;
         mNrArfcn = nrArfcn;
+        mNci = nci;
     }
 
     /**
@@ -62,7 +64,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn);
+        return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, mNci);
     }
 
     @Override
@@ -72,7 +74,17 @@
         }
 
         CellIdentityNr o = (CellIdentityNr) other;
-        return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn;
+        return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn
+                && mNci == o.mNci;
+    }
+
+    /**
+     * Get the NR Cell Identity.
+     *
+     * @return The NR Cell Identity in range [0, 68719476735] or Long.MAX_VALUE if unknown.
+     */
+    public long getNci() {
+        return mNci;
     }
 
     /**
@@ -122,6 +134,7 @@
                 .append(" mNrArfcn = ").append(mNrArfcn)
                 .append(" mMcc = ").append(mMccStr)
                 .append(" mMnc = ").append(mMncStr)
+                .append(" mNci = ").append(mNci)
                 .append(" mAlphaLong = ").append(mAlphaLong)
                 .append(" mAlphaShort = ").append(mAlphaShort)
                 .append(" }")
@@ -134,6 +147,7 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mNrArfcn);
+        dest.writeLong(mNci);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -142,6 +156,7 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mNrArfcn = in.readInt();
+        mNci = in.readLong();
     }
 
     /** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 3814333..dba437a 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -141,6 +141,14 @@
         return mCpid;
     }
 
+    /**
+     * @return 16-bit UMTS Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getUarfcn() {
+        return mUarfcn;
+    }
+
     /** @hide */
     @Override
     public int getChannelNumber() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 30e641d..a4207c9 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -63,6 +63,10 @@
     public CellSignalStrengthGsm(android.hardware.radio.V1_0.GsmSignalStrength gsm) {
         // Convert from HAL values as part of construction.
         this(getRssiDbmFromAsu(gsm.signalStrength), gsm.bitErrorRate, gsm.timingAdvance);
+
+        if (mRssi == CellInfo.UNAVAILABLE) {
+            setDefaultValues();
+        }
     }
 
     /** @hide */
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index 6f52b85..5ae89b0 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -72,6 +72,10 @@
         // Convert from HAL values as part of construction.
         this(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 tdscdma.rscp != CellInfo.UNAVAILABLE ? -tdscdma.rscp : tdscdma.rscp);
+
+        if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) {
+            setDefaultValues();
+        }
     }
 
     /** @hide */
@@ -79,6 +83,10 @@
         // Convert from HAL values as part of construction.
         this(getRssiDbmFromAsu(tdscdma.signalStrength),
                 tdscdma.bitErrorRate, getRscpDbmFromAsu(tdscdma.rscp));
+
+        if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) {
+            setDefaultValues();
+        }
     }
 
     /** @hide */
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 0760407..efa3647 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -92,8 +92,12 @@
     /** @hide */
     public CellSignalStrengthWcdma(android.hardware.radio.V1_0.WcdmaSignalStrength wcdma) {
         // Convert from HAL values as part of construction.
-        this(getRssiDbmFromAsu(wcdma.signalStrength),
-                wcdma.bitErrorRate, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE);
+        this(getRssiDbmFromAsu(wcdma.signalStrength), wcdma.bitErrorRate,
+                CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE);
+
+        if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) {
+            setDefaultValues();
+        }
     }
 
     /** @hide */
@@ -103,6 +107,10 @@
                     wcdma.base.bitErrorRate,
                     getRscpDbmFromAsu(wcdma.rscp),
                     getEcNoDbFromAsu(wcdma.ecno));
+
+        if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) {
+            setDefaultValues();
+        }
     }
 
     /** @hide */
diff --git a/telephony/java/android/telephony/DebugEventReporter.java b/telephony/java/android/telephony/DebugEventReporter.java
new file mode 100644
index 0000000..14b7dd6
--- /dev/null
+++ b/telephony/java/android/telephony/DebugEventReporter.java
@@ -0,0 +1,174 @@
+/*
+ * 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.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.ParcelUuid;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A Simple Surface for Telephony to notify a loosely-coupled debugger of particular issues.
+ *
+ * DebugEventReporter allows an optional external logging component to receive events detected by
+ * the framework and take action. This log surface is designed to provide maximium flexibility
+ * to the receiver of these events. Envisioned use cases of this include notifying a vendor
+ * component of: an event that necessitates (timely) log collection on non-AOSP components;
+ * notifying a vendor component of a rare event that should prompt further action such as a
+ * bug report or user intervention for debug purposes.
+ *
+ * <p>This surface is not intended to enable a diagnostic monitor, nor is it intended to support
+ * streaming logs.
+ *
+ * @hide
+ */
+public final class DebugEventReporter {
+    private static final String TAG = "DebugEventReporter";
+
+    private static Context sContext = null;
+
+    private static Map<UUID, Integer> sEvents = new ConcurrentHashMap<>();
+
+    /*
+     * Because this is only supporting system packages, once we find a package, it will be the
+     * same package until the next system upgrade. Thus, to save time in processing debug events
+     * we can cache this info and skip the resolution process after it's done the first time.
+     */
+    private static String sDebugPackageName = null;
+
+    private DebugEventReporter() {};
+
+    /**
+     * If enabled, build and send an intent to a Debug Service for logging.
+     *
+     * This method sends the {@link TelephonyManager#DEBUG_EVENT DEBUG_EVENT} broadcast, which is
+     * system protected. Invoking this method unless you are the system will result in an error.
+     *
+     * @param eventId a fixed event ID that will be sent for each instance of the same event. This
+     *        ID should be generated randomly.
+     * @param description an optional description, that if included will be used as the subject for
+     *        identification and discussion of this event. This description should ideally be
+     *        static and must not contain any sensitive information (especially PII).
+     */
+    public static void sendEvent(@NonNull UUID eventId, String description) {
+        if (sContext == null) {
+            Rlog.w(TAG, "DebugEventReporter not yet initialized, dropping event=" + eventId);
+            return;
+        }
+
+        // If this event has already occurred, skip sending intents for it; regardless log its
+        // invocation here.
+        Integer count = sEvents.containsKey(eventId) ? sEvents.get(eventId) + 1 : 1;
+        sEvents.put(eventId, count);
+        if (count > 1) return;
+
+        // Even if we are initialized, that doesn't mean that a package name has been found.
+        // This is normal in many cases, such as when no debug package is installed on the system,
+        // so drop these events silently.
+        if (sDebugPackageName == null) return;
+
+        Intent dbgIntent = new Intent(TelephonyManager.ACTION_DEBUG_EVENT);
+        dbgIntent.putExtra(TelephonyManager.EXTRA_DEBUG_EVENT_ID, new ParcelUuid(eventId));
+        if (description != null) {
+            dbgIntent.putExtra(TelephonyManager.EXTRA_DEBUG_EVENT_DESCRIPTION, description);
+        }
+        dbgIntent.setPackage(sDebugPackageName);
+        sContext.sendBroadcast(dbgIntent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+    }
+
+    /**
+     * Initialize the DebugEventReporter with the current context.
+     *
+     * This method must be invoked before any calls to sendEvent() will succeed. This method should
+     * only be invoked at most once.
+     *
+     * @param context a Context object used to initialize this singleton DebugEventReporter in
+     *        the current process.
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public static void initialize(@NonNull Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("DebugEventReporter needs a non-null context.");
+        }
+
+        // Ensure that this context has sufficient permissions to send debug events.
+        context.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
+                "This app does not have privileges to send debug events");
+
+        sContext = context;
+
+        // Check to see if there is a valid debug package; if there are multiple, that's a config
+        // error, so just take the first one.
+        PackageManager pm = sContext.getPackageManager();
+        if (pm == null) return;
+        List<ResolveInfo> packages = pm.queryBroadcastReceivers(
+                new Intent(TelephonyManager.ACTION_DEBUG_EVENT),
+                PackageManager.MATCH_SYSTEM_ONLY
+                        | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+        if (packages == null || packages.isEmpty()) return;
+        if (packages.size() > 1) {
+            Rlog.e(TAG, "Multiple DebugEvent Receivers installed.");
+        }
+
+        for (ResolveInfo r : packages) {
+            if (r.activityInfo == null
+                    || pm.checkPermission(
+                            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                            r.activityInfo.packageName)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Rlog.w(TAG,
+                        "Found package without proper permissions or no activity"
+                                + r.activityInfo.packageName);
+                continue;
+            }
+            Rlog.d(TAG, "Found a valid package " + r.activityInfo.packageName);
+            sDebugPackageName = r.activityInfo.packageName;
+            break;
+        }
+        // Initialization may only be performed once.
+    }
+
+    /** Dump the contents of the DebugEventReporter */
+    public static void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+        if (sContext == null) return;
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        sContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP");
+        pw.println("Initialized=" + (sContext != null ? "Yes" : "No"));
+        pw.println("Debug Package=" + sDebugPackageName);
+        pw.println("Event Counts:");
+        pw.increaseIndent();
+        for (UUID event : sEvents.keySet()) {
+            pw.println(event + ": " + sEvents.get(event));
+        }
+        pw.decreaseIndent();
+        pw.flush();
+    }
+}
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 53d69f4..24db438 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -26,11 +26,12 @@
 import android.content.pm.UserInfo;
 import android.location.LocationManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Process;
-import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
+import android.widget.Toast;
 
 import java.util.List;
 
@@ -41,59 +42,234 @@
 public final class LocationAccessPolicy {
     private static final String TAG = "LocationAccessPolicy";
     private static final boolean DBG = false;
+    public static final int MAX_SDK_FOR_ANY_ENFORCEMENT = Build.VERSION_CODES.P;
 
-    /**
-     * API to determine if the caller has permissions to get cell location.
-     *
-     * @param pkgName Package name of the application requesting access
-     * @param uid The uid of the package
-     * @param pid The pid of the package
-     * @param throwOnDeniedPermission Whether to throw if the location permission is denied.
-     * @return boolean true or false if permissions is granted
-     */
-    public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
-            int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException {
-        Trace.beginSection("TelephonyLocationCheck");
-        try {
-            // Always allow the phone process and system server to access location. This avoid
-            // breaking legacy code that rely on public-facing APIs to access cell location, and
-            // it doesn't create an info leak risk because the cell location is stored in the phone
-            // process anyway, and the system server already has location access.
-            if (uid == Process.PHONE_UID || uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
-                return true;
-            }
+    public enum LocationPermissionResult {
+        ALLOWED,
+        /**
+         * Indicates that the denial is due to a transient device state
+         * (e.g. app-ops, location master switch)
+         */
+        DENIED_SOFT,
+        /**
+         * Indicates that the denial is due to a misconfigured app (e.g. missing entry in manifest)
+         */
+        DENIED_HARD,
+    }
 
-            // We always require the location permission and also require the
-            // location mode to be on for non-legacy apps. Legacy apps are
-            // required to be in the foreground to at least mitigate the case
-            // where a legacy app the user is not using tracks their location.
-            // Granting ACCESS_FINE_LOCATION to an app automatically grants it
-            // ACCESS_COARSE_LOCATION.
-            if (throwOnDeniedPermission) {
-                context.enforcePermission(Manifest.permission.ACCESS_COARSE_LOCATION,
-                        pid, uid, "canAccessCellLocation");
-            } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
-                    pid, uid) == PackageManager.PERMISSION_DENIED) {
-                if (DBG) Log.w(TAG, "Permission checked failed (" + pid + "," + uid + ")");
-                return false;
-            }
-            final int opCode = AppOpsManager.permissionToOpCode(
-                    Manifest.permission.ACCESS_COARSE_LOCATION);
-            if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
-                    .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
-                if (DBG) Log.w(TAG, "AppOp check failed (" + uid + "," + pkgName + ")");
-                return false;
-            }
-            if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
-                if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
-                return false;
-            }
-            // If the user or profile is current, permission is granted.
-            // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
-            return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
-        } finally {
-            Trace.endSection();
+    public static class LocationPermissionQuery {
+        public final String callingPackage;
+        public final int callingUid;
+        public final int callingPid;
+        public final int minSdkVersionForCoarse;
+        public final int minSdkVersionForFine;
+        public final String method;
+
+        private LocationPermissionQuery(String callingPackage, int callingUid, int callingPid,
+                int minSdkVersionForCoarse, int minSdkVersionForFine, String method) {
+            this.callingPackage = callingPackage;
+            this.callingUid = callingUid;
+            this.callingPid = callingPid;
+            this.minSdkVersionForCoarse = minSdkVersionForCoarse;
+            this.minSdkVersionForFine = minSdkVersionForFine;
+            this.method = method;
         }
+
+        public static class Builder {
+            private String mCallingPackage;
+            private int mCallingUid;
+            private int mCallingPid;
+            private int mMinSdkVersionForCoarse = Integer.MAX_VALUE;
+            private int mMinSdkVersionForFine = Integer.MAX_VALUE;
+            private String mMethod;
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingPackage(String callingPackage) {
+                mCallingPackage = callingPackage;
+                return this;
+            }
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingUid(int callingUid) {
+                mCallingUid = callingUid;
+                return this;
+            }
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingPid(int callingPid) {
+                mCallingPid = callingPid;
+                return this;
+            }
+
+            /**
+             * Apps that target at least this sdk version will be checked for coarse location
+             * permission. Defaults to INT_MAX (which means don't check)
+             */
+            public Builder setMinSdkVersionForCoarse(
+                    int minSdkVersionForCoarse) {
+                mMinSdkVersionForCoarse = minSdkVersionForCoarse;
+                return this;
+            }
+
+            /**
+             * Apps that target at least this sdk version will be checked for fine location
+             * permission. Defaults to INT_MAX (which means don't check)
+             */
+            public Builder setMinSdkVersionForFine(
+                    int minSdkVersionForFine) {
+                mMinSdkVersionForFine = minSdkVersionForFine;
+                return this;
+            }
+
+            /**
+             * Optional, for logging purposes only.
+             */
+            public Builder setMethod(String method) {
+                mMethod = method;
+                return this;
+            }
+
+            public LocationPermissionQuery build() {
+                return new LocationPermissionQuery(mCallingPackage, mCallingUid,
+                        mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine, mMethod);
+            }
+        }
+    }
+
+    private static void logError(Context context, String errorMsg) {
+        Log.e(TAG, errorMsg);
+        try {
+            if (Build.IS_DEBUGGABLE) {
+                Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
+            }
+        } catch (Throwable t) {
+            // whatever, not important
+        }
+    }
+
+    private static LocationPermissionResult appOpsModeToPermissionResult(int appOpsMode) {
+        switch (appOpsMode) {
+            case AppOpsManager.MODE_ALLOWED:
+                return LocationPermissionResult.ALLOWED;
+            case AppOpsManager.MODE_ERRORED:
+                return LocationPermissionResult.DENIED_HARD;
+            default:
+                return LocationPermissionResult.DENIED_SOFT;
+        }
+    }
+
+    private static LocationPermissionResult checkAppLocationPermissionHelper(Context context,
+            LocationPermissionQuery query, String permissionToCheck) {
+        String locationTypeForLog =
+                Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+                        ? "fine" : "coarse";
+
+        // Do the app-ops and the manifest check without any of the allow-overrides first.
+        boolean hasManifestPermission = checkManifestPermission(context, query.callingPid,
+                query.callingUid, permissionToCheck);
+
+        int appOpMode = context.getSystemService(AppOpsManager.class)
+                .noteOpNoThrow(AppOpsManager.permissionToOpCode(permissionToCheck),
+                        query.callingUid, query.callingPackage);
+
+        if (hasManifestPermission && appOpMode == AppOpsManager.MODE_ALLOWED) {
+            // If the app did everything right, return without logging.
+            return LocationPermissionResult.ALLOWED;
+        }
+
+        // If the app has the manifest permission but not the app-op permission, it means that
+        // it's aware of the requirement and the user denied permission explicitly. If we see
+        // this, don't let any of the overrides happen.
+        if (hasManifestPermission) {
+            Log.i(TAG, query.callingPackage + " is aware of " + locationTypeForLog + " but the"
+                    + " app-ops permission is specifically denied.");
+            return appOpsModeToPermissionResult(appOpMode);
+        }
+
+        int minSdkVersion = Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+                ? query.minSdkVersionForFine : query.minSdkVersionForCoarse;
+
+        // If the app fails for some reason, see if it should be allowed to proceed.
+        if (minSdkVersion > MAX_SDK_FOR_ANY_ENFORCEMENT) {
+            String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+                    + " because we're not enforcing API " + query.minSdkVersionForFine + " yet."
+                    + " Please fix this app because it will break in the future. Called from "
+                    + query.method;
+            logError(context, errorMsg);
+            return null;
+        } else if (!isAppAtLeastSdkVersion(context, query.callingPackage, minSdkVersion)) {
+            String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+                    + " because it doesn't target API " + query.minSdkVersionForFine + " yet."
+                    + " Please fix this app. Called from " + query.method;
+            logError(context, errorMsg);
+            return null;
+        } else {
+            // If we're not allowing it due to the above two conditions, this means that the app
+            // did not declare the permission in their manifest.
+            return LocationPermissionResult.DENIED_HARD;
+        }
+    }
+
+    public static LocationPermissionResult checkLocationPermission(
+            Context context, LocationPermissionQuery query) {
+        // Always allow the phone process and system server to access location. This avoid
+        // breaking legacy code that rely on public-facing APIs to access cell location, and
+        // it doesn't create an info leak risk because the cell location is stored in the phone
+        // process anyway, and the system server already has location access.
+        if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+                || query.callingUid == Process.ROOT_UID) {
+            return LocationPermissionResult.ALLOWED;
+        }
+
+        // Check the system-wide requirements. If the location master switch is off or
+        // the app's profile isn't in foreground, return a soft denial.
+        if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid)) {
+            return LocationPermissionResult.DENIED_SOFT;
+        }
+
+        // Do the check for fine, then for coarse.
+        if (query.minSdkVersionForFine < Integer.MAX_VALUE) {
+            LocationPermissionResult resultForFine = checkAppLocationPermissionHelper(
+                    context, query, Manifest.permission.ACCESS_FINE_LOCATION);
+            if (resultForFine != null) {
+                return resultForFine;
+            }
+        }
+
+        if (query.minSdkVersionForCoarse < Integer.MAX_VALUE) {
+            LocationPermissionResult resultForCoarse = checkAppLocationPermissionHelper(
+                    context, query, Manifest.permission.ACCESS_COARSE_LOCATION);
+            if (resultForCoarse != null) {
+                return resultForCoarse;
+            }
+        }
+
+        // At this point, we're out of location checks to do. If the app bypassed all the previous
+        // ones due to the SDK grandfathering schemes, allow it access.
+        return LocationPermissionResult.ALLOWED;
+    }
+
+
+    private static boolean checkManifestPermission(Context context, int pid, int uid,
+            String permissionToCheck) {
+        return context.checkPermission(permissionToCheck, pid, uid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid) {
+        if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
+            if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
+            return false;
+        }
+        // If the user or profile is current, permission is granted.
+        // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+        return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid);
     }
 
     private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
@@ -105,10 +281,10 @@
         return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
     }
 
-    private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
-        return context.checkCallingOrSelfPermission(
-                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                == PackageManager.PERMISSION_GRANTED;
+    private static boolean checkInteractAcrossUsersFull(
+            @NonNull Context context, int pid, int uid) {
+        return checkManifestPermission(context, pid, uid,
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL);
     }
 
     private static boolean isCurrentProfile(@NonNull Context context, int uid) {
@@ -132,4 +308,18 @@
             Binder.restoreCallingIdentity(token);
         }
     }
-}
+
+    private static boolean isAppAtLeastSdkVersion(Context context, String pkgName, int sdkVersion) {
+        try {
+            if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion
+                    >= sdkVersion) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, assume known app (more strict checking)
+            // Note: This case will never happen since checkPackage is
+            // called to verify validity before checking app's version.
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index ceb76b5..6e6d59e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -27,6 +27,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Description of a mobile network registration state
@@ -151,7 +152,7 @@
     private final int[] mAvailableServices;
 
     @Nullable
-    private final CellIdentity mCellIdentity;
+    private CellIdentity mCellIdentity;
 
     @Nullable
     private VoiceSpecificRegistrationStates mVoiceSpecificStates;
@@ -360,7 +361,34 @@
         return 0;
     }
 
-    private static String regStateToString(int regState) {
+    /**
+     * Convert service type to string
+     *
+     * @hide
+     *
+     * @param serviceType The service type
+     * @return The service type in string format
+     */
+    public static String serviceTypeToString(@ServiceType int serviceType) {
+        switch (serviceType) {
+            case SERVICE_TYPE_VOICE: return "VOICE";
+            case SERVICE_TYPE_DATA: return "DATA";
+            case SERVICE_TYPE_SMS: return "SMS";
+            case SERVICE_TYPE_VIDEO: return "VIDEO";
+            case SERVICE_TYPE_EMERGENCY: return "EMERGENCY";
+        }
+        return "Unknown service type " + serviceType;
+    }
+
+    /**
+     * Convert registration state to string
+     *
+     * @hide
+     *
+     * @param regState The registration state
+     * @return The reg state in string
+     */
+    public static String regStateToString(@RegState int regState) {
         switch (regState) {
             case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING";
             case REG_STATE_HOME: return "HOME";
@@ -389,14 +417,17 @@
     public String toString() {
         return new StringBuilder("NetworkRegistrationState{")
                 .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
-                .append("transportType=").append(mTransportType)
+                .append(" transportType=").append(TransportType.toString(mTransportType))
                 .append(" regState=").append(regStateToString(mRegState))
-                .append(" roamingType=").append(mRoamingType)
+                .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType))
                 .append(" accessNetworkTechnology=")
                 .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
                 .append(" rejectCause=").append(mRejectCause)
                 .append(" emergencyEnabled=").append(mEmergencyOnly)
-                .append(" supportedServices=").append(mAvailableServices)
+                .append(" availableServices=").append("[" + (mAvailableServices != null
+                        ? Arrays.stream(mAvailableServices)
+                        .mapToObj(type -> serviceTypeToString(type))
+                        .collect(Collectors.joining(",")) : null) + "]")
                 .append(" cellIdentity=").append(mCellIdentity)
                 .append(" voiceSpecificStates=").append(mVoiceSpecificStates)
                 .append(" dataSpecificStates=").append(mDataSpecificStates)
@@ -490,4 +521,22 @@
             return new NetworkRegistrationState[size];
         }
     };
+
+    /**
+     * @hide
+     */
+    public NetworkRegistrationState sanitizeLocationInfo() {
+        NetworkRegistrationState result = copy();
+        result.mCellIdentity = null;
+        return result;
+    }
+
+    private NetworkRegistrationState copy() {
+        Parcel p = Parcel.obtain();
+        this.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        NetworkRegistrationState result = new NetworkRegistrationState(p);
+        p.recycle();
+        return result;
+    }
 }
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index 2ebfa53..21dad77 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -33,15 +33,17 @@
     public final int maxActiveVoiceCalls;
     public final int maxActiveData;
     public final int max5G;
+    public final boolean validationBeforeSwitchSupported;
     public final List<ModemInfo> logicalModemList;
 
     public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G,
-            List<ModemInfo> logicalModemList) {
+            List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) {
         this.maxActiveVoiceCalls = maxActiveVoiceCalls;
         this.maxActiveData = maxActiveData;
         this.max5G = max5G;
         // Make sure it's not null.
         this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList;
+        this.validationBeforeSwitchSupported = validationBeforeSwitchSupported;
     }
 
     @Override
@@ -55,13 +57,15 @@
         maxActiveVoiceCalls = in.readInt();
         maxActiveData = in.readInt();
         max5G = in.readInt();
+        validationBeforeSwitchSupported = in.readBoolean();
         logicalModemList = new ArrayList<>();
         in.readList(logicalModemList, ModemInfo.class.getClassLoader());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList);
+        return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList,
+                validationBeforeSwitchSupported);
     }
 
     @Override
@@ -79,6 +83,7 @@
         return (maxActiveVoiceCalls == s.maxActiveVoiceCalls
                 && maxActiveData == s.maxActiveData
                 && max5G == s.max5G
+                && validationBeforeSwitchSupported == s.validationBeforeSwitchSupported
                 && logicalModemList.equals(s.logicalModemList));
     }
 
@@ -96,6 +101,7 @@
         dest.writeInt(maxActiveVoiceCalls);
         dest.writeInt(maxActiveData);
         dest.writeInt(max5G);
+        dest.writeBoolean(validationBeforeSwitchSupported);
         dest.writeList(logicalModemList);
     }
 
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index fea1b7b..3ce646c 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -45,7 +45,8 @@
  * <p>
  * Override the methods for the state that you wish to receive updates for, and
  * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_
- * flags to {@link TelephonyManager#listen TelephonyManager.listen()}.
+ * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are
+ * called when the state changes, as well as once on initial registration.
  * <p>
  * Note that access to some telephony information is
  * permission-protected. Your application won't receive updates for protected
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index 59f3e1f..19e1931 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -287,11 +287,11 @@
             return false;
         }
         PreciseCallState other = (PreciseCallState) obj;
-        return (mRingingCallState != other.mRingingCallState &&
-            mForegroundCallState != other.mForegroundCallState &&
-            mBackgroundCallState != other.mBackgroundCallState &&
-            mDisconnectCause != other.mDisconnectCause &&
-            mPreciseDisconnectCause != other.mPreciseDisconnectCause);
+        return (mRingingCallState == other.mRingingCallState
+                && mForegroundCallState == other.mForegroundCallState
+                && mBackgroundCallState == other.mBackgroundCallState
+                && mDisconnectCause == other.mDisconnectCause
+                && mPreciseDisconnectCause == other.mPreciseDisconnectCause);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 0d94c4d..c1786be 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -17,12 +17,15 @@
 package android.telephony;
 
 import android.annotation.UnsupportedAppUsage;
+import android.hardware.radio.V1_0.RadioTechnology;
+import android.hardware.radio.V1_4.CellInfo.Info;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.telephony.RILConstants;
 
+
 /**
  * Object to indicate the phone radio type and access technology.
  *
@@ -33,32 +36,34 @@
     /**
      * TODO: get rid of RAF definition in RadioAccessFamily and
      * use {@link TelephonyManager.NetworkTypeBitMask}
+     * TODO: public definition {@link TelephonyManager.NetworkTypeBitMask} is long.
+     * TODO: Convert from int to long everywhere including HAL definitions.
      */
     // 2G
-    public static final int RAF_UNKNOWN = TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN;
-    public static final int RAF_GSM = TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
-    public static final int RAF_GPRS = TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
-    public static final int RAF_EDGE = TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
-    public static final int RAF_IS95A = TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
-    public static final int RAF_IS95B = TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
-    public static final int RAF_1xRTT = TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
+    public static final int RAF_UNKNOWN = (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN;
+    public static final int RAF_GSM = (int) TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
+    public static final int RAF_GPRS = (int) TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
+    public static final int RAF_EDGE = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
+    public static final int RAF_IS95A = (int) TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+    public static final int RAF_IS95B = (int) TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+    public static final int RAF_1xRTT = (int) TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
     // 3G
-    public static final int RAF_EVDO_0 = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
-    public static final int RAF_EVDO_A = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
-    public static final int RAF_EVDO_B = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
-    public static final int RAF_EHRPD = TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
-    public static final int RAF_HSUPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
-    public static final int RAF_HSDPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
-    public static final int RAF_HSPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
-    public static final int RAF_HSPAP = TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
-    public static final int RAF_UMTS = TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
-    public static final int RAF_TD_SCDMA = TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
+    public static final int RAF_EVDO_0 = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
+    public static final int RAF_EVDO_A = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
+    public static final int RAF_EVDO_B = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
+    public static final int RAF_EHRPD = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
+    public static final int RAF_HSUPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
+    public static final int RAF_HSDPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
+    public static final int RAF_HSPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
+    public static final int RAF_HSPAP = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
+    public static final int RAF_UMTS = (int) TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
+    public static final int RAF_TD_SCDMA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
     // 4G
-    public static final int RAF_LTE = TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
-    public static final int RAF_LTE_CA = TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
+    public static final int RAF_LTE = (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
+    public static final int RAF_LTE_CA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
 
     // 5G
-    public static final int RAF_NR = TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+    public static final int RAF_NR = (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR;
 
     // Grouping of RAFs
     // 2G
@@ -147,20 +152,20 @@
     /**
      * Implement the Parcelable interface.
      */
-    public static final Creator<RadioAccessFamily> CREATOR =
-            new Creator<RadioAccessFamily>() {
+    public static final Creator<android.telephony.RadioAccessFamily> CREATOR =
+            new Creator<android.telephony.RadioAccessFamily>() {
 
         @Override
-        public RadioAccessFamily createFromParcel(Parcel in) {
+        public android.telephony.RadioAccessFamily createFromParcel(Parcel in) {
             int phoneId = in.readInt();
             int radioAccessFamily = in.readInt();
 
-            return new RadioAccessFamily(phoneId, radioAccessFamily);
+            return new android.telephony.RadioAccessFamily(phoneId, radioAccessFamily);
         }
 
         @Override
-        public RadioAccessFamily[] newArray(int size) {
-            return new RadioAccessFamily[size];
+        public android.telephony.RadioAccessFamily[] newArray(int size) {
+            return new android.telephony.RadioAccessFamily[size];
         }
     };
 
@@ -391,76 +396,78 @@
     }
 
     /**
-     * convert RAF from {@link ServiceState.RilRadioTechnology} bitmask to
+     * convert RAF from {@link android.hardware.radio.V1_0.RadioAccessFamily} to
      * {@link TelephonyManager.NetworkTypeBitMask}, the bitmask represented by
-     * {@link TelephonyManager.NetworkType}. Reasons are {@link TelephonyManager.NetworkType} are
-     * public while {@link ServiceState.RilRadioTechnology} are hidden. We
-     * don't want to expose two sets of definition to public.
+     * {@link TelephonyManager.NetworkType}.
      *
-     * @param raf bitmask represented by {@link ServiceState.RilRadioTechnology}
+     * @param raf {@link android.hardware.radio.V1_0.RadioAccessFamily}
      * @return {@link TelephonyManager.NetworkTypeBitMask}
      */
     public static int convertToNetworkTypeBitMask(int raf) {
         int networkTypeRaf = 0;
 
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.GSM) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GPRS)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.GPRS) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EDGE)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EDGE) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
         }
         // convert both IS95A/IS95B to CDMA as network mode doesn't support CDMA
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95A)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.IS95A) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95B)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.IS95B) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.ONE_X_RTT) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_0) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_A) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_B) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EHRPD) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSUPA) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSDPA) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPA)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSPA) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSPAP) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_UMTS)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.UMTS) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.TD_SCDMA) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.LTE) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA)) != 0) {
+        if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.LTE_CA) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
         }
-        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_NR)) != 0) {
+        if ((raf & android.hardware.radio.V1_4.RadioAccessFamily.NR) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_NR;
         }
+        // TODO: need hal definition
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN;
+        }
 
         return (networkTypeRaf == 0) ? TelephonyManager.NETWORK_TYPE_UNKNOWN : networkTypeRaf;
     }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 3317876..a1aee6d 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -36,6 +36,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Contains phone state and service related information.
@@ -887,6 +888,24 @@
     }
 
     /**
+     * Convert roaming type to string
+     *
+     * @param roamingType roaming type
+     * @return The roaming type in string format
+     *
+     * @hide
+     */
+    public static String roamingTypeToString(@RoamingType int roamingType) {
+        switch (roamingType) {
+            case ROAMING_TYPE_NOT_ROAMING: return "NOT_ROAMING";
+            case ROAMING_TYPE_UNKNOWN: return "UNKNOWN";
+            case ROAMING_TYPE_DOMESTIC: return "DOMESTIC";
+            case ROAMING_TYPE_INTERNATIONAL: return "INTERNATIONAL";
+        }
+        return "Unknown roaming type " + roamingType;
+    }
+
+    /**
      * Convert radio technology to String
      *
      * @param rt radioTechnology
@@ -1867,4 +1886,29 @@
                 ? range1
                 : range2;
     }
+
+    /**
+     * Returns a copy of self with location-identifying information removed.
+     * Always clears the NetworkRegistrationState's CellIdentity fields, but if removeCoarseLocation
+     * is true, clears other info as well.
+     * @hide
+     */
+    public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) {
+        ServiceState state = new ServiceState(this);
+        if (state.mNetworkRegistrationStates != null) {
+            state.mNetworkRegistrationStates = state.mNetworkRegistrationStates.stream()
+                    .map(NetworkRegistrationState::sanitizeLocationInfo)
+                    .collect(Collectors.toList());
+        }
+        if (!removeCoarseLocation) return state;
+
+        state.mDataOperatorAlphaLong = null;
+        state.mDataOperatorAlphaShort = null;
+        state.mDataOperatorNumeric = null;
+        state.mVoiceOperatorAlphaLong = null;
+        state.mVoiceOperatorAlphaShort = null;
+        state.mVoiceOperatorNumeric = null;
+
+        return state;
+    }
 }
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 91e24a9..52a2085 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -187,15 +187,7 @@
         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
         String format = (PHONE_TYPE_CDMA == activePhone) ?
                 SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
-        message = createFromPdu(pdu, format);
-
-        if (null == message || null == message.mWrappedSmsMessage) {
-            // decoding pdu failed based on activePhone type, must be other format
-            format = (PHONE_TYPE_CDMA == activePhone) ?
-                    SmsConstants.FORMAT_3GPP : SmsConstants.FORMAT_3GPP2;
-            message = createFromPdu(pdu, format);
-        }
-        return message;
+        return createFromPdu(pdu, format);
     }
 
     /**
@@ -211,11 +203,18 @@
      * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
      */
     public static SmsMessage createFromPdu(byte[] pdu, String format) {
-        SmsMessageBase wrappedMessage;
+        return createFromPdu(pdu, format, true);
+    }
+
+    private static SmsMessage createFromPdu(byte[] pdu, String format,
+            boolean fallbackToOtherFormat) {
         if (pdu == null) {
             Rlog.i(LOG_TAG, "createFromPdu(): pdu is null");
             return null;
         }
+        SmsMessageBase wrappedMessage;
+        String otherFormat = SmsConstants.FORMAT_3GPP2.equals(format) ? SmsConstants.FORMAT_3GPP :
+                SmsConstants.FORMAT_3GPP2;
         if (SmsConstants.FORMAT_3GPP2.equals(format)) {
             wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
         } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
@@ -228,8 +227,12 @@
         if (wrappedMessage != null) {
             return new SmsMessage(wrappedMessage);
         } else {
-            Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null");
-            return null;
+            if (fallbackToOtherFormat) {
+                return createFromPdu(pdu, otherFormat, false);
+            } else {
+                Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null");
+                return null;
+            }
         }
     }
 
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 845d23e..79606eb 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -865,7 +865,8 @@
         }
 
         /**
-         * Callback invoked when there is any change to any SubscriptionInfo. Typically
+         * Callback invoked when there is any change to any SubscriptionInfo, as well as once on
+         * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically
          * this method would invoke {@link #getActiveSubscriptionInfoList}
          */
         public void onSubscriptionsChanged() {
@@ -917,7 +918,9 @@
     /**
      * Register for changes to the list of active {@link SubscriptionInfo} records or to the
      * individual records themselves. When a change occurs the onSubscriptionsChanged method of
-     * the listener will be invoked immediately if there has been a notification.
+     * the listener will be invoked immediately if there has been a notification. The
+     * onSubscriptionChanged method will also be triggered once initially when calling this
+     * function.
      *
      * @param listener an instance of {@link OnSubscriptionsChangedListener} with
      *                 onSubscriptionsChanged overridden.
@@ -2048,6 +2051,8 @@
             putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]);
         } else {
             logd("putPhoneIdAndSubIdExtra: no valid subs");
+            intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
+            intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
         }
     }
 
@@ -2860,6 +2865,95 @@
         }
     }
 
+    /**
+     * Enabled or disable a subscription. This is currently used in the settings page.
+     *
+     * <p>
+     * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+     *
+     * @param enable whether user is turning it on or off.
+     * @param subscriptionId Subscription to be enabled or disabled.
+     *                       It could be a eSIM or pSIM subscription.
+     *
+     * @return whether the operation is successful.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean setSubscriptionEnabled(int subscriptionId, boolean enable) {
+        if (VDBG) {
+            logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable);
+        }
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                return iSub.setSubscriptionEnabled(enable, subscriptionId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns whether the subscription is enabled or not. This is different from activated
+     * or deactivated for two aspects. 1) For when user disables a physical subscription, we
+     * actually disable the modem because we can't switch off the subscription. 2) For eSIM,
+     * user may enable one subscription but the system may activate another temporarily. In this
+     * case, user enabled one is different from current active one.
+
+     * @param subscriptionId The subscription it asks about.
+     * @return whether it's enabled or not. {@code true} if user set this subscription enabled
+     * earlier, or user never set subscription enable / disable on this slot explicitly, and
+     * this subscription is currently active. Otherwise, it returns {@code false}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isSubscriptionEnabled(int subscriptionId) {
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                return iSub.isSubscriptionEnabled(subscriptionId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return false;
+    }
+
+    /**
+     * Get which subscription is enabled on this slot. See {@link #isSubscriptionEnabled(int)}
+     * for more details.
+     *
+     * @param slotIndex which slot it asks about.
+     * @return which subscription is enabled on this slot. If there's no enabled subscription
+     *         in this slot, it will return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public int getEnabledSubscriptionId(int slotIndex) {
+        int subId = INVALID_SUBSCRIPTION_ID;
+
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                subId = iSub.getEnabledSubscriptionId(slotIndex);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        if (VDBG) logd("getEnabledSubscriptionId, subId = " + subId);
+        return subId;
+    }
+
     private interface CallISubMethodHelper {
         int callMethod(ISub iSub) throws RemoteException;
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 292ff92..1433b2a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -23,6 +23,7 @@
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -71,6 +72,7 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
@@ -1350,6 +1352,38 @@
     @SystemApi
     public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000;
 
+    /**
+     * Intent sent when an error occurs that debug tools should log and possibly take further
+     * action such as capturing vendor-specific logs.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final String ACTION_DEBUG_EVENT = "android.telephony.action.DEBUG_EVENT";
+
+    /**
+     * An arbitrary ParcelUuid which should be consistent for each occurrence of the same event.
+     *
+     * This field must be included in all events.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_DEBUG_EVENT_ID = "android.telephony.extra.DEBUG_EVENT_ID";
+
+    /**
+     * A freeform string description of the event.
+     *
+     * This field is optional for all events and as a guideline should not exceed 80 characters
+     * and should be as short as possible to convey the essence of the event.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_DEBUG_EVENT_DESCRIPTION =
+            "android.telephony.extra.DEBUG_EVENT_DESCRIPTION";
+
     //
     //
     // Device Info
@@ -1630,10 +1664,7 @@
      * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.ACCESS_COARSE_LOCATION,
-            android.Manifest.permission.ACCESS_FINE_LOCATION
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public CellLocation getCellLocation() {
         try {
             ITelephony telephony = getITelephony();
@@ -3229,6 +3260,35 @@
         }
     }
 
+    /**
+     * Get the mapping from logical slots to physical slots. The mapping represent by a pair list.
+     * The key of the piar is the logical slot id and the value of the pair is the physical
+     * slots id mapped to this logical slot id.
+     *
+     * @return an pair list indicates the mapping from logical slots to physical slots. The size of
+     * the list should be {@link #getPhoneCount()} if success, otherwise return an empty list.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @NonNull
+    public List<Pair<Integer, Integer>> getLogicalToPhysicalSlotMapping() {
+        List<Pair<Integer, Integer>> slotMapping = new ArrayList<>();
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                int[] slotMappingArray = telephony.getSlotsMapping();
+                for (int i = 0; i < slotMappingArray.length; i++) {
+                    slotMapping.add(new Pair(i, slotMappingArray[i]));
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "getSlotsMapping RemoteException", e);
+        }
+        return slotMapping;
+    }
+
     //
     //
     // Subscriber Info
@@ -4844,12 +4904,13 @@
      *
      * <p>Apps targeting {@link android.os.Build.VERSION_CODES#Q Android Q} or higher will no
      * longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps
-     * will receive the latest cached results. Apps targeting
+     * will receive the latest cached results, which may not be current. Apps targeting
      * {@link android.os.Build.VERSION_CODES#Q Android Q} or higher that wish to request updated
      * CellInfo should call
-     * {android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()} and
-     * listen for responses via {@link android.telephony.PhoneStateListener#onCellInfoChanged
-     * onCellInfoChanged()}.
+     * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()};
+     * however, in all cases, updates will be rate-limited and are not guaranteed. To determine the
+     * recency of CellInfo data, callers should check
+     * {@link android.telephony.CellInfo#getTimeStamp CellInfo#getTimeStamp()}.
      *
      * <p>This method returns valid data for devices with
      * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. In cases
@@ -4864,7 +4925,7 @@
      * @return List of {@link android.telephony.CellInfo}; null if cell
      * information is unavailable.
      */
-    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public List<CellInfo> getAllCellInfo() {
         try {
             ITelephony telephony = getITelephony();
@@ -4941,7 +5002,7 @@
      * @param executor the executor on which callback will be invoked.
      * @param callback a callback to receive CellInfo.
      */
-    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public void requestCellInfoUpdate(
             @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
         try {
@@ -4980,7 +5041,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_COARSE_LOCATION,
+    @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION,
             android.Manifest.permission.MODIFY_PHONE_STATE})
     public void requestCellInfoUpdate(@NonNull WorkSource workSource,
             @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
@@ -6507,6 +6568,37 @@
     }
 
     /**
+     * Get the preferred network type bitmap.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return The bitmap of preferred network types.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    public @NetworkTypeBitMask long getPreferredNetworkTypeBitmap() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return (long) RadioAccessFamily.getRafFromNetworkType(
+                        telephony.getPreferredNetworkType(getSubId()));
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getPreferredNetworkTypeBitmap RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getPreferredNetworkTypeBitmap NPE", ex);
+        }
+        return 0;
+    }
+
+    /**
      * Sets the network selection mode to automatic.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
@@ -6539,9 +6631,10 @@
      *
      * <p> Note that this scan can take a long time (sometimes minutes) to happen.
      *
-     * <p>Requires Permission:
+     * <p>Requires Permissions:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier
      * privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
      *
      * @return {@link CellNetworkScanResult} with the status
      * {@link CellNetworkScanResult#STATUS_SUCCESS} and a list of
@@ -6550,12 +6643,15 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
     public CellNetworkScanResult getAvailableNetworks() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getCellNetworkScanResults(getSubId());
+                return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName());
             }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getAvailableNetworks RemoteException", ex);
@@ -6574,7 +6670,8 @@
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
-     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
      *
      * @param request Contains all the RAT with bands/channels that need to be scanned.
      * @param executor The executor through which the callback should be invoked. Since the scan
@@ -6585,7 +6682,10 @@
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public NetworkScan requestNetworkScan(
             NetworkScanRequest request, Executor executor,
             TelephonyScanManager.NetworkScanCallback callback) {
@@ -6594,7 +6694,8 @@
                 mTelephonyScanManager = new TelephonyScanManager();
             }
         }
-        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback);
+        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
+                getOpPackageName());
     }
 
     /**
@@ -6604,7 +6705,10 @@
      * @removed
      */
     @Deprecated
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public NetworkScan requestNetworkScan(
         NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
         return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
@@ -6721,6 +6825,38 @@
     }
 
     /**
+     * Set the preferred network type bitmap.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param networkTypeBitmap The bitmap of preferred network types.
+     * @return true on success; false on any failure.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public boolean setPreferredNetworkTypeBitmap(@NetworkTypeBitMask long networkTypeBitmap) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.setPreferredNetworkType(
+                        getSubId(), RadioAccessFamily.getNetworkTypeFromRaf(
+                                (int) networkTypeBitmap));
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setPreferredNetworkType NPE", ex);
+        }
+        return false;
+    }
+
+    /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -8572,10 +8708,14 @@
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
-     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
     public ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
     }
@@ -9577,10 +9717,10 @@
      *
      * <p>
      * Requires Permission:
-     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isOpportunisticNetworkEnabled() {
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         boolean isEnabled = false;
@@ -9599,7 +9739,7 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, prefix = {"NETWORK_TYPE_BITMASK_"},
+    @LongDef(flag = true, prefix = {"NETWORK_TYPE_BITMASK_"},
             value = {NETWORK_TYPE_BITMASK_UNKNOWN,
                     NETWORK_TYPE_BITMASK_GSM,
                     NETWORK_TYPE_BITMASK_GPRS,
@@ -9628,118 +9768,125 @@
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_UNKNOWN = (1 << NETWORK_TYPE_UNKNOWN);
+    public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L;
     /**
      * network type bitmask indicating the support of radio tech GSM.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_GSM = (1 << NETWORK_TYPE_GSM);
+    public static final long NETWORK_TYPE_BITMASK_GSM = (1 << (NETWORK_TYPE_GSM -1));
     /**
      * network type bitmask indicating the support of radio tech GPRS.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_GPRS = (1 << NETWORK_TYPE_GPRS);
+    public static final long NETWORK_TYPE_BITMASK_GPRS = (1 << (NETWORK_TYPE_GPRS -1));
     /**
      * network type bitmask indicating the support of radio tech EDGE.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_EDGE = (1 << NETWORK_TYPE_EDGE);
+    public static final long NETWORK_TYPE_BITMASK_EDGE = (1 << (NETWORK_TYPE_EDGE -1));
     /**
      * network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B).
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_CDMA = (1 << NETWORK_TYPE_CDMA);
+    public static final long NETWORK_TYPE_BITMASK_CDMA = (1 << (NETWORK_TYPE_CDMA -1));
     /**
      * network type bitmask indicating the support of radio tech 1xRTT.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_1xRTT = (1 << NETWORK_TYPE_1xRTT);
+    public static final long NETWORK_TYPE_BITMASK_1xRTT = (1 << (NETWORK_TYPE_1xRTT - 1));
     // 3G
     /**
      * network type bitmask indicating the support of radio tech EVDO 0.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_EVDO_0 = (1 << NETWORK_TYPE_EVDO_0);
+    public static final long NETWORK_TYPE_BITMASK_EVDO_0 = (1 << (NETWORK_TYPE_EVDO_0 -1));
     /**
      * network type bitmask indicating the support of radio tech EVDO A.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_EVDO_A = (1 << NETWORK_TYPE_EVDO_A);
+    public static final long NETWORK_TYPE_BITMASK_EVDO_A = (1 << (NETWORK_TYPE_EVDO_A - 1));
     /**
      * network type bitmask indicating the support of radio tech EVDO B.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_EVDO_B = (1 << NETWORK_TYPE_EVDO_B);
+    public static final long NETWORK_TYPE_BITMASK_EVDO_B = (1 << (NETWORK_TYPE_EVDO_B -1));
     /**
      * network type bitmask indicating the support of radio tech EHRPD.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_EHRPD = (1 << NETWORK_TYPE_EHRPD);
+    public static final long NETWORK_TYPE_BITMASK_EHRPD = (1 << (NETWORK_TYPE_EHRPD -1));
     /**
      * network type bitmask indicating the support of radio tech HSUPA.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_HSUPA = (1 << NETWORK_TYPE_HSUPA);
+    public static final long NETWORK_TYPE_BITMASK_HSUPA = (1 << (NETWORK_TYPE_HSUPA -1));
     /**
      * network type bitmask indicating the support of radio tech HSDPA.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_HSDPA = (1 << NETWORK_TYPE_HSDPA);
+    public static final long NETWORK_TYPE_BITMASK_HSDPA = (1 << (NETWORK_TYPE_HSDPA -1));
     /**
      * network type bitmask indicating the support of radio tech HSPA.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_HSPA = (1 << NETWORK_TYPE_HSPA);
+    public static final long NETWORK_TYPE_BITMASK_HSPA = (1 << (NETWORK_TYPE_HSPA -1));
     /**
      * network type bitmask indicating the support of radio tech HSPAP.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_HSPAP = (1 << NETWORK_TYPE_HSPAP);
+    public static final long NETWORK_TYPE_BITMASK_HSPAP = (1 << (NETWORK_TYPE_HSPAP -1));
     /**
      * network type bitmask indicating the support of radio tech UMTS.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_UMTS = (1 << NETWORK_TYPE_UMTS);
+    public static final long NETWORK_TYPE_BITMASK_UMTS = (1 << (NETWORK_TYPE_UMTS -1));
     /**
      * network type bitmask indicating the support of radio tech TD_SCDMA.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << NETWORK_TYPE_TD_SCDMA);
+    public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << (NETWORK_TYPE_TD_SCDMA -1));
     // 4G
     /**
      * network type bitmask indicating the support of radio tech LTE.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_LTE = (1 << NETWORK_TYPE_LTE);
+    public static final long NETWORK_TYPE_BITMASK_LTE = (1 << (NETWORK_TYPE_LTE -1));
     /**
      * network type bitmask indicating the support of radio tech LTE CA (carrier aggregation).
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_LTE_CA = (1 << NETWORK_TYPE_LTE_CA);
+    public static final long NETWORK_TYPE_BITMASK_LTE_CA = (1 << (NETWORK_TYPE_LTE_CA -1));
 
     /**
      * network type bitmask indicating the support of radio tech NR(New Radio) 5G.
      * @hide
      */
     @SystemApi
-    public static final int NETWORK_TYPE_BITMASK_NR = (1 << NETWORK_TYPE_NR);
+    public static final long NETWORK_TYPE_BITMASK_NR = (1 << (NETWORK_TYPE_NR -1));
+
+    /**
+     * network type bitmask indicating the support of radio tech IWLAN.
+     * @hide
+     */
+    @SystemApi
+    public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1));
 
     /**
      * @return Modem supported radio access family bitmask
@@ -9750,11 +9897,11 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public @NetworkTypeBitMask int getSupportedRadioAccessFamily() {
+    public @NetworkTypeBitMask long getSupportedRadioAccessFamily() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
+                return (long) telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
             } else {
                 // This can happen when the ITelephony interface is not up yet.
                 return NETWORK_TYPE_BITMASK_UNKNOWN;
@@ -9961,12 +10108,17 @@
      * Get preferred opportunistic data subscription Id
      *
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}),
-     * or has permission {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}.
+     * or has either READ_PRIVILEGED_PHONE_STATE
+     * or {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission.
      * @return subId preferred opportunistic subscription id or
      * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
      * subscription id
      *
      */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
     public int getPreferredOpportunisticDataSubscription() {
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -10138,11 +10290,14 @@
 
     /**
      * Get whether reboot is required or not after making changes to modem configurations.
-     * @Return {@code True} if reboot is required after making changes to modem configurations,
-     * otherwise return {@code False}.
+     * The modem configuration change refers to switching from single SIM configuration to DSDS
+     * or the other way around.
+     * @Return {@code true} if reboot is required after making changes to modem configurations,
+     * otherwise return {@code false}.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isRebootRequiredForModemConfigChange() {
         try {
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 96ff332..91f74b8 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -29,14 +29,14 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.util.Log;
 import android.util.SparseArray;
+
+import com.android.internal.telephony.ITelephony;
+
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Executor;
 
-import com.android.internal.telephony.ITelephony;
-
 /**
  * Manages the radio access network scan requests and callbacks.
  */
@@ -183,6 +183,7 @@
      *
      * <p>
      * Requires Permission:
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
      *
@@ -192,11 +193,13 @@
      * @hide
      */
     public NetworkScan requestNetworkScan(int subId,
-            NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
+            NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
+            String callingPackage) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder());
+                int scanId = telephony.requestNetworkScan(
+                        subId, request, mMessenger, new Binder(), callingPackage);
                 saveScanInfo(scanId, request, executor, callback);
                 return new NetworkScan(scanId, subId);
             }
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
index 709b3aa..d4a78ff 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
@@ -15,42 +15,70 @@
  */
 package android.telephony.ims;
 
-import android.os.Parcel;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
 
 /**
  * Rcs1To1Thread represents a single RCS conversation thread with a total of two
- * {@link RcsParticipant}s.
- * @hide - TODO(sahinc) make this public
+ * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal
+ * Profile Service Definition Document)
  */
 public class Rcs1To1Thread extends RcsThread {
+    private int mThreadId;
+
+    /**
+     * Public constructor only for RcsMessageStoreController to initialize new threads.
+     *
+     * @hide
+     */
     public Rcs1To1Thread(int threadId) {
         super(threadId);
+        mThreadId = threadId;
     }
 
-    public static final Creator<Rcs1To1Thread> CREATOR = new Creator<Rcs1To1Thread>() {
-        @Override
-        public Rcs1To1Thread createFromParcel(Parcel in) {
-            return new Rcs1To1Thread(in);
-        }
-
-        @Override
-        public Rcs1To1Thread[] newArray(int size) {
-            return new Rcs1To1Thread[size];
-        }
-    };
-
-    protected Rcs1To1Thread(Parcel in) {
-        super(in);
-    }
-
+    /**
+     * @return Returns {@code false} as this is always a 1 to 1 thread.
+     */
     @Override
-    public int describeContents() {
-        return 0;
+    public boolean isGroup() {
+        return false;
     }
 
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(RCS_1_TO_1_TYPE);
-        super.writeToParcel(dest, flags);
+    /**
+     * {@link Rcs1To1Thread}s can fall back to SMS as a back-up protocol. This function returns the
+     * thread id to be used to query {@code content://mms-sms/conversation/#} to get the fallback
+     * thread.
+     *
+     * @return The thread id to be used to query the mms-sms authority
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public long getFallbackThreadId() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadFallbackThreadId(mThreadId));
+    }
+
+    /**
+     * If the RCS client allows falling back to SMS, it needs to create an MMS-SMS thread in the
+     * SMS/MMS Provider( see {@link android.provider.Telephony.MmsSms#CONTENT_CONVERSATIONS_URI}.
+     * Use this function to link the {@link Rcs1To1Thread} to the MMS-SMS thread. This function
+     * also updates the storage.
+     *
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setFallbackThreadId(long fallbackThreadId) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.set1To1ThreadFallbackThreadId(mThreadId, fallbackThreadId));
+    }
+
+    /**
+     * @return Returns the {@link RcsParticipant} that receives the messages sent in this thread.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @NonNull
+    @WorkerThread
+    public RcsParticipant getRecipient() throws RcsMessageStoreException {
+        return new RcsParticipant(
+                RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadOtherParticipantId(mThreadId)));
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java
new file mode 100644
index 0000000..5512c4c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsControllerCall.java
@@ -0,0 +1,64 @@
+/*
+ * 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.telephony.ims;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.ims.aidl.IRcs;
+
+/**
+ * A wrapper class around RPC calls that {@link RcsMessageStore} APIs to minimize boilerplate code.
+ *
+ * @hide - not meant for public use
+ */
+class RcsControllerCall {
+    static <R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException {
+        IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE));
+        if (iRcs == null) {
+            throw new RcsMessageStoreException("Could not connect to RCS storage service");
+        }
+
+        try {
+            return serviceCall.methodOnIRcs(iRcs);
+        } catch (RemoteException exception) {
+            throw new RcsMessageStoreException(exception.getMessage());
+        }
+    }
+
+    static void callWithNoReturn(RcsServiceCallWithNoReturn serviceCall)
+            throws RcsMessageStoreException {
+        IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE));
+        if (iRcs == null) {
+            throw new RcsMessageStoreException("Could not connect to RCS storage service");
+        }
+
+        try {
+            serviceCall.methodOnIRcs(iRcs);
+        } catch (RemoteException exception) {
+            throw new RcsMessageStoreException(exception.getMessage());
+        }
+    }
+
+    interface RcsServiceCall<R> {
+        R methodOnIRcs(IRcs iRcs) throws RemoteException;
+    }
+
+    interface RcsServiceCallWithNoReturn {
+        void methodOnIRcs(IRcs iRcs) throws RemoteException;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsPart.aidl b/telephony/java/android/telephony/ims/RcsEvent.aidl
similarity index 96%
rename from telephony/java/android/telephony/ims/RcsPart.aidl
rename to telephony/java/android/telephony/ims/RcsEvent.aidl
index 8b8077d..08974e0 100644
--- a/telephony/java/android/telephony/ims/RcsPart.aidl
+++ b/telephony/java/android/telephony/ims/RcsEvent.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsPart;
+parcelable RcsEvent;
diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java
new file mode 100644
index 0000000..df62277
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsEvent.java
@@ -0,0 +1,61 @@
+/*
+ * 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.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s.
+ */
+public abstract class RcsEvent {
+    /**
+     * @hide
+     */
+    protected final long mTimestamp;
+
+    protected RcsEvent(long timestamp) {
+        mTimestamp = timestamp;
+    }
+
+    /**
+     * @return Returns the time of when this event happened. The timestamp is defined as
+     * milliseconds passed after midnight, January 1, 1970 UTC
+     */
+    public long getTimestamp() {
+        return mTimestamp;
+    }
+
+    /**
+     * Persists the event to the data store
+     *
+     * @hide
+     */
+    abstract void persist() throws RcsMessageStoreException;
+
+    /**
+     * @hide
+     */
+    RcsEvent(Parcel in) {
+        mTimestamp = in.readLong();
+    }
+
+    /**
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mTimestamp);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsEventQueryParams.aidl
index 82d985d..f18c4df 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsEventQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
new file mode 100644
index 0000000..9dbfe43
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
@@ -0,0 +1,319 @@
+/*
+ * 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.telephony.ims;
+
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.ICON_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.NAME_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_LEFT_EVENT_TYPE;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * The parameters to pass into
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a
+ * subset of {@link RcsEvent}s present in the message store.
+ */
+public final class RcsEventQueryParams implements Parcelable {
+    /**
+     * Flag to be used with {@link Builder#setEventType(int)} to make
+     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return all types of
+     * {@link RcsEvent}s
+     */
+    public static final int ALL_EVENTS = -1;
+
+    /**
+     * Flag to be used with {@link Builder#setEventType(int)} to make
+     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return sub-types of
+     * {@link RcsGroupThreadEvent}s
+     */
+    public static final int ALL_GROUP_THREAD_EVENTS = 0;
+
+    /**
+     * Flag to be used with {@link Builder#setEventType(int)} to make
+     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+     * {@link RcsParticipantAliasChangedEvent}s
+     */
+    public static final int PARTICIPANT_ALIAS_CHANGED_EVENT =
+            PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
+
+    /**
+     * Flag to be used with {@link Builder#setEventType(int)} to make
+     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+     * {@link RcsGroupThreadParticipantJoinedEvent}s
+     */
+    public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT =
+            PARTICIPANT_JOINED_EVENT_TYPE;
+
+    /**
+     * Flag to be used with {@link Builder#setEventType(int)} to make
+     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+     * {@link RcsGroupThreadParticipantLeftEvent}s
+     */
+    public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT =
+            PARTICIPANT_LEFT_EVENT_TYPE;
+
+    /**
+     * Flag to be used with {@link Builder#setEventType(int)} to make
+     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+     * {@link RcsGroupThreadNameChangedEvent}s
+     */
+    public static final int GROUP_THREAD_NAME_CHANGED_EVENT = NAME_CHANGED_EVENT_TYPE;
+
+    /**
+     * Flag to be used with {@link Builder#setEventType(int)} to make
+     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+     * {@link RcsGroupThreadIconChangedEvent}s
+     */
+    public static final int GROUP_THREAD_ICON_CHANGED_EVENT = ICON_CHANGED_EVENT_TYPE;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ALL_EVENTS, ALL_GROUP_THREAD_EVENTS, PARTICIPANT_ALIAS_CHANGED_EVENT,
+            GROUP_THREAD_PARTICIPANT_JOINED_EVENT, GROUP_THREAD_PARTICIPANT_LEFT_EVENT,
+            GROUP_THREAD_NAME_CHANGED_EVENT, GROUP_THREAD_ICON_CHANGED_EVENT})
+    public @interface EventType {
+    }
+
+    /**
+     * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
+     * in the order of creation for faster query results.
+     */
+    public static final int SORT_BY_CREATION_ORDER = 0;
+
+    /**
+     * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
+     * with respect to {@link RcsEvent#getTimestamp()}
+     */
+    public static final int SORT_BY_TIMESTAMP = 1;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
+    public @interface SortingProperty {
+    }
+
+    /**
+     * The key to pass into a Bundle, for usage in RcsProvider.query(Bundle)
+     * @hide - not meant for public use
+     */
+    public static final String EVENT_QUERY_PARAMETERS_KEY = "event_query_parameters";
+
+    // Which types of events the results should be limited to
+    private @EventType int mEventType;
+    // The property which the results should be sorted against
+    private int mSortingProperty;
+    // Whether the results should be sorted in ascending order
+    private boolean mIsAscending;
+    // The number of results that should be returned with this query
+    private int mLimit;
+    // The thread that the results are limited to
+    private int mThreadId;
+
+    RcsEventQueryParams(@EventType int eventType, int threadId,
+            @SortingProperty int sortingProperty, boolean isAscending, int limit) {
+        mEventType = eventType;
+        mSortingProperty = sortingProperty;
+        mIsAscending = isAscending;
+        mLimit = limit;
+        mThreadId = threadId;
+    }
+
+    /**
+     * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParams} is
+     * set to query for.
+     */
+    public @EventType int getEventType() {
+        return mEventType;
+    }
+
+    /**
+     * @return Returns the number of {@link RcsEvent}s to be returned from the query. A value of
+     * 0 means there is no set limit.
+     */
+    public int getLimit() {
+        return mLimit;
+    }
+
+    /**
+     * @return Returns the property where the results should be sorted against.
+     * @see SortingProperty
+     */
+    public int getSortingProperty() {
+        return mSortingProperty;
+    }
+
+    /**
+     * @return Returns {@code true} if the result set will be sorted in ascending order,
+     * {@code false} if it will be sorted in descending order.
+     */
+    public boolean getSortDirection() {
+        return mIsAscending;
+    }
+
+    /**
+     * @return Returns the ID of the {@link RcsGroupThread} that the results are limited to. As this
+     * API exposes an ID, it should stay hidden.
+     *
+     * @hide
+     */
+    public int getThreadId() {
+        return mThreadId;
+    }
+
+    /**
+     * A helper class to build the {@link RcsEventQueryParams}.
+     */
+    public static class Builder {
+        private @EventType int mEventType;
+        private @SortingProperty int mSortingProperty;
+        private boolean mIsAscending;
+        private int mLimit = 100;
+        private int mThreadId;
+
+        /**
+         * Creates a new builder for {@link RcsEventQueryParams} to be used in
+         * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
+         */
+        public Builder() {
+            // empty implementation
+        }
+
+        /**
+         * Desired number of events to be returned from the query. Passing in 0 will return all
+         * existing events at once. The limit defaults to 100.
+         *
+         * @param limit The number to limit the query result to.
+         * @return The same instance of the builder to chain parameters.
+         * @throws InvalidParameterException If the given limit is negative.
+         */
+        @CheckResult
+        public Builder setResultLimit(@IntRange(from = 0) int limit)
+                throws InvalidParameterException {
+            if (limit < 0) {
+                throw new InvalidParameterException("The query limit must be non-negative");
+            }
+
+            mLimit = limit;
+            return this;
+        }
+
+        /**
+         * Sets the type of events to be returned from the query.
+         *
+         * @param eventType The type of event to be returned.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setEventType(@EventType int eventType) {
+            mEventType = eventType;
+            return this;
+        }
+
+        /**
+         * Sets the property where the results should be sorted against. Defaults to
+         * {@link RcsEventQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
+         *
+         * @param sortingProperty against which property the results should be sorted
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setSortProperty(@SortingProperty int sortingProperty) {
+            mSortingProperty = sortingProperty;
+            return this;
+        }
+
+        /**
+         * Sets whether the results should be sorted ascending or descending
+         *
+         * @param isAscending whether the results should be sorted ascending
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setSortDirection(boolean isAscending) {
+            mIsAscending = isAscending;
+            return this;
+        }
+
+        /**
+         * Limits the results to the given {@link RcsGroupThread}. Setting this value prevents
+         * returning any instances of {@link RcsParticipantAliasChangedEvent}.
+         *
+         * @param groupThread The thread to limit the results to.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setGroupThread(@NonNull RcsGroupThread groupThread) {
+            mThreadId = groupThread.getThreadId();
+            return this;
+        }
+
+        /**
+         * Builds the {@link RcsEventQueryParams} to use in
+         * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
+         *
+         * @return An instance of {@link RcsEventQueryParams} to use with the event query.
+         */
+        public RcsEventQueryParams build() {
+            return new RcsEventQueryParams(mEventType, mThreadId, mSortingProperty,
+                    mIsAscending, mLimit);
+        }
+    }
+
+    private RcsEventQueryParams(Parcel in) {
+        mEventType = in.readInt();
+        mThreadId = in.readInt();
+        mSortingProperty = in.readInt();
+        mIsAscending = in.readBoolean();
+        mLimit = in.readInt();
+    }
+
+    public static final Creator<RcsEventQueryParams> CREATOR =
+            new Creator<RcsEventQueryParams>() {
+                @Override
+                public RcsEventQueryParams createFromParcel(Parcel in) {
+                    return new RcsEventQueryParams(in);
+                }
+
+                @Override
+                public RcsEventQueryParams[] newArray(int size) {
+                    return new RcsEventQueryParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mEventType);
+        dest.writeInt(mThreadId);
+        dest.writeInt(mSortingProperty);
+        dest.writeBoolean(mIsAscending);
+        dest.writeInt(mLimit);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsEventQueryResult.aidl
index 82d985d..7d13335 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsEventQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
new file mode 100644
index 0000000..c30e4cc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
@@ -0,0 +1,89 @@
+/*
+ * 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.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
+ * call. This class allows getting the token for querying the next batch of events in order to
+ * prevent handling large amounts of data at once.
+ */
+public final class RcsEventQueryResult implements Parcelable {
+    private RcsQueryContinuationToken mContinuationToken;
+    private List<RcsEvent> mEvents;
+
+    /**
+     * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+     * to create query results
+     *
+     * @hide
+     */
+    public RcsEventQueryResult(
+            RcsQueryContinuationToken continuationToken,
+            List<RcsEvent> events) {
+        mContinuationToken = continuationToken;
+        mEvents = events;
+    }
+
+    /**
+     * Returns a token to call
+     * {@link RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)}
+     * to get the next batch of {@link RcsEvent}s.
+     */
+    public RcsQueryContinuationToken getContinuationToken() {
+        return mContinuationToken;
+    }
+
+    /**
+     * Returns all the {@link RcsEvent}s in the current query result. Call {@link
+     * RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)} to get the next batch
+     * of {@link RcsEvent}s.
+     */
+    public List<RcsEvent> getEvents() {
+        return mEvents;
+    }
+
+    private RcsEventQueryResult(Parcel in) {
+        mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader());
+    }
+
+    public static final Creator<RcsEventQueryResult> CREATOR = new Creator<RcsEventQueryResult>() {
+        @Override
+        public RcsEventQueryResult createFromParcel(Parcel in) {
+            return new RcsEventQueryResult(in);
+        }
+
+        @Override
+        public RcsEventQueryResult[] newArray(int size) {
+            return new RcsEventQueryResult[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mContinuationToken, flags);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl
index 82d985d..1552190 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsFileTransferCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
new file mode 100644
index 0000000..14af8ea
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
@@ -0,0 +1,358 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Pass an instance of this class to
+ * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an
+ * {@link RcsFileTransferPart} and save it into storage.
+ */
+public final class RcsFileTransferCreationParams implements Parcelable {
+    private String mRcsFileTransferSessionId;
+    private Uri mContentUri;
+    private String mContentMimeType;
+    private long mFileSize;
+    private long mTransferOffset;
+    private int mWidth;
+    private int mHeight;
+    private long mMediaDuration;
+    private Uri mPreviewUri;
+    private String mPreviewMimeType;
+    private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
+
+    /**
+     * @return Returns the globally unique RCS file transfer session ID for the
+     * {@link RcsFileTransferPart} to be created
+     */
+    public String getRcsFileTransferSessionId() {
+        return mRcsFileTransferSessionId;
+    }
+
+    /**
+     * @return Returns the URI for the content of the {@link RcsFileTransferPart} to be created
+     */
+    public Uri getContentUri() {
+        return mContentUri;
+    }
+
+    /**
+     * @return Returns the MIME type for the content of the {@link RcsFileTransferPart} to be
+     * created
+     */
+    public String getContentMimeType() {
+        return mContentMimeType;
+    }
+
+    /**
+     * @return Returns the file size in bytes for the {@link RcsFileTransferPart} to be created
+     */
+    public long getFileSize() {
+        return mFileSize;
+    }
+
+    /**
+     * @return Returns the transfer offset for the {@link RcsFileTransferPart} to be created. The
+     * file transfer offset is defined as how many bytes have been successfully transferred to the
+     * receiver of this file transfer.
+     */
+    public long getTransferOffset() {
+        return mTransferOffset;
+    }
+
+    /**
+     * @return Returns the width of the {@link RcsFileTransferPart} to be created. The value is in
+     * pixels.
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * @return Returns the height of the {@link RcsFileTransferPart} to be created. The value is in
+     * pixels.
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * @return Returns the duration of the {@link RcsFileTransferPart} to be created.
+     */
+    public long getMediaDuration() {
+        return mMediaDuration;
+    }
+
+    /**
+     * @return Returns the URI of the preview of the content of the {@link RcsFileTransferPart} to
+     * be created. This should only be used for multi-media files.
+     */
+    public Uri getPreviewUri() {
+        return mPreviewUri;
+    }
+
+    /**
+     * @return Returns the MIME type of the preview of the content of the
+     * {@link RcsFileTransferPart} to be created. This should only be used for multi-media files.
+     */
+    public String getPreviewMimeType() {
+        return mPreviewMimeType;
+    }
+
+    /**
+     * @return Returns the status of the {@link RcsFileTransferPart} to be created.
+     */
+    public @RcsFileTransferPart.RcsFileTransferStatus int getFileTransferStatus() {
+        return mFileTransferStatus;
+    }
+
+    /**
+     * @hide
+     */
+    RcsFileTransferCreationParams(Builder builder) {
+        mRcsFileTransferSessionId = builder.mRcsFileTransferSessionId;
+        mContentUri = builder.mContentUri;
+        mContentMimeType = builder.mContentMimeType;
+        mFileSize = builder.mFileSize;
+        mTransferOffset = builder.mTransferOffset;
+        mWidth = builder.mWidth;
+        mHeight = builder.mHeight;
+        mMediaDuration = builder.mLength;
+        mPreviewUri = builder.mPreviewUri;
+        mPreviewMimeType = builder.mPreviewMimeType;
+        mFileTransferStatus = builder.mFileTransferStatus;
+    }
+
+    /**
+     * A builder to create instances of {@link RcsFileTransferCreationParams}
+     */
+    public class Builder {
+        private String mRcsFileTransferSessionId;
+        private Uri mContentUri;
+        private String mContentMimeType;
+        private long mFileSize;
+        private long mTransferOffset;
+        private int mWidth;
+        private int mHeight;
+        private long mLength;
+        private Uri mPreviewUri;
+        private String mPreviewMimeType;
+        private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
+
+        /**
+         * Sets the globally unique RCS file transfer session ID for the {@link RcsFileTransferPart}
+         * to be created
+         *
+         * @param sessionId The RCS file transfer session ID
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setFileTransferSessionId(String sessionId) {
+            mRcsFileTransferSessionId = sessionId;
+            return this;
+        }
+
+        /**
+         * Sets the URI for the content of the {@link RcsFileTransferPart} to be created
+         *
+         * @param contentUri The URI for the file
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setContentUri(Uri contentUri) {
+            mContentUri = contentUri;
+            return this;
+        }
+
+        /**
+         * Sets the MIME type for the content of the {@link RcsFileTransferPart} to be created
+         *
+         * @param contentType The MIME type of the file
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setContentMimeType(String contentType) {
+            mContentMimeType = contentType;
+            return this;
+        }
+
+        /**
+         * Sets the file size for the {@link RcsFileTransferPart} to be created
+         *
+         * @param size The size of the file in bytes
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setFileSize(long size) {
+            mFileSize = size;
+            return this;
+        }
+
+        /**
+         * Sets the transfer offset for the {@link RcsFileTransferPart} to be created. The file
+         * transfer offset is defined as how many bytes have been successfully transferred to the
+         * receiver of this file transfer.
+         *
+         * @param offset The transfer offset in bytes
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setTransferOffset(long offset) {
+            mTransferOffset = offset;
+            return this;
+        }
+
+        /**
+         * Sets the width of the {@link RcsFileTransferPart} to be created. This should only be used
+         * for multi-media files.
+         *
+         * @param width The width of the multi-media file in pixels.
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setWidth(int width) {
+            mWidth = width;
+            return this;
+        }
+
+        /**
+         * Sets the height of the {@link RcsFileTransferPart} to be created. This should only be
+         * used for multi-media files.
+         *
+         * @param height The height of the multi-media file in pixels.
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setHeight(int height) {
+            mHeight = height;
+            return this;
+        }
+
+        /**
+         * Sets the length of the {@link RcsFileTransferPart} to be created. This should only be
+         * used for multi-media files such as audio or video.
+         *
+         * @param length The length of the multi-media file in milliseconds
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setMediaDuration(long length) {
+            mLength = length;
+            return this;
+        }
+
+        /**
+         * Sets the URI of the preview of the content of the {@link RcsFileTransferPart} to be
+         * created. This should only be used for multi-media files.
+         *
+         * @param previewUri The URI of the preview of the file transfer
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setPreviewUri(Uri previewUri) {
+            mPreviewUri = previewUri;
+            return this;
+        }
+
+        /**
+         * Sets the MIME type of the preview of the content of the {@link RcsFileTransferPart} to
+         * be created. This should only be used for multi-media files.
+         *
+         * @param previewType The MIME type of the preview of the file transfer
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setPreviewMimeType(String previewType) {
+            mPreviewMimeType = previewType;
+            return this;
+        }
+
+        /**
+         * Sets the status of the {@link RcsFileTransferPart} to be created.
+         *
+         * @param status The status of the file transfer
+         * @return The same instance of {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setFileTransferStatus(
+                @RcsFileTransferPart.RcsFileTransferStatus int status) {
+            mFileTransferStatus = status;
+            return this;
+        }
+
+        /**
+         * Creates an instance of {@link RcsFileTransferCreationParams} with the given
+         * parameters.
+         *
+         * @return The same instance of {@link Builder} to chain methods
+         * @see RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)
+         */
+        public RcsFileTransferCreationParams build() {
+            return new RcsFileTransferCreationParams(this);
+        }
+    }
+
+    private RcsFileTransferCreationParams(Parcel in) {
+        mRcsFileTransferSessionId = in.readString();
+        mContentUri = in.readParcelable(Uri.class.getClassLoader());
+        mContentMimeType = in.readString();
+        mFileSize = in.readLong();
+        mTransferOffset = in.readLong();
+        mWidth = in.readInt();
+        mHeight = in.readInt();
+        mMediaDuration = in.readLong();
+        mPreviewUri = in.readParcelable(Uri.class.getClassLoader());
+        mPreviewMimeType = in.readString();
+        mFileTransferStatus = in.readInt();
+    }
+
+    public static final Creator<RcsFileTransferCreationParams> CREATOR =
+            new Creator<RcsFileTransferCreationParams>() {
+                @Override
+                public RcsFileTransferCreationParams createFromParcel(Parcel in) {
+                    return new RcsFileTransferCreationParams(in);
+                }
+
+                @Override
+                public RcsFileTransferCreationParams[] newArray(int size) {
+                    return new RcsFileTransferCreationParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mRcsFileTransferSessionId);
+        dest.writeParcelable(mContentUri, flags);
+        dest.writeString(mContentMimeType);
+        dest.writeLong(mFileSize);
+        dest.writeLong(mTransferOffset);
+        dest.writeInt(mWidth);
+        dest.writeInt(mHeight);
+        dest.writeLong(mMediaDuration);
+        dest.writeParcelable(mPreviewUri, flags);
+        dest.writeString(mPreviewMimeType);
+        dest.writeInt(mFileTransferStatus);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl b/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
deleted file mode 100644
index eaf3128..0000000
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsFileTransferPart;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
index 39c58dd..9531c2e 100644
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java
+++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
@@ -15,34 +15,344 @@
  */
 package android.telephony.ims;
 
-import android.os.Parcel;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.net.Uri;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
- * A part of a composite {@link RcsMessage} that holds a file transfer.
- * @hide - TODO(sahinc) make this public
+ * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7
+ * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
  */
-public class RcsFileTransferPart extends RcsPart {
-    public static final Creator<RcsFileTransferPart> CREATOR = new Creator<RcsFileTransferPart>() {
-        @Override
-        public RcsFileTransferPart createFromParcel(Parcel in) {
-            return new RcsFileTransferPart(in);
-        }
+public class RcsFileTransferPart {
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} is not set yet.
+     */
+    public static final int NOT_SET = 0;
 
-        @Override
-        public RcsFileTransferPart[] newArray(int size) {
-            return new RcsFileTransferPart[size];
-        }
-    };
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} is a draft and is not in the
+     * process of sending yet.
+     */
+    public static final int DRAFT = 1;
 
-    protected RcsFileTransferPart(Parcel in) {
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} is actively being sent right
+     * now.
+     */
+    public static final int SENDING = 2;
+
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} was being sent, but the user has
+     * paused the sending process.
+     */
+    public static final int SENDING_PAUSED = 3;
+
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
+     * send.
+     */
+    public static final int SENDING_FAILED = 4;
+
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
+     * send.
+     */
+    public static final int SENDING_CANCELLED = 5;
+
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} is actively being downloaded
+     * right now.
+     */
+    public static final int DOWNLOADING = 6;
+
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} was being downloaded, but the
+     * user paused the downloading process.
+     */
+    public static final int DOWNLOADING_PAUSED = 7;
+
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
+     * download.
+     */
+    public static final int DOWNLOADING_FAILED = 8;
+
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
+     * download.
+     */
+    public static final int DOWNLOADING_CANCELLED = 9;
+
+    /**
+     * The status to indicate that this {@link RcsFileTransferPart} was successfully sent or
+     * received.
+     */
+    public static final int SUCCEEDED = 10;
+
+    @IntDef({
+            DRAFT, SENDING, SENDING_PAUSED, SENDING_FAILED, SENDING_CANCELLED, DOWNLOADING,
+            DOWNLOADING_PAUSED, DOWNLOADING_FAILED, DOWNLOADING_CANCELLED, SUCCEEDED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RcsFileTransferStatus {
     }
 
-    @Override
-    public int describeContents() {
-        return 0;
+    private int mId;
+
+    /**
+     * @hide
+     */
+    RcsFileTransferPart(int id) {
+        mId = id;
     }
 
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    /**
+     * @hide
+     */
+    public void setId(int id) {
+        mId = id;
+    }
+
+    /**
+     * @hide
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Sets the RCS file transfer session ID for this file transfer and persists into storage.
+     *
+     * @param sessionId The session ID to be used for this file transfer.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setFileTransferSessionId(String sessionId) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferSessionId(mId, sessionId));
+    }
+
+    /**
+     * @return Returns the file transfer session ID.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public String getFileTransferSessionId() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferSessionId(mId));
+    }
+
+    /**
+     * Sets the content URI for this file transfer and persists into storage. The file transfer
+     * should be reachable using this URI.
+     *
+     * @param contentUri The URI for this file transfer.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setContentUri(Uri contentUri) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferContentUri(mId, contentUri));
+    }
+
+    /**
+     * @return Returns the URI for this file transfer
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @Nullable
+    @WorkerThread
+    public Uri getContentUri() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentUri(mId));
+    }
+
+    /**
+     * Sets the MIME type of this file transfer and persists into storage. Whether this type
+     * actually matches any known or supported types is not checked.
+     *
+     * @param contentMimeType The type of this file transfer.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setContentMimeType(String contentMimeType) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setFileTransferContentType(mId, contentMimeType));
+    }
+
+    /**
+     * @return Returns the content type of this file transfer
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    @Nullable
+    public String getContentMimeType() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentType(mId));
+    }
+
+    /**
+     * Sets the content length (i.e. file size) for this file transfer and persists into storage.
+     *
+     * @param contentLength The content length of this file transfer
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setFileSize(long contentLength) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setFileTransferFileSize(mId, contentLength));
+    }
+
+    /**
+     * @return Returns the content length (i.e. file size) for this file transfer.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public long getFileSize() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferFileSize(mId));
+    }
+
+    /**
+     * Sets the transfer offset for this file transfer and persists into storage. The file transfer
+     * offset is defined as how many bytes have been successfully transferred to the receiver of
+     * this file transfer.
+     *
+     * @param transferOffset The transfer offset for this file transfer.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setTransferOffset(long transferOffset) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setFileTransferTransferOffset(mId, transferOffset));
+    }
+
+    /**
+     * @return Returns the number of bytes that have successfully transferred.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public long getTransferOffset() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferTransferOffset(mId));
+    }
+
+    /**
+     * Sets the status for this file transfer and persists into storage.
+     *
+     * @param status The status of this file transfer.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setFileTransferStatus(@RcsFileTransferStatus int status)
+            throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferStatus(mId, status));
+    }
+
+    /**
+     * @return Returns the status of this file transfer.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public @RcsFileTransferStatus int getFileTransferStatus() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferStatus(mId));
+    }
+
+    /**
+     * @return Returns the width of this multi-media message part in pixels.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public int getWidth() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferWidth(mId));
+    }
+
+    /**
+     * Sets the width of this RCS multi-media message part and persists into storage.
+     *
+     * @param width The width value in pixels
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setWidth(int width) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferWidth(mId, width));
+    }
+
+    /**
+     * @return Returns the height of this multi-media message part in pixels.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public int getHeight() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferHeight(mId));
+    }
+
+    /**
+     * Sets the height of this RCS multi-media message part and persists into storage.
+     *
+     * @param height The height value in pixels
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setHeight(int height) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferHeight(mId, height));
+    }
+
+    /**
+     * @return Returns the length of this multi-media file (e.g. video or audio) in milliseconds.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public long getLength() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferLength(mId));
+    }
+
+    /**
+     * Sets the length of this multi-media file (e.g. video or audio) and persists into storage.
+     *
+     * @param length The length of the file in milliseconds.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setLength(long length) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferLength(mId, length));
+    }
+
+    /**
+     * @return Returns the URI for the preview of this multi-media file (e.g. an image thumbnail for
+     * a video)
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public Uri getPreviewUri() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewUri(mId));
+    }
+
+    /**
+     * Sets the URI for the preview of this multi-media file and persists into storage.
+     *
+     * @param previewUri The URI to access to the preview file.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setPreviewUri(Uri previewUri) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferPreviewUri(mId, previewUri));
+    }
+
+    /**
+     * @return Returns the MIME type of this multi-media file's preview.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public String getPreviewMimeType() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewType(mId));
+    }
+
+    /**
+     * Sets the MIME type for this multi-media file's preview and persists into storage.
+     *
+     * @param previewMimeType The MIME type for the preview
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setPreviewMimeType(String previewMimeType) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setFileTransferPreviewType(mId, previewMimeType));
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.aidl b/telephony/java/android/telephony/ims/RcsGroupThread.aidl
deleted file mode 100644
index c4ce529..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThread.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsGroupThread;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
index d954b2d..6e17bc2 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThread.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.java
@@ -15,38 +15,189 @@
  */
 package android.telephony.ims;
 
-import android.os.Parcel;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.net.Uri;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
 
 /**
  * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
- * or leave.
- * @hide - TODO(sahinc) make this public
+ * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service
+ * Definition Document)
  */
 public class RcsGroupThread extends RcsThread {
-    public static final Creator<RcsGroupThread> CREATOR = new Creator<RcsGroupThread>() {
-        @Override
-        public RcsGroupThread createFromParcel(Parcel in) {
-            return new RcsGroupThread(in);
-        }
-
-        @Override
-        public RcsGroupThread[] newArray(int size) {
-            return new RcsGroupThread[size];
-        }
-    };
-
-    protected RcsGroupThread(Parcel in) {
-        super(in);
+    /**
+     * Public constructor only for RcsMessageStoreController to initialize new threads.
+     *
+     * @hide
+     */
+    public RcsGroupThread(int threadId) {
+        super(threadId);
     }
 
+    /**
+     * @return Returns {@code true} as this is always a group thread
+     */
     @Override
-    public int describeContents() {
-        return 0;
+    public boolean isGroup() {
+        return true;
     }
 
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(RCS_GROUP_TYPE);
-        super.writeToParcel(dest, flags);
+    /**
+     * @return Returns the given name of this {@link RcsGroupThread}. Please see US6-2 - GSMA RCC.71
+     * (RCS Universal Profile Service Definition Document)
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @Nullable
+    @WorkerThread
+    public String getGroupName() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadName(mThreadId));
+    }
+
+    /**
+     * Sets the name of this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
+     * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+     *
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setGroupName(String groupName) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadName(mThreadId, groupName));
+    }
+
+    /**
+     * @return Returns a URI that points to the group's icon {@link RcsGroupThread}. Please see
+     * US6-2 - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @Nullable
+    public Uri getGroupIcon() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadIcon(mThreadId));
+    }
+
+    /**
+     * Sets the icon for this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
+     * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+     *
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setGroupIcon(@Nullable Uri groupIcon) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadIcon(mThreadId, groupIcon));
+    }
+
+    /**
+     * @return Returns the owner of this thread or {@code null} if there doesn't exist an owner
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @Nullable
+    @WorkerThread
+    public RcsParticipant getOwner() throws RcsMessageStoreException {
+        return new RcsParticipant(RcsControllerCall.call(
+                iRcs -> iRcs.getGroupThreadOwner(mThreadId)));
+    }
+
+    /**
+     * Sets the owner of this {@link RcsGroupThread} and saves it into storage. This is intended to
+     * be used for selecting a new owner for a group thread if the owner leaves the thread. The
+     * owner needs to be in the list of existing participants.
+     *
+     * @param participant The new owner of the thread. {@code null} values are allowed.
+     * @throws RcsMessageStoreException if the operation could not be persisted into storage
+     */
+    @WorkerThread
+    public void setOwner(@Nullable RcsParticipant participant) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setGroupThreadOwner(mThreadId, participant.getId()));
+    }
+
+    /**
+     * Adds a new {@link RcsParticipant} to this group thread and persists into storage. If the user
+     * is actively participating in this {@link RcsGroupThread}, an {@link RcsParticipant} on behalf
+     * of them should be added.
+     *
+     * @param participant The new participant to be added to the thread.
+     * @throws RcsMessageStoreException if the operation could not be persisted into storage
+     */
+    @WorkerThread
+    public void addParticipant(@NonNull RcsParticipant participant)
+            throws RcsMessageStoreException {
+        if (participant == null) {
+            return;
+        }
+
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.addParticipantToGroupThread(mThreadId, participant.getId()));
+    }
+
+    /**
+     * Removes an {@link RcsParticipant} from this group thread and persists into storage. If the
+     * removed participant was the owner of this group, the owner will become null.
+     *
+     * @throws RcsMessageStoreException if the operation could not be persisted into storage
+     */
+    @WorkerThread
+    public void removeParticipant(@NonNull RcsParticipant participant)
+            throws RcsMessageStoreException {
+        if (participant == null) {
+            return;
+        }
+
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.removeParticipantFromGroupThread(mThreadId, participant.getId()));
+    }
+
+    /**
+     * Returns the set of {@link RcsParticipant}s that contribute to this group thread. The
+     * returned set does not support modifications, please use
+     * {@link RcsGroupThread#addParticipant(RcsParticipant)}
+     * and {@link RcsGroupThread#removeParticipant(RcsParticipant)} instead.
+     *
+     * @return the immutable set of {@link RcsParticipant} in this group thread.
+     * @throws RcsMessageStoreException if the values could not be read from the storage
+     */
+    @WorkerThread
+    @NonNull
+    public Set<RcsParticipant> getParticipants() throws RcsMessageStoreException {
+        RcsParticipantQueryParams queryParameters =
+                new RcsParticipantQueryParams.Builder().setThread(this).build();
+
+        RcsParticipantQueryResult queryResult = RcsControllerCall.call(
+                iRcs -> iRcs.getParticipants(queryParameters));
+
+        List<RcsParticipant> participantList = queryResult.getParticipants();
+        Set<RcsParticipant> participantSet = new LinkedHashSet<>(participantList);
+        return Collections.unmodifiableSet(participantSet);
+    }
+
+    /**
+     * Returns the conference URI for this {@link RcsGroupThread}. Please see 4.4.5.2 - GSMA RCC.53
+     * (RCS Device API 1.6 Specification
+     *
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @Nullable
+    @WorkerThread
+    public Uri getConferenceUri() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadConferenceUri(mThreadId));
+    }
+
+    /**
+     * Sets the conference URI for this {@link RcsGroupThread} and persists into storage. Please see
+     * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
+     *
+     * @param conferenceUri The URI as String to be used as the conference URI.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @Nullable
+    @WorkerThread
+    public void setConferenceUri(Uri conferenceUri) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setGroupThreadConferenceUri(mThreadId, conferenceUri));
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl
similarity index 95%
rename from telephony/java/android/telephony/ims/RcsThreadEvent.aidl
rename to telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl
index 4a40d89..77a2372 100644
--- a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl
@@ -1,5 +1,4 @@
 /*
- *
  * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,4 +16,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadEvent;
+parcelable RcsGroupThreadEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
new file mode 100644
index 0000000..609b174
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
@@ -0,0 +1,68 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+/**
+ * An event that happened on an {@link RcsGroupThread}.
+ */
+public abstract class RcsGroupThreadEvent extends RcsEvent {
+    private final int mRcsGroupThreadId;
+    private final int mOriginatingParticipantId;
+
+    RcsGroupThreadEvent(long timestamp, int rcsGroupThreadId,
+            int originatingParticipantId) {
+        super(timestamp);
+        mRcsGroupThreadId = rcsGroupThreadId;
+        mOriginatingParticipantId = originatingParticipantId;
+    }
+
+    /**
+     * @return Returns the {@link RcsGroupThread} that this event happened on.
+     */
+    @NonNull
+    public RcsGroupThread getRcsGroupThread() {
+        return new RcsGroupThread(mRcsGroupThreadId);
+    }
+
+    /**
+     * @return Returns the {@link RcsParticipant} that performed the event.
+     */
+    @NonNull
+    public RcsParticipant getOriginatingParticipant() {
+        return new RcsParticipant(mOriginatingParticipantId);
+    }
+
+    /**
+     * @hide
+     */
+    RcsGroupThreadEvent(Parcel in) {
+        super(in);
+        mRcsGroupThreadId = in.readInt();
+        mOriginatingParticipantId = in.readInt();
+    }
+
+    /**
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(mRcsGroupThreadId);
+        dest.writeInt(mOriginatingParticipantId);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl
index 82d985d..daea792 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl
@@ -1,5 +1,4 @@
 /*
- *
  * Copyright 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,4 +16,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsGroupThreadIconChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
new file mode 100644
index 0000000..e768439
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
@@ -0,0 +1,109 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA
+ * RCC.71 (RCS Universal Profile Service Definition Document)
+ */
+public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent implements
+        Parcelable {
+    private final Uri mNewIcon;
+
+    /**
+     * Creates a new {@link RcsGroupThreadIconChangedEvent}. This event is not persisted into
+     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+     *
+     * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+     *                  midnight, January 1st, 1970 UTC
+     * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+     * @param originatingParticipant The {@link RcsParticipant} that changed the
+     *                               {@link RcsGroupThread}'s icon.
+     * @param newIcon {@link Uri} to the new icon of this {@link RcsGroupThread}
+     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+     */
+    public RcsGroupThreadIconChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread,
+            @NonNull RcsParticipant originatingParticipant, @Nullable Uri newIcon) {
+        super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+        mNewIcon = newIcon;
+    }
+
+    /**
+     * @hide - internal constructor for queries
+     */
+    public RcsGroupThreadIconChangedEvent(long timestamp, int rcsGroupThreadId,
+            int originatingParticipantId, @Nullable Uri newIcon) {
+        super(timestamp, rcsGroupThreadId, originatingParticipantId);
+        mNewIcon = newIcon;
+    }
+
+    /**
+     * @return Returns the {@link Uri} to the icon of the {@link RcsGroupThread} after this
+     * {@link RcsGroupThreadIconChangedEvent} occured.
+     */
+    @Nullable
+    public Uri getNewIcon() {
+        return mNewIcon;
+    }
+
+    /**
+     * Persists the event to the data store.
+     *
+     * @hide - not meant for public use.
+     */
+    @Override
+    public void persist() throws RcsMessageStoreException {
+        // TODO ensure failure throws
+        RcsControllerCall.call(iRcs -> iRcs.createGroupThreadIconChangedEvent(
+                getTimestamp(), getRcsGroupThread().getThreadId(),
+                getOriginatingParticipant().getId(), mNewIcon));
+    }
+
+    public static final Creator<RcsGroupThreadIconChangedEvent> CREATOR =
+            new Creator<RcsGroupThreadIconChangedEvent>() {
+                @Override
+                public RcsGroupThreadIconChangedEvent createFromParcel(Parcel in) {
+                    return new RcsGroupThreadIconChangedEvent(in);
+                }
+
+                @Override
+                public RcsGroupThreadIconChangedEvent[] newArray(int size) {
+                    return new RcsGroupThreadIconChangedEvent[size];
+                }
+            };
+
+    private RcsGroupThreadIconChangedEvent(Parcel in) {
+        super(in);
+        mNewIcon = in.readParcelable(Uri.class.getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeParcelable(mNewIcon, flags);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl
index 82d985d..3ed9bd1 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsGroupThreadNameChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
new file mode 100644
index 0000000..02030bc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
@@ -0,0 +1,107 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA
+ * RCC.71 (RCS Universal Profile Service Definition Document)
+ */
+public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent implements
+        Parcelable {
+    private final String mNewName;
+
+    /**
+     * Creates a new {@link RcsGroupThreadNameChangedEvent}. This event is not persisted into
+     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+     *
+     * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+     *                  midnight, January 1st, 1970 UTC
+     * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+     * @param originatingParticipant The {@link RcsParticipant} that changed the
+     *                               {@link RcsGroupThread}'s icon.
+     * @param newName The new name of the {@link RcsGroupThread}
+     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+     */
+    public RcsGroupThreadNameChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread,
+            @NonNull RcsParticipant originatingParticipant, @Nullable String newName) {
+        super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+        mNewName = newName;
+    }
+
+    /**
+     * @hide - internal constructor for queries
+     */
+    public RcsGroupThreadNameChangedEvent(long timestamp, int rcsGroupThreadId,
+            int originatingParticipantId, @Nullable String newName) {
+        super(timestamp, rcsGroupThreadId, originatingParticipantId);
+        mNewName = newName;
+    }
+
+    /**
+     * @return Returns the name of this {@link RcsGroupThread} after this
+     * {@link RcsGroupThreadNameChangedEvent} happened.
+     */
+    @Nullable
+    public String getNewName() {
+        return mNewName;
+    }
+
+    /**
+     * Persists the event to the data store.
+     *
+     * @hide - not meant for public use.
+     */
+    @Override
+    public void persist() throws RcsMessageStoreException {
+        RcsControllerCall.call(iRcs -> iRcs.createGroupThreadNameChangedEvent(
+                getTimestamp(), getRcsGroupThread().getThreadId(),
+                getOriginatingParticipant().getId(), mNewName));
+    }
+
+    public static final Creator<RcsGroupThreadNameChangedEvent> CREATOR =
+            new Creator<RcsGroupThreadNameChangedEvent>() {
+                @Override
+                public RcsGroupThreadNameChangedEvent createFromParcel(Parcel in) {
+                    return new RcsGroupThreadNameChangedEvent(in);
+                }
+
+                @Override
+                public RcsGroupThreadNameChangedEvent[] newArray(int size) {
+                    return new RcsGroupThreadNameChangedEvent[size];
+                }
+            };
+
+    private RcsGroupThreadNameChangedEvent(Parcel in) {
+        super(in);
+        mNewName = in.readString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mNewName);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessage.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl
similarity index 92%
copy from telephony/java/android/telephony/ims/RcsMessage.aidl
copy to telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl
index b32cd12..420abff 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsMessage;
+parcelable RcsGroupThreadParticipantJoinedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
new file mode 100644
index 0000000..0d1a573
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
@@ -0,0 +1,107 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ */
+public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent implements
+        Parcelable {
+    private final int mJoinedParticipantId;
+
+    /**
+     * Creates a new {@link RcsGroupThreadParticipantJoinedEvent}. This event is not persisted into
+     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+     *
+     * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+     *                  midnight, January 1st, 1970 UTC
+     * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+     * @param originatingParticipant The {@link RcsParticipant} that added or invited the new
+     *                               {@link RcsParticipant} into the {@link RcsGroupThread}
+     * @param joinedParticipant The new {@link RcsParticipant} that joined the
+     *                          {@link RcsGroupThread}
+     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+     */
+    public RcsGroupThreadParticipantJoinedEvent(long timestamp,
+            @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
+            @NonNull RcsParticipant joinedParticipant) {
+        super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+        mJoinedParticipantId = joinedParticipant.getId();
+    }
+
+    /**
+     * @hide - internal constructor for queries
+     */
+    public RcsGroupThreadParticipantJoinedEvent(long timestamp, int rcsGroupThreadId,
+            int originatingParticipantId, int joinedParticipantId) {
+        super(timestamp, rcsGroupThreadId, originatingParticipantId);
+        mJoinedParticipantId = joinedParticipantId;
+    }
+
+    /**
+     * @return Returns the {@link RcsParticipant} that joined the associated {@link RcsGroupThread}
+     */
+    public RcsParticipant getJoinedParticipant() {
+        return new RcsParticipant(mJoinedParticipantId);
+    }
+
+    /**
+     * Persists the event to the data store.
+     *
+     * @hide - not meant for public use.
+     */
+    @Override
+    public void persist() throws RcsMessageStoreException {
+        RcsControllerCall.call(
+                iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(),
+                        getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
+                        getJoinedParticipant().getId()));
+    }
+
+    public static final Creator<RcsGroupThreadParticipantJoinedEvent> CREATOR =
+            new Creator<RcsGroupThreadParticipantJoinedEvent>() {
+                @Override
+                public RcsGroupThreadParticipantJoinedEvent createFromParcel(Parcel in) {
+                    return new RcsGroupThreadParticipantJoinedEvent(in);
+                }
+
+                @Override
+                public RcsGroupThreadParticipantJoinedEvent[] newArray(int size) {
+                    return new RcsGroupThreadParticipantJoinedEvent[size];
+                }
+            };
+
+    private RcsGroupThreadParticipantJoinedEvent(Parcel in) {
+        super(in);
+        mJoinedParticipantId = in.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(mJoinedParticipantId);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessage.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl
similarity index 92%
rename from telephony/java/android/telephony/ims/RcsMessage.aidl
rename to telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl
index b32cd12..ff139ac 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsMessage;
+parcelable RcsGroupThreadParticipantLeftEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
new file mode 100644
index 0000000..cd52508
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
@@ -0,0 +1,106 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ */
+public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent implements
+        Parcelable {
+    private final int mLeavingParticipantId;
+
+    /**
+     * Creates a new {@link RcsGroupThreadParticipantLeftEvent}. his event is not persisted into
+     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+     *
+     * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+     *                  midnight, January 1st, 1970 UTC
+     * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+     * @param originatingParticipant The {@link RcsParticipant} that removed the
+     *                               {@link RcsParticipant} from the {@link RcsGroupThread}. It is
+     *                               possible that originatingParticipant and leavingParticipant are
+     *                               the same (i.e. {@link RcsParticipant} left the group
+     *                               themselves)
+     * @param leavingParticipant The {@link RcsParticipant} that left the {@link RcsGroupThread}
+     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+     */
+    public RcsGroupThreadParticipantLeftEvent(long timestamp,
+            @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
+            @NonNull RcsParticipant leavingParticipant) {
+        super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+        mLeavingParticipantId = leavingParticipant.getId();
+    }
+
+    /**
+     * @hide - internal constructor for queries
+     */
+    public RcsGroupThreadParticipantLeftEvent(long timestamp, int rcsGroupThreadId,
+            int originatingParticipantId, int leavingParticipantId) {
+        super(timestamp, rcsGroupThreadId, originatingParticipantId);
+        mLeavingParticipantId = leavingParticipantId;
+    }
+
+    /**
+     * @return Returns the {@link RcsParticipant} that left the associated {@link RcsGroupThread}
+     * after this {@link RcsGroupThreadParticipantLeftEvent} happened.
+     */
+    @NonNull
+    public RcsParticipant getLeavingParticipantId() {
+        return new RcsParticipant(mLeavingParticipantId);
+    }
+
+    @Override
+    public void persist() throws RcsMessageStoreException {
+        RcsControllerCall.call(
+                iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(),
+                        getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
+                        getLeavingParticipantId().getId()));
+    }
+
+    public static final Creator<RcsGroupThreadParticipantLeftEvent> CREATOR =
+            new Creator<RcsGroupThreadParticipantLeftEvent>() {
+                @Override
+                public RcsGroupThreadParticipantLeftEvent createFromParcel(Parcel in) {
+                    return new RcsGroupThreadParticipantLeftEvent(in);
+                }
+
+                @Override
+                public RcsGroupThreadParticipantLeftEvent[] newArray(int size) {
+                    return new RcsGroupThreadParticipantLeftEvent[size];
+                }
+            };
+
+    private RcsGroupThreadParticipantLeftEvent(Parcel in) {
+        super(in);
+        mLeavingParticipantId = in.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(mLeavingParticipantId);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
deleted file mode 100644
index 6552a82..0000000
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsIncomingMessage;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
index f39e06d..61911ab 100644
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
@@ -15,34 +15,80 @@
  */
 package android.telephony.ims;
 
-import android.os.Parcel;
+import android.annotation.WorkerThread;
 
 /**
  * This is a single instance of a message received over RCS.
- * @hide - TODO(sahinc) make this public
  */
 public class RcsIncomingMessage extends RcsMessage {
-    public static final Creator<RcsIncomingMessage> CREATOR = new Creator<RcsIncomingMessage>() {
-        @Override
-        public RcsIncomingMessage createFromParcel(Parcel in) {
-            return new RcsIncomingMessage(in);
-        }
-
-        @Override
-        public RcsIncomingMessage[] newArray(int size) {
-            return new RcsIncomingMessage[size];
-        }
-    };
-
-    protected RcsIncomingMessage(Parcel in) {
+    /**
+     * @hide
+     */
+    RcsIncomingMessage(int id) {
+        super(id);
     }
 
-    @Override
-    public int describeContents() {
-        return 0;
+    /**
+     * Sets the timestamp of arrival for this message and persists into storage. The timestamp is
+     * defined as milliseconds passed after midnight, January 1, 1970 UTC
+     *
+     * @param arrivalTimestamp The timestamp to set to.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setArrivalTimestamp(long arrivalTimestamp) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setMessageArrivalTimestamp(mId, true, arrivalTimestamp));
     }
 
+    /**
+     * @return Returns the timestamp of arrival for this message. The timestamp is defined as
+     * milliseconds passed after midnight, January 1, 1970 UTC
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public long getArrivalTimestamp() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getMessageArrivalTimestamp(mId, true));
+    }
+
+    /**
+     * Sets the timestamp of when the user saw this message and persists into storage. The timestamp
+     * is defined as milliseconds passed after midnight, January 1, 1970 UTC
+     *
+     * @param notifiedTimestamp The timestamp to set to.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setSeenTimestamp(long notifiedTimestamp) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setMessageSeenTimestamp(mId, true, notifiedTimestamp));
+    }
+
+    /**
+     * @return Returns the timestamp of when the user saw this message. The timestamp is defined as
+     * milliseconds passed after midnight, January 1, 1970 UTC
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public long getSeenTimestamp() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getMessageSeenTimestamp(mId, true));
+    }
+
+    /**
+     * @return Returns the sender of this incoming message.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public RcsParticipant getSenderParticipant() throws RcsMessageStoreException {
+        return new RcsParticipant(
+                RcsControllerCall.call(iRcs -> iRcs.getSenderParticipant(mId)));
+    }
+
+    /**
+     * @return Returns {@code true} as this is an incoming message
+     */
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public boolean isIncoming() {
+        return true;
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl
index 82d985d..1f1d4f6 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsIncomingMessageCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
new file mode 100644
index 0000000..61dedbc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
@@ -0,0 +1,178 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed
+ * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an
+ * {@link RcsIncomingMessage} on that {@link RcsThread}
+ */
+public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements
+        Parcelable {
+    // The arrival timestamp for the RcsIncomingMessage to be created
+    private final long mArrivalTimestamp;
+    // The seen timestamp for the RcsIncomingMessage to be created
+    private final long mSeenTimestamp;
+    // The participant that sent this incoming message
+    private final int mSenderParticipantId;
+
+    /**
+     * Builder to help create an {@link RcsIncomingMessageCreationParams}
+     *
+     * @see RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)
+     */
+    public static class Builder extends RcsMessageCreationParams.Builder {
+        private RcsParticipant mSenderParticipant;
+        private long mArrivalTimestamp;
+        private long mSeenTimestamp;
+
+        /**
+         * Creates a {@link Builder} to create an instance of
+         * {@link RcsIncomingMessageCreationParams}
+         *
+         * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
+         *                             timestamp value in milliseconds passed after midnight,
+         *                             January 1, 1970 UTC
+         * @param arrivalTimestamp The timestamp of arrival, defined as milliseconds passed after
+         *                         midnight, January 1, 1970 UTC
+         * @param subscriptionId The subscription ID that was used to send or receive this
+         *                       {@link RcsMessage}
+         */
+        public Builder(long originationTimestamp, long arrivalTimestamp, int subscriptionId) {
+            super(originationTimestamp, subscriptionId);
+            mArrivalTimestamp = arrivalTimestamp;
+        }
+
+        /**
+         * Sets the {@link RcsParticipant} that send this {@link RcsIncomingMessage}
+         *
+         * @param senderParticipant The {@link RcsParticipant} that sent this
+         * {@link RcsIncomingMessage}
+         * @return The same instance of {@link Builder} to chain methods.
+         */
+        @CheckResult
+        public Builder setSenderParticipant(RcsParticipant senderParticipant) {
+            mSenderParticipant = senderParticipant;
+            return this;
+        }
+
+        /**
+         * Sets the time of the arrival of this {@link RcsIncomingMessage}
+
+         * @return The same instance of {@link Builder} to chain methods.
+         * @see RcsIncomingMessage#setArrivalTimestamp(long)
+         */
+        @CheckResult
+        public Builder setArrivalTimestamp(long arrivalTimestamp) {
+            mArrivalTimestamp = arrivalTimestamp;
+            return this;
+        }
+
+        /**
+         * Sets the time of the when this user saw the {@link RcsIncomingMessage}
+         * @param seenTimestamp The seen timestamp , defined as milliseconds passed after midnight,
+         *                      January 1, 1970 UTC
+         * @return The same instance of {@link Builder} to chain methods.
+         * @see RcsIncomingMessage#setSeenTimestamp(long)
+         */
+        @CheckResult
+        public Builder setSeenTimestamp(long seenTimestamp) {
+            mSeenTimestamp = seenTimestamp;
+            return this;
+        }
+
+        /**
+         * Creates parameters for creating a new incoming message.
+         * @return A new instance of {@link RcsIncomingMessageCreationParams} to create a new
+         * {@link RcsIncomingMessage}
+         */
+        public RcsIncomingMessageCreationParams build() {
+            return new RcsIncomingMessageCreationParams(this);
+        }
+    }
+
+    private RcsIncomingMessageCreationParams(Builder builder) {
+        super(builder);
+        mArrivalTimestamp = builder.mArrivalTimestamp;
+        mSeenTimestamp = builder.mSeenTimestamp;
+        mSenderParticipantId = builder.mSenderParticipant.getId();
+    }
+
+    private RcsIncomingMessageCreationParams(Parcel in) {
+        super(in);
+        mArrivalTimestamp = in.readLong();
+        mSeenTimestamp = in.readLong();
+        mSenderParticipantId = in.readInt();
+    }
+
+    /**
+     * @return Returns the arrival timestamp for the {@link RcsIncomingMessage} to be created.
+     * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
+     */
+    public long getArrivalTimestamp() {
+        return mArrivalTimestamp;
+    }
+
+    /**
+     * @return Returns the seen timestamp for the {@link RcsIncomingMessage} to be created.
+     * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
+     */
+    public long getSeenTimestamp() {
+        return mSeenTimestamp;
+    }
+
+    /**
+     * Helper getter for {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
+     * create {@link RcsIncomingMessage}s
+     *
+     * Since the API doesn't expose any ID's to API users, this should be hidden.
+     * @hide
+     */
+    public int getSenderParticipantId() {
+        return mSenderParticipantId;
+    }
+
+    public static final Creator<RcsIncomingMessageCreationParams> CREATOR =
+            new Creator<RcsIncomingMessageCreationParams>() {
+                @Override
+                public RcsIncomingMessageCreationParams createFromParcel(Parcel in) {
+                    return new RcsIncomingMessageCreationParams(in);
+                }
+
+                @Override
+                public RcsIncomingMessageCreationParams[] newArray(int size) {
+                    return new RcsIncomingMessageCreationParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest);
+        dest.writeLong(mArrivalTimestamp);
+        dest.writeLong(mSeenTimestamp);
+        dest.writeInt(mSenderParticipantId);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.aidl b/telephony/java/android/telephony/ims/RcsLocationPart.aidl
deleted file mode 100644
index 4fe5ca9..0000000
--- a/telephony/java/android/telephony/ims/RcsLocationPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsLocationPart;
diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.java b/telephony/java/android/telephony/ims/RcsLocationPart.java
deleted file mode 100644
index 19be4ce..0000000
--- a/telephony/java/android/telephony/ims/RcsLocationPart.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a location
- * @hide - TODO(sahinc) make this public
- */
-public class RcsLocationPart extends RcsPart {
-    public static final Creator<RcsLocationPart> CREATOR = new Creator<RcsLocationPart>() {
-        @Override
-        public RcsLocationPart createFromParcel(Parcel in) {
-            return new RcsLocationPart(in);
-        }
-
-        @Override
-        public RcsLocationPart[] newArray(int size) {
-            return new RcsLocationPart[size];
-        }
-    };
-
-    protected RcsLocationPart(Parcel in) {
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsManager.aidl b/telephony/java/android/telephony/ims/RcsManager.aidl
deleted file mode 100644
index 63bc71c..0000000
--- a/telephony/java/android/telephony/ims/RcsManager.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsManager;
diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java
index df108c8..22e4b22 100644
--- a/telephony/java/android/telephony/ims/RcsManager.java
+++ b/telephony/java/android/telephony/ims/RcsManager.java
@@ -20,15 +20,21 @@
 
 /**
  * The manager class for RCS related utilities.
- * @hide
  */
 @SystemService(Context.TELEPHONY_RCS_SERVICE)
 public class RcsManager {
 
+    /**
+     * @hide
+     */
+    public RcsManager() {
+        // empty constructor
+    }
+
     private static final RcsMessageStore sRcsMessageStoreInstance = new RcsMessageStore();
 
     /**
-     * Returns an instance of RcsMessageStore.
+     * Returns an instance of {@link RcsMessageStore}
      */
     public RcsMessageStore getRcsMessageStore() {
         return sRcsMessageStoreInstance;
diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java
index d46685c..3227413 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.java
+++ b/telephony/java/android/telephony/ims/RcsMessage.java
@@ -15,11 +15,315 @@
  */
 package android.telephony.ims;
 
-import android.os.Parcelable;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * This is a single instance of a message sent or received over RCS.
- * @hide - TODO(sahinc) make this public
  */
-public abstract class RcsMessage implements Parcelable {
+public abstract class RcsMessage {
+    /**
+     * The value to indicate that this {@link RcsMessage} does not have any location information.
+     */
+    public static final double LOCATION_NOT_SET = Double.MIN_VALUE;
+
+    /**
+     * The status to indicate that this {@link RcsMessage}s status is not set yet.
+     */
+    public static final int NOT_SET = 0;
+
+    /**
+     * The status to indicate that this {@link RcsMessage} is a draft and is not in the process of
+     * sending yet.
+     */
+    public static final int DRAFT = 1;
+
+    /**
+     * The status to indicate that this {@link RcsMessage} was successfully sent.
+     */
+    public static final int QUEUED = 2;
+
+    /**
+     * The status to indicate that this {@link RcsMessage} is actively being sent.
+     */
+    public static final int SENDING = 3;
+
+    /**
+     * The status to indicate that this {@link RcsMessage} was successfully sent.
+     */
+    public static final int SENT = 4;
+
+    /**
+     * The status to indicate that this {@link RcsMessage} failed to send in an attempt before, and
+     * now being retried.
+     */
+    public static final int RETRYING = 5;
+
+    /**
+     * The status to indicate that this {@link RcsMessage} has permanently failed to send.
+     */
+    public static final int FAILED = 6;
+
+    /**
+     * The status to indicate that this {@link RcsMessage} was successfully received.
+     */
+    public static final int RECEIVED = 7;
+
+    /**
+     * The status to indicate that this {@link RcsMessage} was seen.
+     */
+    public static final int SEEN = 9;
+
+    /**
+     * @hide
+     */
+    protected final int mId;
+
+    @IntDef({
+            DRAFT, QUEUED, SENDING, SENT, RETRYING, FAILED, RECEIVED, SEEN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RcsMessageStatus {
+    }
+
+    RcsMessage(int id) {
+        mId = id;
+    }
+
+    /**
+     * Returns the row Id from the common message.
+     *
+     * @hide
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * @return Returns the subscription ID that this {@link RcsMessage} was sent from, or delivered
+     * to.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     * @see android.telephony.SubscriptionInfo#getSubscriptionId
+     */
+    public int getSubscriptionId() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getMessageSubId(mId, isIncoming()));
+    }
+
+    /**
+     * Sets the subscription ID that this {@link RcsMessage} was sent from, or delivered to and
+     * persists it into storage.
+     *
+     * @param subId The subscription ID to persists into storage.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     * @see android.telephony.SubscriptionInfo#getSubscriptionId
+     */
+    @WorkerThread
+    public void setSubscriptionId(int subId) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setMessageSubId(mId, isIncoming(), subId));
+    }
+
+    /**
+     * Sets the status of this message and persists it into storage. Please see
+     * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
+     *
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setStatus(@RcsMessageStatus int rcsMessageStatus) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setMessageStatus(mId, isIncoming(), rcsMessageStatus));
+    }
+
+    /**
+     * @return Returns the status of this message. Please see
+     * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public @RcsMessageStatus int getStatus() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getMessageStatus(mId, isIncoming()));
+    }
+
+    /**
+     * Sets the origination timestamp of this message and persists it into storage. Origination is
+     * defined as when the sender tapped the send button.
+     *
+     * @param timestamp The origination timestamp value in milliseconds passed after midnight,
+     *                  January 1, 1970 UTC
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setOriginationTimestamp(long timestamp) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setMessageOriginationTimestamp(mId, isIncoming(), timestamp));
+    }
+
+    /**
+     * @return Returns the origination timestamp of this message in milliseconds passed after
+     * midnight, January 1, 1970 UTC. Origination is defined as when the sender tapped the send
+     * button.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public long getOriginationTimestamp() throws RcsMessageStoreException {
+        return RcsControllerCall.call(
+                iRcs -> iRcs.getMessageOriginationTimestamp(mId, isIncoming()));
+    }
+
+    /**
+     * Sets the globally unique RCS message identifier for this message and persists it into
+     * storage. This function does not confirm that this message id is unique. Please see 4.4.5.2
+     * - GSMA RCC.53 (RCS Device API 1.6 Specification
+     *
+     * @param rcsMessageGlobalId The globally RCS message identifier
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setRcsMessageId(String rcsMessageGlobalId) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setGlobalMessageIdForMessage(mId, isIncoming(), rcsMessageGlobalId));
+    }
+
+    /**
+     * @return Returns the globally unique RCS message identifier for this message. Please see
+     * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public String getRcsMessageId() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getGlobalMessageIdForMessage(mId, isIncoming()));
+    }
+
+    /**
+     * @return Returns the user visible text included in this message.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public String getText() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getTextForMessage(mId, isIncoming()));
+    }
+
+    /**
+     * Sets the user visible text for this message and persists in storage.
+     *
+     * @param text The text this message now has
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setText(String text) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setTextForMessage(mId, isIncoming(), text));
+    }
+
+    /**
+     * @return Returns the associated latitude for this message, or
+     * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
+     *
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public double getLatitude() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getLatitudeForMessage(mId, isIncoming()));
+    }
+
+    /**
+     * Sets the latitude for this message and persists in storage.
+     *
+     * @param latitude The latitude for this location message.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setLatitude(double latitude) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setLatitudeForMessage(mId, isIncoming(), latitude));
+    }
+
+    /**
+     * @return Returns the associated longitude for this message, or
+     * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
+     *
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public double getLongitude() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getLongitudeForMessage(mId, isIncoming()));
+    }
+
+    /**
+     * Sets the longitude for this message and persists in storage.
+     *
+     * @param longitude The longitude for this location message.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setLongitude(double longitude) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.setLongitudeForMessage(mId, isIncoming(), longitude));
+    }
+
+    /**
+     * Attaches an {@link RcsFileTransferPart} to this message and persists into storage.
+     *
+     * @param fileTransferCreationParameters The parameters to be used to create the
+     *                                       {@link RcsFileTransferPart}
+     * @return A new instance of {@link RcsFileTransferPart}
+     * @throws RcsMessageStoreException if the file transfer could not be persisted into storage.
+     */
+    @NonNull
+    @WorkerThread
+    public RcsFileTransferPart insertFileTransfer(
+            RcsFileTransferCreationParams fileTransferCreationParameters)
+            throws RcsMessageStoreException {
+        return new RcsFileTransferPart(RcsControllerCall.call(
+                iRcs -> iRcs.storeFileTransfer(mId, isIncoming(), fileTransferCreationParameters)));
+    }
+
+    /**
+     * @return Returns all the {@link RcsFileTransferPart}s associated with this message in an
+     * unmodifiable set.
+     * @throws RcsMessageStoreException if the file transfers could not be read from the storage
+     */
+    @NonNull
+    @WorkerThread
+    public Set<RcsFileTransferPart> getFileTransferParts() throws RcsMessageStoreException {
+        Set<RcsFileTransferPart> fileTransferParts = new HashSet<>();
+
+        int[] fileTransferIds = RcsControllerCall.call(
+                iRcs -> iRcs.getFileTransfersAttachedToMessage(mId, isIncoming()));
+
+        for (int fileTransfer : fileTransferIds) {
+            fileTransferParts.add(new RcsFileTransferPart(fileTransfer));
+        }
+
+        return Collections.unmodifiableSet(fileTransferParts);
+    }
+
+    /**
+     * Removes a {@link RcsFileTransferPart} from this message, and deletes it in storage.
+     *
+     * @param fileTransferPart The part to delete.
+     * @throws RcsMessageStoreException if the file transfer could not be removed from storage
+     */
+    @WorkerThread
+    public void removeFileTransferPart(@NonNull RcsFileTransferPart fileTransferPart)
+            throws RcsMessageStoreException {
+        if (fileTransferPart == null) {
+            return;
+        }
+
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.deleteFileTransfer(fileTransferPart.getId()));
+    }
+
+    /**
+     * @return Returns {@code true} if this message was received on this device, {@code false} if it
+     * was sent.
+     */
+    public abstract boolean isIncoming();
 }
diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
new file mode 100644
index 0000000..c46c605
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
@@ -0,0 +1,240 @@
+/*
+ * 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.telephony.ims;
+
+import static android.telephony.ims.RcsMessage.LOCATION_NOT_SET;
+
+import android.annotation.CheckResult;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+/**
+ * The collection of parameters to be passed into
+ * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and
+ * {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist
+ * {@link RcsMessage}s on an {@link RcsThread}
+ */
+public class RcsMessageCreationParams {
+    // The globally unique id of the RcsMessage to be created.
+    private final String mRcsMessageGlobalId;
+
+    // The subscription that this message was/will be received/sent from.
+    private final int mSubId;
+    // The sending/receiving status of the message
+    private final @RcsMessage.RcsMessageStatus int mMessageStatus;
+    // The timestamp of message creation
+    private final long mOriginationTimestamp;
+    // The user visible content of the message
+    private final String mText;
+    // The latitude of the message if this is a location message
+    private final double mLatitude;
+    // The longitude of the message if this is a location message
+    private final double mLongitude;
+
+    /**
+     * @return Returns the globally unique RCS Message ID for the {@link RcsMessage} to be created.
+     * Please see 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
+     */
+    @Nullable
+    public String getRcsMessageGlobalId() {
+        return mRcsMessageGlobalId;
+    }
+
+    /**
+     * @return Returns the subscription ID that was used to send or receive the {@link RcsMessage}
+     * to be created.
+     */
+    public int getSubId() {
+        return mSubId;
+    }
+
+    /**
+     * @return Returns the status for the {@link RcsMessage} to be created.
+     * @see RcsMessage.RcsMessageStatus
+     */
+    public int getMessageStatus() {
+        return mMessageStatus;
+    }
+
+    /**
+     * @return Returns the origination timestamp of the {@link RcsMessage} to be created in
+     * milliseconds passed after midnight, January 1, 1970 UTC. Origination is defined as when
+     * the sender tapped the send button.
+     */
+    public long getOriginationTimestamp() {
+        return mOriginationTimestamp;
+    }
+
+    /**
+     * @return Returns the user visible text contained in the {@link RcsMessage} to be created
+     */
+    @Nullable
+    public String getText() {
+        return mText;
+    }
+
+    /**
+     * @return Returns the latitude of the {@link RcsMessage} to be created, or
+     * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
+     */
+    public double getLatitude() {
+        return mLatitude;
+    }
+
+    /**
+     * @return Returns the longitude of the {@link RcsMessage} to be created, or
+     * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
+     */
+    public double getLongitude() {
+        return mLongitude;
+    }
+
+    /**
+     * The base builder for creating {@link RcsMessage}s on {@link RcsThread}s.
+     *
+     * @see RcsIncomingMessageCreationParams
+     */
+    public static class Builder {
+        private String mRcsMessageGlobalId;
+        private int mSubId;
+        private @RcsMessage.RcsMessageStatus int mMessageStatus;
+        private long mOriginationTimestamp;
+        private String mText;
+        private double mLatitude = LOCATION_NOT_SET;
+        private double mLongitude = LOCATION_NOT_SET;
+
+        /**
+         * @hide
+         */
+        public Builder(long originationTimestamp, int subscriptionId) {
+            mOriginationTimestamp = originationTimestamp;
+            mSubId = subscriptionId;
+        }
+
+        /**
+         * Sets the status of the {@link RcsMessage} to be built.
+         *
+         * @param rcsMessageStatus The status to be set
+         * @return The same instance of {@link Builder} to chain methods
+         * @see RcsMessage#setStatus(int)
+         */
+        @CheckResult
+        public Builder setStatus(@RcsMessage.RcsMessageStatus int rcsMessageStatus) {
+            mMessageStatus = rcsMessageStatus;
+            return this;
+        }
+
+        /**
+         * Sets the globally unique RCS message identifier for the {@link RcsMessage} to be built.
+         * This function does not confirm that this message id is unique. Please see 4.4.5.2 - GSMA
+         * RCC.53 (RCS Device API 1.6 Specification)
+         *
+         * @param rcsMessageId The ID to be set
+         * @return The same instance of {@link Builder} to chain methods
+         * @see RcsMessage#setRcsMessageId(String)
+         */
+        @CheckResult
+        public Builder setRcsMessageId(String rcsMessageId) {
+            mRcsMessageGlobalId = rcsMessageId;
+            return this;
+        }
+
+        /**
+         * Sets the text of the {@link RcsMessage} to be built.
+         *
+         * @param text The user visible text of the message
+         * @return The same instance of {@link Builder} to chain methods
+         * @see RcsMessage#setText(String)
+         */
+        @CheckResult
+        public Builder setText(String text) {
+            mText = text;
+            return this;
+        }
+
+        /**
+         * Sets the latitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
+         * (RCS Universal Profile Service Definition Document)
+         *
+         * @param latitude The latitude of the location information associated with this message.
+         * @return The same instance of {@link Builder} to chain methods
+         * @see RcsMessage#setLatitude(double)
+         */
+        @CheckResult
+        public Builder setLatitude(double latitude) {
+            mLatitude = latitude;
+            return this;
+        }
+
+        /**
+         * Sets the longitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
+         * (RCS Universal Profile Service Definition Document)
+         *
+         * @param longitude The longitude of the location information associated with this message.
+         * @return The same instance of {@link Builder} to chain methods
+         * @see RcsMessage#setLongitude(double)
+         */
+        @CheckResult
+        public Builder setLongitude(double longitude) {
+            mLongitude = longitude;
+            return this;
+        }
+
+        /**
+         * @return Builds and returns a newly created {@link RcsMessageCreationParams}
+         */
+        public RcsMessageCreationParams build() {
+            return new RcsMessageCreationParams(this);
+        }
+    }
+
+    protected RcsMessageCreationParams(Builder builder) {
+        mRcsMessageGlobalId = builder.mRcsMessageGlobalId;
+        mSubId = builder.mSubId;
+        mMessageStatus = builder.mMessageStatus;
+        mOriginationTimestamp = builder.mOriginationTimestamp;
+        mText = builder.mText;
+        mLatitude = builder.mLatitude;
+        mLongitude = builder.mLongitude;
+    }
+
+    /**
+     * @hide
+     */
+    RcsMessageCreationParams(Parcel in) {
+        mRcsMessageGlobalId = in.readString();
+        mSubId = in.readInt();
+        mMessageStatus = in.readInt();
+        mOriginationTimestamp = in.readLong();
+        mText = in.readString();
+        mLatitude = in.readDouble();
+        mLongitude = in.readDouble();
+    }
+
+    /**
+     * @hide
+     */
+    public void writeToParcel(Parcel dest) {
+        dest.writeString(mRcsMessageGlobalId);
+        dest.writeInt(mSubId);
+        dest.writeInt(mMessageStatus);
+        dest.writeLong(mOriginationTimestamp);
+        dest.writeString(mText);
+        dest.writeDouble(mLatitude);
+        dest.writeDouble(mLongitude);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl
similarity index 94%
rename from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
rename to telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl
index 82d985d..e9cbd9c 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsMessageQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
new file mode 100644
index 0000000..535a597
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
@@ -0,0 +1,359 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * The parameters to pass into
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a
+ * subset of {@link RcsMessage}s present in the message store.
+ */
+public final class RcsMessageQueryParams implements Parcelable {
+    /**
+     * @hide - not meant for public use
+     */
+    public static final int THREAD_ID_NOT_SET = -1;
+
+    /**
+     * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+     * be sorted in the same order of {@link RcsMessage}s that got persisted into storage for faster
+     * results.
+     */
+    public static final int SORT_BY_CREATION_ORDER = 0;
+
+    /**
+     * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+     * be sorted according to the timestamp of {@link RcsMessage#getOriginationTimestamp()}
+     */
+    public static final int SORT_BY_TIMESTAMP = 1;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
+    public @interface SortingProperty {
+    }
+
+    /**
+     * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
+     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return
+     * {@link RcsIncomingMessage}s.
+     */
+    public static final int MESSAGE_TYPE_INCOMING = 0x0001;
+
+    /**
+     * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
+     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return
+     * {@link RcsOutgoingMessage}s.
+     */
+    public static final int MESSAGE_TYPE_OUTGOING = 0x0002;
+
+    /**
+     * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
+     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s
+     * that have an {@link RcsFileTransferPart} attached.
+     */
+    public static final int MESSAGES_WITH_FILE_TRANSFERS = 0x0004;
+
+    /**
+     * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
+     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s
+     * that don't have an {@link RcsFileTransferPart} attached.
+     */
+    public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 0x0008;
+
+    /**
+     * @hide - not meant for public use
+     */
+    public static final String MESSAGE_QUERY_PARAMETERS_KEY = "message_query_parameters";
+
+    // Whether the result should be filtered against incoming or outgoing messages
+    private int mMessageType;
+    // Whether the result should have file transfer messages attached or not
+    private int mFileTransferPresence;
+    // The SQL "Like" clause to filter messages
+    private String mMessageLike;
+    // The property the messages should be sorted against
+    private @SortingProperty int mSortingProperty;
+    // Whether the messages should be sorted in ascending order
+    private boolean mIsAscending;
+    // The number of results that should be returned with this query
+    private int mLimit;
+    // The thread that the results should be limited to
+    private int mThreadId;
+
+    RcsMessageQueryParams(int messageType, int fileTransferPresence, String messageLike,
+            int threadId, @SortingProperty int sortingProperty, boolean isAscending, int limit) {
+        mMessageType = messageType;
+        mFileTransferPresence = fileTransferPresence;
+        mMessageLike = messageLike;
+        mSortingProperty = sortingProperty;
+        mIsAscending = isAscending;
+        mLimit = limit;
+        mThreadId = threadId;
+    }
+
+    /**
+     * @return Returns the type of {@link RcsMessage}s that this {@link RcsMessageQueryParams}
+     * is set to query for.
+     */
+    public int getMessageType() {
+        return mMessageType;
+    }
+
+    /**
+     * @return Returns whether the result query should return {@link RcsMessage}s with
+     * {@link RcsFileTransferPart}s or not
+     */
+    public int getFileTransferPresence() {
+        return mFileTransferPresence;
+    }
+
+    /**
+     * @return Returns the SQL-inspired "LIKE" clause that will be used to match {@link RcsMessage}s
+     */
+    public String getMessageLike() {
+        return mMessageLike;
+    }
+
+    /**
+     * @return Returns the number of {@link RcsThread}s to be returned from the query. A value of
+     * 0 means there is no set limit.
+     */
+    public int getLimit() {
+        return mLimit;
+    }
+
+    /**
+     * @return Returns the property that will be used to sort the result against.
+     * @see SortingProperty
+     */
+    public @SortingProperty int getSortingProperty() {
+        return mSortingProperty;
+    }
+
+    /**
+     * @return Returns {@code true} if the result set will be sorted in ascending order,
+     * {@code false} if it will be sorted in descending order.
+     */
+    public boolean getSortDirection() {
+        return mIsAscending;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * the thread that the result query should be limited to.
+     *
+     * As we do not expose any sort of integer ID's to public usage, this should be hidden.
+     *
+     * @hide - not meant for public use
+     */
+    public int getThreadId() {
+        return mThreadId;
+    }
+
+    /**
+     * A helper class to build the {@link RcsMessageQueryParams}.
+     */
+    public static class Builder {
+        private @SortingProperty int mSortingProperty;
+        private int mMessageType;
+        private int mFileTransferPresence;
+        private String mMessageLike;
+        private boolean mIsAscending;
+        private int mLimit = 100;
+        private int mThreadId = THREAD_ID_NOT_SET;
+
+        /**
+         * Creates a new builder for {@link RcsMessageQueryParams} to be used in
+         * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
+         *
+         */
+        public Builder() {
+            // empty implementation
+        }
+
+        /**
+         * Desired number of threads to be returned from the query. Passing in 0 will return all
+         * existing threads at once. The limit defaults to 100.
+         *
+         * @param limit The number to limit the query result to.
+         * @return The same instance of the builder to chain parameters.
+         * @throws InvalidParameterException If the given limit is negative.
+         */
+        @CheckResult
+        public Builder setResultLimit(@IntRange(from = 0) int limit)
+                throws InvalidParameterException {
+            if (limit < 0) {
+                throw new InvalidParameterException("The query limit must be non-negative");
+            }
+
+            mLimit = limit;
+            return this;
+        }
+
+        /**
+         * Sets the type of messages to be returned from the query.
+         *
+         * @param messageType The type of message to be returned.
+         * @return The same instance of the builder to chain parameters.
+         * @see RcsMessageQueryParams#MESSAGE_TYPE_INCOMING
+         * @see RcsMessageQueryParams#MESSAGE_TYPE_OUTGOING
+         */
+        @CheckResult
+        public Builder setMessageType(int messageType) {
+            mMessageType = messageType;
+            return this;
+        }
+
+        /**
+         * Sets whether file transfer messages should be included in the query result or not.
+         *
+         * @param fileTransferPresence Whether file transfers should be included in the result
+         * @return The same instance of the builder to chain parameters.
+         * @see RcsMessageQueryParams#MESSAGES_WITH_FILE_TRANSFERS
+         * @see RcsMessageQueryParams#MESSAGES_WITHOUT_FILE_TRANSFERS
+         */
+        @CheckResult
+        public Builder setFileTransferPresence(int fileTransferPresence) {
+            mFileTransferPresence = fileTransferPresence;
+            return this;
+        }
+
+        /**
+         * Sets an SQL-inspired "like" clause to match with messages. Using a percent sign ('%')
+         * wildcard matches any sequence of zero or more characters. Using an underscore ('_')
+         * wildcard matches any single character. Not using any wildcards would only perform a
+         * string match. The input string is case-insensitive.
+         *
+         * The input "Wh%" would match messages "who", "where" and "what", while the input "Wh_"
+         * would only match "who"
+         *
+         * @param messageLike The "like" clause for matching {@link RcsMessage}s.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setMessageLike(String messageLike) {
+            mMessageLike = messageLike;
+            return this;
+        }
+
+        /**
+         * Sets the property where the results should be sorted against. Defaults to
+         * {@link RcsMessageQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
+         *
+         * @param sortingProperty against which property the results should be sorted
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setSortProperty(@SortingProperty int sortingProperty) {
+            mSortingProperty = sortingProperty;
+            return this;
+        }
+
+        /**
+         * Sets whether the results should be sorted ascending or descending
+         *
+         * @param isAscending whether the results should be sorted ascending
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setSortDirection(boolean isAscending) {
+            mIsAscending = isAscending;
+            return this;
+        }
+
+        /**
+         * Limits the results to the given thread.
+         *
+         * @param thread the {@link RcsThread} that results should be limited to. If set to
+         *               {@code null}, messages on all threads will be queried
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setThread(@Nullable RcsThread thread) {
+            if (thread == null) {
+                mThreadId = THREAD_ID_NOT_SET;
+            } else {
+                mThreadId = thread.getThreadId();
+            }
+            return this;
+        }
+
+        /**
+         * Builds the {@link RcsMessageQueryParams} to use in
+         * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
+         *
+         * @return An instance of {@link RcsMessageQueryParams} to use with the message
+         * query.
+         */
+        public RcsMessageQueryParams build() {
+            return new RcsMessageQueryParams(mMessageType, mFileTransferPresence, mMessageLike,
+                    mThreadId, mSortingProperty, mIsAscending, mLimit);
+        }
+    }
+
+    /**
+     * Parcelable boilerplate below.
+     */
+    private RcsMessageQueryParams(Parcel in) {
+        mMessageType = in.readInt();
+        mFileTransferPresence = in.readInt();
+        mMessageLike = in.readString();
+        mSortingProperty = in.readInt();
+        mIsAscending = in.readBoolean();
+        mLimit = in.readInt();
+        mThreadId = in.readInt();
+    }
+
+    public static final Creator<RcsMessageQueryParams> CREATOR =
+            new Creator<RcsMessageQueryParams>() {
+                @Override
+                public RcsMessageQueryParams createFromParcel(Parcel in) {
+                    return new RcsMessageQueryParams(in);
+                }
+
+                @Override
+                public RcsMessageQueryParams[] newArray(int size) {
+                    return new RcsMessageQueryParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mMessageType);
+        dest.writeInt(mFileTransferPresence);
+        dest.writeString(mMessageLike);
+        dest.writeInt(mSortingProperty);
+        dest.writeBoolean(mIsAscending);
+        dest.writeInt(mLimit);
+        dest.writeInt(mThreadId);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl
index 82d985d..a73ba50 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsMessageQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
new file mode 100644
index 0000000..3514b48e
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
@@ -0,0 +1,113 @@
+/*
+ * 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.telephony.ims;
+
+import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_INCOMING;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.ims.RcsTypeIdPair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
+ * call. This class allows getting the token for querying the next batch of messages in order to
+ * prevent handling large amounts of data at once.
+ */
+public final class RcsMessageQueryResult implements Parcelable {
+    // The token to continue the query to get the next batch of results
+    private RcsQueryContinuationToken mContinuationToken;
+    // The message type and message ID pairs for all the messages in this query result
+    private List<RcsTypeIdPair> mMessageTypeIdPairs;
+
+    /**
+     * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+     * to create query results
+     *
+     * @hide
+     */
+    public RcsMessageQueryResult(
+            RcsQueryContinuationToken continuationToken,
+            List<RcsTypeIdPair> messageTypeIdPairs) {
+        mContinuationToken = continuationToken;
+        mMessageTypeIdPairs = messageTypeIdPairs;
+    }
+
+    /**
+     * Returns a token to call
+     * {@link RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)}
+     * to get the next batch of {@link RcsMessage}s.
+     */
+    @Nullable
+    public RcsQueryContinuationToken getContinuationToken() {
+        return mContinuationToken;
+    }
+
+    /**
+     * Returns all the {@link RcsMessage}s in the current query result. Call {@link
+     * RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)} to get the next batch
+     * of {@link RcsMessage}s.
+     */
+    @NonNull
+    public List<RcsMessage> getMessages() {
+        List<RcsMessage> messages = new ArrayList<>();
+        for (RcsTypeIdPair typeIdPair : mMessageTypeIdPairs) {
+            if (typeIdPair.getType() == MESSAGE_TYPE_INCOMING) {
+                messages.add(new RcsIncomingMessage(typeIdPair.getId()));
+            } else {
+                messages.add(new RcsOutgoingMessage(typeIdPair.getId()));
+            }
+        }
+
+        return messages;
+    }
+
+    private RcsMessageQueryResult(Parcel in) {
+        mContinuationToken = in.readParcelable(
+                RcsQueryContinuationToken.class.getClassLoader());
+        in.readTypedList(mMessageTypeIdPairs, RcsTypeIdPair.CREATOR);
+    }
+
+    public static final Creator<RcsMessageQueryResult> CREATOR =
+            new Creator<RcsMessageQueryResult>() {
+                @Override
+                public RcsMessageQueryResult createFromParcel(Parcel in) {
+                    return new RcsMessageQueryResult(in);
+                }
+
+                @Override
+                public RcsMessageQueryResult[] newArray(int size) {
+                    return new RcsMessageQueryResult[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mContinuationToken, flags);
+        dest.writeTypedList(mMessageTypeIdPairs);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
similarity index 95%
rename from telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
rename to telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
index 9fdc41d..99b8eb7 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable Rcs1To1Thread;
+parcelable RcsMessageSnippet;
diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
new file mode 100644
index 0000000..b0b930c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
@@ -0,0 +1,96 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.RcsMessage.RcsMessageStatus;
+
+/**
+ * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread}
+ */
+public final class RcsMessageSnippet implements Parcelable {
+    private final String mText;
+    private final @RcsMessageStatus int mStatus;
+    private final long mTimestamp;
+
+    /**
+     * @hide
+     */
+    public RcsMessageSnippet(String text, @RcsMessageStatus int status, long timestamp) {
+        mText = text;
+        mStatus = status;
+        mTimestamp = timestamp;
+    }
+
+    /**
+     * @return Returns the text of the {@link RcsMessage} with highest origination timestamp value
+     * (i.e. latest) in this thread
+     */
+    @Nullable
+    public String getSnippetText() {
+        return mText;
+    }
+
+    /**
+     * @return Returns the status of the {@link RcsMessage} with highest origination timestamp value
+     * (i.e. latest) in this thread
+     */
+    public @RcsMessageStatus int getSnippetStatus() {
+        return mStatus;
+    }
+
+    /**
+     * @return Returns the timestamp of the {@link RcsMessage} with highest origination timestamp
+     * value (i.e. latest) in this thread
+     */
+    public long getSnippetTimestamp() {
+        return mTimestamp;
+    }
+
+    private RcsMessageSnippet(Parcel in) {
+        mText = in.readString();
+        mStatus = in.readInt();
+        mTimestamp = in.readLong();
+    }
+
+    public static final Creator<RcsMessageSnippet> CREATOR =
+            new Creator<RcsMessageSnippet>() {
+                @Override
+                public RcsMessageSnippet createFromParcel(Parcel in) {
+                    return new RcsMessageSnippet(in);
+                }
+
+                @Override
+                public RcsMessageSnippet[] newArray(int size) {
+                    return new RcsMessageSnippet[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mText);
+        dest.writeInt(mStatus);
+        dest.writeLong(mTimestamp);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
index 1bf6ffd..d811c6e 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStore.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -16,106 +16,221 @@
 
 package android.telephony.ims;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.Rlog;
-import android.telephony.ims.aidl.IRcs;
+import android.net.Uri;
+
+import java.util.List;
 
 /**
  * RcsMessageStore is the application interface to RcsProvider and provides access methods to
  * RCS related database tables.
- * @hide - TODO make this public
  */
 public class RcsMessageStore {
-    static final String TAG = "RcsMessageStore";
-
     /**
      * Returns the first chunk of existing {@link RcsThread}s in the common storage.
+     *
      * @param queryParameters Parameters to specify to return a subset of all RcsThreads.
      *                        Passing a value of null will return all threads.
+     * @throws RcsMessageStoreException if the query could not be completed on the storage
      */
     @WorkerThread
-    public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParameters queryParameters) {
-        try {
-            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
-            if (iRcs != null) {
-                return iRcs.getRcsThreads(queryParameters);
-            }
-        } catch (RemoteException re) {
-            Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
-        }
-
-        return null;
+    @NonNull
+    public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParams queryParameters)
+            throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getRcsThreads(queryParameters));
     }
 
     /**
      * Returns the next chunk of {@link RcsThread}s in the common storage.
+     *
      * @param continuationToken A token to continue the query to get the next chunk. This is
-     *                          obtained through {@link RcsThreadQueryResult#nextChunkToken}.
+     *                          obtained through {@link RcsThreadQueryResult#getContinuationToken}.
+     * @throws RcsMessageStoreException if the query could not be completed on the storage
      */
     @WorkerThread
-    public RcsThreadQueryResult getRcsThreads(RcsThreadQueryContinuationToken continuationToken) {
-        try {
-            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
-            if (iRcs != null) {
-                return iRcs.getRcsThreadsWithToken(continuationToken);
-            }
-        } catch (RemoteException re) {
-            Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
-        }
+    @NonNull
+    public RcsThreadQueryResult getRcsThreads(@NonNull RcsQueryContinuationToken continuationToken)
+            throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getRcsThreadsWithToken(continuationToken));
+    }
 
-        return null;
+    /**
+     * Returns the first chunk of existing {@link RcsParticipant}s in the common storage.
+     *
+     * @param queryParameters Parameters to specify to return a subset of all RcsParticipants.
+     *                        Passing a value of null will return all participants.
+     * @throws RcsMessageStoreException if the query could not be completed on the storage
+     */
+    @WorkerThread
+    @NonNull
+    public RcsParticipantQueryResult getRcsParticipants(
+            @Nullable RcsParticipantQueryParams queryParameters)
+            throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getParticipants(queryParameters));
+    }
+
+    /**
+     * Returns the next chunk of {@link RcsParticipant}s in the common storage.
+     *
+     * @param continuationToken A token to continue the query to get the next chunk. This is
+     *                          obtained through
+     *                          {@link RcsParticipantQueryResult#getContinuationToken}
+     * @throws RcsMessageStoreException if the query could not be completed on the storage
+     */
+    @WorkerThread
+    @NonNull
+    public RcsParticipantQueryResult getRcsParticipants(
+            @NonNull RcsQueryContinuationToken continuationToken)
+            throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getParticipantsWithToken(continuationToken));
+    }
+
+    /**
+     * Returns the first chunk of existing {@link RcsMessage}s in the common storage.
+     *
+     * @param queryParameters Parameters to specify to return a subset of all RcsMessages.
+     *                        Passing a value of null will return all messages.
+     * @throws RcsMessageStoreException if the query could not be completed on the storage
+     */
+    @WorkerThread
+    @NonNull
+    public RcsMessageQueryResult getRcsMessages(
+            @Nullable RcsMessageQueryParams queryParameters) throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters));
+    }
+
+    /**
+     * Returns the next chunk of {@link RcsMessage}s in the common storage.
+     *
+     * @param continuationToken A token to continue the query to get the next chunk. This is
+     *                          obtained through {@link RcsMessageQueryResult#getContinuationToken}
+     * @throws RcsMessageStoreException if the query could not be completed on the storage
+     */
+    @WorkerThread
+    @NonNull
+    public RcsMessageQueryResult getRcsMessages(
+            @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getMessagesWithToken(continuationToken));
+    }
+
+    /**
+     * Returns the first chunk of existing {@link RcsEvent}s in the common storage.
+     *
+     * @param queryParameters Parameters to specify to return a subset of all RcsEvents.
+     *                        Passing a value of null will return all events.
+     * @throws RcsMessageStoreException if the query could not be completed on the storage
+     */
+    @WorkerThread
+    @NonNull
+    public RcsEventQueryResult getRcsEvents(
+            @Nullable RcsEventQueryParams queryParameters) throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getEvents(queryParameters));
+    }
+
+    /**
+     * Returns the next chunk of {@link RcsEvent}s in the common storage.
+     *
+     * @param continuationToken A token to continue the query to get the next chunk. This is
+     *                          obtained through {@link RcsEventQueryResult#getContinuationToken}.
+     * @throws RcsMessageStoreException if the query could not be completed on the storage
+     */
+    @WorkerThread
+    @NonNull
+    public RcsEventQueryResult getRcsEvents(
+            @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getEventsWithToken(continuationToken));
+    }
+
+    /**
+     * Persists an {@link RcsEvent} to common storage.
+     *
+     * @param persistableEvent The {@link RcsEvent} to persist into storage.
+     * @throws RcsMessageStoreException if the query could not be completed on the storage
+     *
+     * @see RcsGroupThreadNameChangedEvent
+     * @see RcsGroupThreadIconChangedEvent
+     * @see RcsGroupThreadParticipantJoinedEvent
+     * @see RcsGroupThreadParticipantLeftEvent
+     * @see RcsParticipantAliasChangedEvent
+     */
+    @WorkerThread
+    @NonNull
+    public void persistRcsEvent(RcsEvent persistableEvent) throws RcsMessageStoreException {
+        persistableEvent.persist();
     }
 
     /**
      * Creates a new 1 to 1 thread with the given participant and persists it in the storage.
+     *
+     * @param recipient The {@link RcsParticipant} that will receive the messages in this thread.
+     * @return The newly created {@link Rcs1To1Thread}
+     * @throws RcsMessageStoreException if the thread could not be persisted in the storage
      */
     @WorkerThread
-    public Rcs1To1Thread createRcs1To1Thread(RcsParticipant recipient) {
-        try {
-            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
-            if (iRcs != null) {
-                return iRcs.createRcs1To1Thread(recipient);
-            }
-        } catch (RemoteException re) {
-            Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcs1To1Thread", re);
-        }
-
-        return null;
+    @NonNull
+    public Rcs1To1Thread createRcs1To1Thread(@NonNull RcsParticipant recipient)
+            throws RcsMessageStoreException {
+        return new Rcs1To1Thread(
+                RcsControllerCall.call(iRcs -> iRcs.createRcs1To1Thread(recipient.getId())));
     }
 
     /**
-     * Delete the {@link RcsThread} identified by the given threadId.
-     * @param threadId threadId of the thread to be deleted.
+     * Creates a new group thread with the given participants and persists it in the storage.
+     *
+     * @throws RcsMessageStoreException if the thread could not be persisted in the storage
      */
     @WorkerThread
-    public void deleteThread(int threadId) {
-        try {
-            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
-            if (iRcs != null) {
-                iRcs.deleteThread(threadId);
+    @NonNull
+    public RcsGroupThread createGroupThread(@Nullable List<RcsParticipant> recipients,
+            @Nullable String groupName, @Nullable Uri groupIcon) throws RcsMessageStoreException {
+        int[] recipientIds = null;
+        if (recipients != null) {
+            recipientIds = new int[recipients.size()];
+
+            for (int i = 0; i < recipients.size(); i++) {
+                recipientIds[i] = recipients.get(i).getId();
             }
-        } catch (RemoteException re) {
-            Rlog.e(TAG, "RcsMessageStore: Exception happened during deleteThread", re);
+        }
+
+        int[] finalRecipientIds = recipientIds;
+        return new RcsGroupThread(RcsControllerCall.call(
+                iRcs -> iRcs.createGroupThread(finalRecipientIds, groupName, groupIcon)));
+    }
+
+    /**
+     * Delete the given {@link RcsThread} from the storage.
+     *
+     * @param thread The thread to be deleted.
+     * @throws RcsMessageStoreException if the thread could not be deleted from the storage
+     */
+    @WorkerThread
+    public void deleteThread(@NonNull RcsThread thread) throws RcsMessageStoreException {
+        if (thread == null) {
+            return;
+        }
+
+        boolean isDeleteSucceeded = RcsControllerCall.call(
+                iRcs -> iRcs.deleteThread(thread.getThreadId(), thread.getThreadType()));
+
+        if (!isDeleteSucceeded) {
+            throw new RcsMessageStoreException("Could not delete RcsThread");
         }
     }
 
     /**
      * Creates a new participant and persists it in the storage.
+     *
      * @param canonicalAddress The defining address (e.g. phone number) of the participant.
+     * @param alias            The RCS alias for the participant.
+     * @throws RcsMessageStoreException if the participant could not be created on the storage
      */
-    public RcsParticipant createRcsParticipant(String canonicalAddress) {
-        try {
-            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
-            if (iRcs != null) {
-                return iRcs.createRcsParticipant(canonicalAddress);
-            }
-        } catch (RemoteException re) {
-            Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcsParticipant", re);
-        }
-
-        return null;
+    @WorkerThread
+    @NonNull
+    public RcsParticipant createRcsParticipant(String canonicalAddress, @Nullable String alias)
+            throws RcsMessageStoreException {
+        return new RcsParticipant(
+                RcsControllerCall.call(iRcs -> iRcs.createRcsParticipant(canonicalAddress, alias)));
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsMessageStoreException.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
new file mode 100644
index 0000000..f25bb17
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.telephony.ims;
+
+/**
+ * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in
+ * {@link android.telephony.ims}
+ */
+public class RcsMessageStoreException extends Exception {
+
+    /**
+     * Constructs an {@link RcsMessageStoreException} with the specified detail message.
+     * @param message The detail message
+     * @see Throwable#getMessage()
+     */
+    public RcsMessageStoreException(String message) {
+        super(message);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java b/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
deleted file mode 100644
index d295fba..0000000
--- a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a media that is rendered on the screen
- * (i.e. image, video etc)
- * @hide - TODO(sahinc) make this public
- */
-public class RcsMultiMediaPart extends RcsFileTransferPart {
-    public static final Creator<RcsMultiMediaPart> CREATOR = new Creator<RcsMultiMediaPart>() {
-        @Override
-        public RcsMultiMediaPart createFromParcel(Parcel in) {
-            return new RcsMultiMediaPart(in);
-        }
-
-        @Override
-        public RcsMultiMediaPart[] newArray(int size) {
-            return new RcsMultiMediaPart[size];
-        }
-    };
-
-    protected RcsMultiMediaPart(Parcel in) {
-        super(in);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl b/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
deleted file mode 100644
index 5992d95..0000000
--- a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsMultimediaPart;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
deleted file mode 100644
index 6e0c80f..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsOutgoingMessage;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
index bfb1611..06fb832 100644
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
@@ -15,34 +15,51 @@
  */
 package android.telephony.ims;
 
-import android.os.Parcel;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This is a single instance of a message sent over RCS.
- * @hide - TODO(sahinc) make this public
  */
 public class RcsOutgoingMessage extends RcsMessage {
-    public static final Creator<RcsOutgoingMessage> CREATOR = new Creator<RcsOutgoingMessage>() {
-        @Override
-        public RcsOutgoingMessage createFromParcel(Parcel in) {
-            return new RcsOutgoingMessage(in);
-        }
-
-        @Override
-        public RcsOutgoingMessage[] newArray(int size) {
-            return new RcsOutgoingMessage[size];
-        }
-    };
-
-    protected RcsOutgoingMessage(Parcel in) {
+    RcsOutgoingMessage(int id) {
+        super(id);
     }
 
-    @Override
-    public int describeContents() {
-        return 0;
+    /**
+     * @return Returns the {@link RcsOutgoingMessageDelivery}s associated with this message. Please
+     * note that the deliveries returned for the {@link RcsOutgoingMessage} may not always match the
+     * {@link RcsParticipant}s on the {@link RcsGroupThread} as the group recipients may have
+     * changed.
+     * @throws RcsMessageStoreException if the outgoing deliveries could not be read from storage.
+     */
+    @NonNull
+    @WorkerThread
+    public List<RcsOutgoingMessageDelivery> getOutgoingDeliveries()
+            throws RcsMessageStoreException {
+        int[] deliveryParticipants;
+        List<RcsOutgoingMessageDelivery> messageDeliveries = new ArrayList<>();
+
+        deliveryParticipants = RcsControllerCall.call(
+                iRcs -> iRcs.getMessageRecipients(mId));
+
+        if (deliveryParticipants != null) {
+            for (Integer deliveryParticipant : deliveryParticipants) {
+                messageDeliveries.add(new RcsOutgoingMessageDelivery(deliveryParticipant, mId));
+            }
+        }
+
+        return messageDeliveries;
     }
 
+    /**
+     * @return Returns {@code false} as this is not an incoming message.
+     */
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public boolean isIncoming() {
+        return false;
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl
similarity index 93%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl
index 82d985d..0c38d9f 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsOutgoingMessageCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
new file mode 100644
index 0000000..979634a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
@@ -0,0 +1,87 @@
+/*
+ * 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.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed
+ * into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an
+ * {@link RcsOutgoingMessage} on that {@link RcsThread}
+ */
+public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams
+        implements Parcelable {
+    /**
+     * A builder to instantiate and persist an {@link RcsOutgoingMessage}
+     */
+    public static class Builder extends RcsMessageCreationParams.Builder {
+
+        /**
+         * Creates a new {@link Builder} to create an instance of
+         * {@link RcsOutgoingMessageCreationParams}.
+         *
+         * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
+         *                             timestamp value in milliseconds passed after midnight,
+         *                             January 1, 1970 UTC
+         * @param subscriptionId The subscription ID that was used to send or receive this
+         *                       {@link RcsMessage}
+         * @see android.telephony.SubscriptionInfo#getSubscriptionId()
+         */
+        public Builder(long originationTimestamp, int subscriptionId) {
+            super(originationTimestamp, subscriptionId);
+        }
+
+        /**
+         * Creates configuration parameters for a new message.
+         */
+        public RcsOutgoingMessageCreationParams build() {
+            return new RcsOutgoingMessageCreationParams(this);
+        }
+    }
+
+    private RcsOutgoingMessageCreationParams(Builder builder) {
+        super(builder);
+    }
+
+    private RcsOutgoingMessageCreationParams(Parcel in) {
+        super(in);
+    }
+
+    public static final Creator<RcsOutgoingMessageCreationParams> CREATOR =
+            new Creator<RcsOutgoingMessageCreationParams>() {
+                @Override
+                public RcsOutgoingMessageCreationParams createFromParcel(Parcel in) {
+                    return new RcsOutgoingMessageCreationParams(in);
+                }
+
+                @Override
+                public RcsOutgoingMessageCreationParams[] newArray(int size) {
+                    return new RcsOutgoingMessageCreationParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
new file mode 100644
index 0000000..1c87b13
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
@@ -0,0 +1,129 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+/**
+ * This class holds the delivery information of an {@link RcsOutgoingMessage} for each
+ * {@link RcsParticipant} that the message was intended for.
+ */
+public class RcsOutgoingMessageDelivery {
+    // The participant that this delivery is intended for
+    private final int mRecipientId;
+    // The message this delivery is associated with
+    private final int mRcsOutgoingMessageId;
+
+    /**
+     * Constructor to be used with RcsOutgoingMessage.getDelivery()
+     *
+     * @hide
+     */
+    RcsOutgoingMessageDelivery(int recipientId, int messageId) {
+        mRecipientId = recipientId;
+        mRcsOutgoingMessageId = messageId;
+    }
+
+    /**
+     * Sets the delivery time of this outgoing delivery and persists into storage.
+     *
+     * @param deliveredTimestamp The timestamp to set to delivery. It is defined as milliseconds
+     *                           passed after midnight, January 1, 1970 UTC
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setDeliveredTimestamp(long deliveredTimestamp) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryDeliveredTimestamp(
+                mRcsOutgoingMessageId, mRecipientId, deliveredTimestamp));
+    }
+
+    /**
+     * @return Returns the delivered timestamp of the associated message to the associated
+     * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
+     * Returns 0 if the {@link RcsOutgoingMessage} is not delivered yet.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public long getDeliveredTimestamp() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getOutgoingDeliveryDeliveredTimestamp(
+                mRcsOutgoingMessageId, mRecipientId));
+    }
+
+    /**
+     * Sets the seen time of this outgoing delivery and persists into storage.
+     *
+     * @param seenTimestamp The timestamp to set to delivery. It is defined as milliseconds
+     *                      passed after midnight, January 1, 1970 UTC
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setSeenTimestamp(long seenTimestamp) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliverySeenTimestamp(
+                mRcsOutgoingMessageId, mRecipientId, seenTimestamp));
+    }
+
+    /**
+     * @return Returns the seen timestamp of the associated message by the associated
+     * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
+     * Returns 0 if the {@link RcsOutgoingMessage} is not seen yet.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public long getSeenTimestamp() throws RcsMessageStoreException {
+        return RcsControllerCall.call(
+                iRcs -> iRcs.getOutgoingDeliverySeenTimestamp(mRcsOutgoingMessageId, mRecipientId));
+    }
+
+    /**
+     * Sets the status of this outgoing delivery and persists into storage.
+     *
+     * @param status The status of the associated {@link RcsMessage}s delivery to the associated
+     *               {@link RcsParticipant}
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
+     */
+    @WorkerThread
+    public void setStatus(@RcsMessage.RcsMessageStatus int status) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryStatus(
+                mRcsOutgoingMessageId, mRecipientId, status));
+    }
+
+    /**
+     * @return Returns the status of this outgoing delivery.
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @WorkerThread
+    public @RcsMessage.RcsMessageStatus int getStatus() throws RcsMessageStoreException {
+        return RcsControllerCall.call(
+                iRcs -> iRcs.getOutgoingDeliveryStatus(mRcsOutgoingMessageId, mRecipientId));
+    }
+
+    /**
+     * @return Returns the recipient associated with this delivery.
+     */
+    @NonNull
+    public RcsParticipant getRecipient() {
+        return new RcsParticipant(mRecipientId);
+    }
+
+    /**
+     * @return Returns the {@link RcsOutgoingMessage} associated with this delivery.
+     */
+    @NonNull
+    public RcsOutgoingMessage getMessage() {
+        return new RcsOutgoingMessage(mRcsOutgoingMessageId);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.aidl b/telephony/java/android/telephony/ims/RcsParticipant.aidl
deleted file mode 100644
index 1c44363..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipant.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsParticipant;
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
index f678ec7..7ba5d8e 100644
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -15,33 +15,15 @@
  */
 package android.telephony.ims;
 
-import static android.telephony.ims.RcsMessageStore.TAG;
-
-import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.WorkerThread;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.Rlog;
-import android.telephony.ims.aidl.IRcs;
-import android.text.TextUtils;
-
-import com.android.internal.util.Preconditions;
 
 /**
  * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
- * @hide - TODO(sahinc) make this public
  */
-public class RcsParticipant implements Parcelable {
+public class RcsParticipant {
     // The row ID of this participant in the database
     private int mId;
-    // The phone number of this participant
-    private String mCanonicalAddress;
-    // The RCS alias of this participant. This is different than the name of the contact in the
-    // Contacts app - i.e. RCS protocol allows users to define aliases for themselves that doesn't
-    // require other users to add them as contacts and give them a name.
-    private String mAlias;
 
     /**
      * Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
@@ -49,68 +31,87 @@
      *
      * @hide
      */
-    public RcsParticipant(int id, @NonNull String canonicalAddress) {
+    public RcsParticipant(int id) {
         mId = id;
-        mCanonicalAddress = canonicalAddress;
     }
 
     /**
-     * @return Returns the canonical address (i.e. normalized phone number) for this participant
+     * @return Returns the canonical address (i.e. normalized phone number) for this
+     * {@link RcsParticipant}
+     * @throws RcsMessageStoreException if the value could not be read from the storage
      */
-    public String getCanonicalAddress() {
-        return mCanonicalAddress;
+    @Nullable
+    @WorkerThread
+    public String getCanonicalAddress() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantCanonicalAddress(mId));
     }
 
     /**
-     * Sets the canonical address for this participant and updates it in storage.
-     * @param canonicalAddress the canonical address to update to.
+     * @return Returns the alias for this {@link RcsParticipant}. Alias is usually the real name of
+     * the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal Profile Service
+     * Definition Document)
+     * @throws RcsMessageStoreException if the value could not be read from the storage
+     */
+    @Nullable
+    @WorkerThread
+    public String getAlias() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantAlias(mId));
+    }
+
+    /**
+     * Sets the alias for this {@link RcsParticipant} and persists it in storage. Alias is usually
+     * the real name of the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal
+     * Profile Service Definition Document)
+     *
+     * @param alias The alias to set to.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
      */
     @WorkerThread
-    public void setCanonicalAddress(@NonNull String canonicalAddress) {
-        Preconditions.checkNotNull(canonicalAddress);
-        if (canonicalAddress.equals(mCanonicalAddress)) {
-            return;
-        }
-
-        mCanonicalAddress = canonicalAddress;
-
-        try {
-            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
-            if (iRcs != null) {
-                iRcs.updateRcsParticipantCanonicalAddress(mId, mCanonicalAddress);
-            }
-        } catch (RemoteException re) {
-            Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
-        }
+    public void setAlias(String alias) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantAlias(mId, alias));
     }
 
     /**
-     * @return Returns the alias for this participant. Alias is usually the real name of the person
-     * themselves.
+     * @return Returns the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
+     * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
+     * API 1.6 Specification)
+     * @throws RcsMessageStoreException if the value could not be read from the storage
      */
-    public String getAlias() {
-        return mAlias;
+    @Nullable
+    @WorkerThread
+    public String getContactId() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantContactId(mId));
     }
 
     /**
-     * Sets the alias for this participant and persists it in storage. Alias is usually the real
-     * name of the person themselves.
+     * Sets the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
+     * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
+     * API 1.6 Specification)
+     *
+     * @param contactId The contact ID to set to.
+     * @throws RcsMessageStoreException if the value could not be persisted into storage
      */
     @WorkerThread
-    public void setAlias(String alias) {
-        if (TextUtils.equals(mAlias, alias)) {
-            return;
-        }
-        mAlias = alias;
+    public void setContactId(String contactId) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantContactId(mId, contactId));
+    }
 
-        try {
-            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
-            if (iRcs != null) {
-                iRcs.updateRcsParticipantAlias(mId, mAlias);
-            }
-        } catch (RemoteException re) {
-            Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
         }
+        if (!(obj instanceof RcsParticipant)) {
+            return false;
+        }
+        RcsParticipant other = (RcsParticipant) obj;
+
+        return mId == other.mId;
+    }
+
+    @Override
+    public int hashCode() {
+        return mId;
     }
 
     /**
@@ -121,34 +122,4 @@
     public int getId() {
         return mId;
     }
-
-    public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() {
-        @Override
-        public RcsParticipant createFromParcel(Parcel in) {
-            return new RcsParticipant(in);
-        }
-
-        @Override
-        public RcsParticipant[] newArray(int size) {
-            return new RcsParticipant[size];
-        }
-    };
-
-    protected RcsParticipant(Parcel in) {
-        mId = in.readInt();
-        mCanonicalAddress = in.readString();
-        mAlias = in.readString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mId);
-        dest.writeString(mCanonicalAddress);
-        dest.writeString(mAlias);
-    }
 }
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
index b9ca5a8..c9a2b0d 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
@@ -15,27 +15,92 @@
  */
 package android.telephony.ims;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 /**
- * An event that indicates an {@link RcsParticipant}'s alias was changed.
- * @hide - TODO(sahinc) make this public
+ * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA
+ * RCC.71 (RCS Universal Profile Service Definition Document)
  */
-public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent {
+public final class RcsParticipantAliasChangedEvent extends RcsEvent implements Parcelable {
+    // The ID of the participant that changed their alias
+    private final int mParticipantId;
+    // The new alias of the above participant
+    private final String mNewAlias;
+
+    /**
+     * Creates a new {@link RcsParticipantAliasChangedEvent}. This event is not persisted into
+     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+     *
+     * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+     *                  midnight, January 1st, 1970 UTC
+     * @param participant The {@link RcsParticipant} that got their alias changed
+     * @param newAlias The new alias the {@link RcsParticipant} has.
+     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+     */
+    public RcsParticipantAliasChangedEvent(long timestamp, @NonNull RcsParticipant participant,
+            @Nullable String newAlias) {
+        super(timestamp);
+        mParticipantId = participant.getId();
+        mNewAlias = newAlias;
+    }
+
+    /**
+     * @hide - internal constructor for queries
+     */
+    public RcsParticipantAliasChangedEvent(long timestamp, int participantId,
+            @Nullable String newAlias) {
+        super(timestamp);
+        mParticipantId = participantId;
+        mNewAlias = newAlias;
+    }
+
+    /**
+     * @return Returns the {@link RcsParticipant} whose alias was changed.
+     */
+    @NonNull
+    public RcsParticipant getParticipantId() {
+        return new RcsParticipant(mParticipantId);
+    }
+
+    /**
+     * @return Returns the alias of the associated {@link RcsParticipant} after this event happened
+     */
+    @Nullable
+    public String getNewAlias() {
+        return mNewAlias;
+    }
+
+    /**
+     * Persists the event to the data store.
+     *
+     * @hide - not meant for public use.
+     */
+    @Override
+    public void persist() throws RcsMessageStoreException {
+        RcsControllerCall.call(iRcs -> iRcs.createParticipantAliasChangedEvent(
+                getTimestamp(), getParticipantId().getId(), getNewAlias()));
+    }
+
     public static final Creator<RcsParticipantAliasChangedEvent> CREATOR =
             new Creator<RcsParticipantAliasChangedEvent>() {
-        @Override
-        public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) {
-            return new RcsParticipantAliasChangedEvent(in);
-        }
+                @Override
+                public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) {
+                    return new RcsParticipantAliasChangedEvent(in);
+                }
 
-        @Override
-        public RcsParticipantAliasChangedEvent[] newArray(int size) {
-            return new RcsParticipantAliasChangedEvent[size];
-        }
-    };
+                @Override
+                public RcsParticipantAliasChangedEvent[] newArray(int size) {
+                    return new RcsParticipantAliasChangedEvent[size];
+                }
+            };
 
-    protected RcsParticipantAliasChangedEvent(Parcel in) {
+    private RcsParticipantAliasChangedEvent(Parcel in) {
+        super(in);
+        mNewAlias = in.readString();
+        mParticipantId = in.readInt();
     }
 
     @Override
@@ -45,5 +110,8 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mNewAlias);
+        dest.writeInt(mParticipantId);
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl b/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
deleted file mode 100644
index c0a7789..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsParticipantEvent;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.java b/telephony/java/android/telephony/ims/RcsParticipantEvent.java
deleted file mode 100644
index 371b8b7..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.os.Parcelable;
-
-/**
- * An event that is associated with an {@link RcsParticipant}
- * @hide - TODO(sahinc) make this public
- */
-public abstract class RcsParticipantEvent implements Parcelable {
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl
index 82d985d..b7c0f93 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsParticipantQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
new file mode 100644
index 0000000..d24d079
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
@@ -0,0 +1,308 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * The parameters to pass into
+ * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a
+ * subset of {@link RcsThread}s present in the message store.
+ */
+public final class RcsParticipantQueryParams implements Parcelable {
+    /**
+     * Flag to set with {@link Builder#setSortProperty(int)} to sort the results in the order of
+     * creation time for faster query results
+     */
+    public static final int SORT_BY_CREATION_ORDER = 0;
+
+    /**
+     * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
+     * {@link RcsParticipant} aliases
+     */
+    public static final int SORT_BY_ALIAS = 1;
+
+    /**
+     * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
+     * {@link RcsParticipant} canonical addresses
+     */
+    public static final int SORT_BY_CANONICAL_ADDRESS = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_ALIAS, SORT_BY_CANONICAL_ADDRESS})
+    public @interface SortingProperty {
+    }
+
+    // The SQL "like" statement to filter against participant aliases
+    private String mAliasLike;
+    // The SQL "like" statement to filter against canonical addresses
+    private String mCanonicalAddressLike;
+    // The property to sort the result against
+    private @SortingProperty int mSortingProperty;
+    // Whether to sort the result in ascending order
+    private boolean mIsAscending;
+    // The number of results to be returned from the query
+    private int mLimit;
+    // Used to limit the results to participants of a single thread
+    private int mThreadId;
+
+    /**
+     * @hide
+     */
+    public static final String PARTICIPANT_QUERY_PARAMETERS_KEY = "participant_query_parameters";
+
+    RcsParticipantQueryParams(int rcsThreadId, String aliasLike, String canonicalAddressLike,
+            @SortingProperty int sortingProperty, boolean isAscending,
+            int limit) {
+        mThreadId = rcsThreadId;
+        mAliasLike = aliasLike;
+        mCanonicalAddressLike = canonicalAddressLike;
+        mSortingProperty = sortingProperty;
+        mIsAscending = isAscending;
+        mLimit = limit;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * the thread that the result query should be limited to.
+     *
+     * As we do not expose any sort of integer ID's to public usage, this should be hidden.
+     *
+     * @hide - not meant for public use
+     */
+    public int getThreadId() {
+        return mThreadId;
+    }
+
+    /**
+     * @return Returns the SQL-inspired "LIKE" clause that will be used to match
+     * {@link RcsParticipant}s with respect to their aliases
+     *
+     * @see RcsParticipant#getAlias()
+     */
+    public String getAliasLike() {
+        return mAliasLike;
+    }
+
+    /**
+     * @return Returns the SQL-inspired "LIKE" clause that will be used to match
+     * {@link RcsParticipant}s with respect to their canonical addresses.
+     *
+     * @see RcsParticipant#getCanonicalAddress()
+     */
+    public String getCanonicalAddressLike() {
+        return mCanonicalAddressLike;
+    }
+
+    /**
+     * @return Returns the number of {@link RcsParticipant}s to be returned from the query. A value
+     * of 0 means there is no set limit.
+     */
+    public int getLimit() {
+        return mLimit;
+    }
+
+    /**
+     * @return Returns the property that will be used to sort the result against.
+     * @see SortingProperty
+     */
+    public int getSortingProperty() {
+        return mSortingProperty;
+    }
+
+    /**
+     * @return Returns {@code true} if the result set will be sorted in ascending order,
+     * {@code false} if it will be sorted in descending order.
+     */
+    public boolean getSortDirection() {
+        return mIsAscending;
+    }
+
+    /**
+     * A helper class to build the {@link RcsParticipantQueryParams}.
+     */
+    public static class Builder {
+        private String mAliasLike;
+        private String mCanonicalAddressLike;
+        private @SortingProperty int mSortingProperty;
+        private boolean mIsAscending;
+        private int mLimit = 100;
+        private int mThreadId;
+
+        /**
+         * Creates a new builder for {@link RcsParticipantQueryParams} to be used in
+         * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
+         */
+        public Builder() {
+            // empty implementation
+        }
+
+        /**
+         * Limits the resulting {@link RcsParticipant}s to only the given {@link RcsThread}
+         *
+         * @param rcsThread The thread that the participants should be searched in.
+         * @return The same {@link Builder} to chain methods.
+         */
+        @CheckResult
+        public Builder setThread(RcsThread rcsThread) {
+            mThreadId = rcsThread.getThreadId();
+            return this;
+        }
+
+        /**
+         * Sets an SQL-inspired "like" clause to match with participant aliases. Using a percent
+         * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
+         * ('_') wildcard matches any single character. Not using any wildcards would only perform a
+         * string match.The input string is case-insensitive.
+         *
+         * The input "An%e" would match {@link RcsParticipant}s with names Anne, Annie, Antonie,
+         * while the input "An_e" would only match Anne.
+         *
+         * @param likeClause The like clause to use for matching {@link RcsParticipant} aliases.
+         * @return The same {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setAliasLike(String likeClause) {
+            mAliasLike = likeClause;
+            return this;
+        }
+
+        /**
+         * Sets an SQL-inspired "like" clause to match with participant addresses. Using a percent
+         * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
+         * ('_') wildcard matches any single character. Not using any wildcards would only perform a
+         * string match. The input string is case-insensitive.
+         *
+         * The input "+999%111" would match {@link RcsParticipant}s with addresses like "+9995111"
+         * or "+99955555111", while the input "+999_111" would only match "+9995111".
+         *
+         * @param likeClause The like clause to use for matching {@link RcsParticipant} canonical
+         *                   addresses.
+         * @return The same {@link Builder} to chain methods
+         */
+        @CheckResult
+        public Builder setCanonicalAddressLike(String likeClause) {
+            mCanonicalAddressLike = likeClause;
+            return this;
+        }
+
+        /**
+         * Desired number of threads to be returned from the query. Passing in 0 will return all
+         * existing threads at once. The limit defaults to 100.
+         *
+         * @param limit The number to limit the query result to.
+         * @return The same instance of the builder to chain parameters.
+         * @throws InvalidParameterException If the given limit is negative.
+         */
+        @CheckResult
+        public Builder setResultLimit(@IntRange(from = 0) int limit)
+                throws InvalidParameterException {
+            if (limit < 0) {
+                throw new InvalidParameterException("The query limit must be non-negative");
+            }
+
+            mLimit = limit;
+            return this;
+        }
+
+        /**
+         * Sets the property where the results should be sorted against. Defaults to
+         * {@link RcsParticipantQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
+         *
+         * @param sortingProperty against which property the results should be sorted
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setSortProperty(@SortingProperty int sortingProperty) {
+            mSortingProperty = sortingProperty;
+            return this;
+        }
+
+        /**
+         * Sets whether the results should be sorted ascending or descending
+         *
+         * @param isAscending whether the results should be sorted ascending
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setSortDirection(boolean isAscending) {
+            mIsAscending = isAscending;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RcsParticipantQueryParams} to use in
+         * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
+         *
+         * @return An instance of {@link RcsParticipantQueryParams} to use with the participant
+         * query.
+         */
+        public RcsParticipantQueryParams build() {
+            return new RcsParticipantQueryParams(mThreadId, mAliasLike, mCanonicalAddressLike,
+                    mSortingProperty, mIsAscending, mLimit);
+        }
+    }
+
+    /**
+     * Parcelable boilerplate below.
+     */
+    private RcsParticipantQueryParams(Parcel in) {
+        mAliasLike = in.readString();
+        mCanonicalAddressLike = in.readString();
+        mSortingProperty = in.readInt();
+        mIsAscending = in.readByte() == 1;
+        mLimit = in.readInt();
+        mThreadId = in.readInt();
+    }
+
+    public static final Creator<RcsParticipantQueryParams> CREATOR =
+            new Creator<RcsParticipantQueryParams>() {
+                @Override
+                public RcsParticipantQueryParams createFromParcel(Parcel in) {
+                    return new RcsParticipantQueryParams(in);
+                }
+
+                @Override
+                public RcsParticipantQueryParams[] newArray(int size) {
+                    return new RcsParticipantQueryParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mAliasLike);
+        dest.writeString(mCanonicalAddressLike);
+        dest.writeInt(mSortingProperty);
+        dest.writeByte((byte) (mIsAscending ? 1 : 0));
+        dest.writeInt(mLimit);
+        dest.writeInt(mThreadId);
+    }
+
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl
index 82d985d..db5c00c 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsParticipantQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
new file mode 100644
index 0000000..505f1a5
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
@@ -0,0 +1,103 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
+ * call. This class allows getting the token for querying the next batch of participants in order to
+ * prevent handling large amounts of data at once.
+ */
+public final class RcsParticipantQueryResult implements Parcelable {
+    // A token for the caller to continue their query for the next batch of results
+    private RcsQueryContinuationToken mContinuationToken;
+    // The list of participant IDs returned with this query
+    private List<Integer> mParticipants;
+
+    /**
+     * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+     * to create query results
+     *
+     * @hide
+     */
+    public RcsParticipantQueryResult(
+            RcsQueryContinuationToken continuationToken,
+            List<Integer> participants) {
+        mContinuationToken = continuationToken;
+        mParticipants = participants;
+    }
+
+    /**
+     * Returns a token to call
+     * {@link RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)}
+     * to get the next batch of {@link RcsParticipant}s.
+     */
+    @Nullable
+    public RcsQueryContinuationToken getContinuationToken() {
+        return mContinuationToken;
+    }
+
+    /**
+     * Returns all the {@link RcsParticipant}s in the current query result. Call {@link
+     * RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)} to get the next
+     * batch of {@link RcsParticipant}s.
+     */
+    @NonNull
+    public List<RcsParticipant> getParticipants() {
+        List<RcsParticipant> participantList = new ArrayList<>();
+        for (Integer participantId : mParticipants) {
+            participantList.add(new RcsParticipant(participantId));
+        }
+
+        return participantList;
+    }
+
+    private RcsParticipantQueryResult(Parcel in) {
+        mContinuationToken = in.readParcelable(
+                RcsQueryContinuationToken.class.getClassLoader());
+    }
+
+    public static final Creator<RcsParticipantQueryResult> CREATOR =
+            new Creator<RcsParticipantQueryResult>() {
+                @Override
+                public RcsParticipantQueryResult createFromParcel(Parcel in) {
+                    return new RcsParticipantQueryResult(in);
+                }
+
+                @Override
+                public RcsParticipantQueryResult[] newArray(int size) {
+                    return new RcsParticipantQueryResult[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mContinuationToken, flags);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
index 82d985d..319379a 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
new file mode 100644
index 0000000..08643de
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
@@ -0,0 +1,155 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A token for enabling continuation queries. Instances are acquired through
+ * {@code getContinuationToken} on result objects after initial query is done.
+ *
+ * @see RcsEventQueryResult#getContinuationToken()
+ * @see RcsMessageQueryResult#getContinuationToken()
+ * @see RcsParticipantQueryResult#getContinuationToken()
+ * @see RcsThreadQueryResult#getContinuationToken()
+ */
+public final class RcsQueryContinuationToken implements Parcelable {
+    /**
+     * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+     * {@link RcsEvent} queries
+     */
+    public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0;
+
+    /**
+     * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+     * {@link RcsMessage} queries
+     */
+    public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1;
+
+    /**
+     * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+     * {@link RcsParticipant} queries
+     */
+    public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2;
+
+    /**
+     * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+     * {@link RcsThread} queries
+     */
+    public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3;
+
+    /**
+     * @hide - not meant for public use
+     */
+    public static final String QUERY_CONTINUATION_TOKEN = "query_continuation_token";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({EVENT_QUERY_CONTINUATION_TOKEN_TYPE, MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE,
+        PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, THREAD_QUERY_CONTINUATION_TOKEN_TYPE})
+    public @interface ContinuationTokenType {}
+
+    // The type of query this token should allow to continue
+    private @ContinuationTokenType int mQueryType;
+    // The raw query string for the initial query
+    private final String mRawQuery;
+    // The number of results that is returned with each query
+    private final int mLimit;
+    // The offset value that this query should start the query from
+    private int mOffset;
+
+    /**
+     * @hide
+     */
+    public RcsQueryContinuationToken(@ContinuationTokenType int queryType, String rawQuery,
+            int limit, int offset) {
+        mQueryType = queryType;
+        mRawQuery = rawQuery;
+        mLimit = limit;
+        mOffset = offset;
+    }
+
+    /**
+     * Returns the original raw query used on {@link com.android.providers.telephony.RcsProvider}
+     * @hide
+     */
+    public String getRawQuery() {
+        return mRawQuery;
+    }
+
+    /**
+     * Returns which index this continuation query should start from
+     * @hide
+     */
+    public int getOffset() {
+        return mOffset;
+    }
+
+    /**
+     * Increments the offset by the amount of result rows returned with the continuation query for
+     * the next query.
+     * @hide
+     */
+    public void incrementOffset() {
+        mOffset += mLimit;
+    }
+
+    /**
+     * Returns the type of query that this {@link RcsQueryContinuationToken} is intended to be used
+     * to continue.
+     */
+    public @ContinuationTokenType int getQueryType() {
+        return mQueryType;
+    }
+
+    private RcsQueryContinuationToken(Parcel in) {
+        mQueryType = in.readInt();
+        mRawQuery = in.readString();
+        mLimit = in.readInt();
+        mOffset = in.readInt();
+    }
+
+    public static final Creator<RcsQueryContinuationToken> CREATOR =
+            new Creator<RcsQueryContinuationToken>() {
+                @Override
+                public RcsQueryContinuationToken createFromParcel(Parcel in) {
+                    return new RcsQueryContinuationToken(in);
+                }
+
+                @Override
+                public RcsQueryContinuationToken[] newArray(int size) {
+                    return new RcsQueryContinuationToken[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mQueryType);
+        dest.writeString(mRawQuery);
+        dest.writeInt(mLimit);
+        dest.writeInt(mOffset);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsTextPart.aidl b/telephony/java/android/telephony/ims/RcsTextPart.aidl
deleted file mode 100644
index 4f9fe1f..0000000
--- a/telephony/java/android/telephony/ims/RcsTextPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsTextPart;
diff --git a/telephony/java/android/telephony/ims/RcsTextPart.java b/telephony/java/android/telephony/ims/RcsTextPart.java
deleted file mode 100644
index 2a72df1..0000000
--- a/telephony/java/android/telephony/ims/RcsTextPart.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a string
- * @hide - TODO(sahinc) make this public
- */
-public class RcsTextPart extends RcsPart {
-    public static final Creator<RcsTextPart> CREATOR = new Creator<RcsTextPart>() {
-        @Override
-        public RcsTextPart createFromParcel(Parcel in) {
-            return new RcsTextPart(in);
-        }
-
-        @Override
-        public RcsTextPart[] newArray(int size) {
-            return new RcsTextPart[size];
-        }
-    };
-
-    protected RcsTextPart(Parcel in) {
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThread.aidl b/telephony/java/android/telephony/ims/RcsThread.aidl
deleted file mode 100644
index d9cf6db..0000000
--- a/telephony/java/android/telephony/ims/RcsThread.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony;
-
-parcelable RcsThread;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
index c0a0d94..e015dd3 100644
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -16,60 +16,118 @@
 
 package android.telephony.ims;
 
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_GROUP;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * RcsThread represents a single RCS conversation thread. It holds messages that were sent and
  * received and events that occurred on that thread.
- * @hide - TODO(sahinc) make this public
  */
-public abstract class RcsThread implements Parcelable {
-    // Since this is an abstract class that gets parcelled, the sub-classes need to write these
-    // magic values into the parcel so that we know which type to unparcel into.
-    protected static final int RCS_1_TO_1_TYPE = 998;
-    protected static final int RCS_GROUP_TYPE = 999;
-
+public abstract class RcsThread {
+    /**
+     * The rcs_participant_thread_id that represents this thread in the database
+     * @hide
+     */
     protected int mThreadId;
 
+    /**
+     * @hide
+     */
     protected RcsThread(int threadId) {
         mThreadId = threadId;
     }
 
-    protected RcsThread(Parcel in) {
-        mThreadId = in.readInt();
+    /**
+     * @return Returns the summary of the latest message in this {@link RcsThread} packaged in an
+     * {@link RcsMessageSnippet} object
+     */
+    @WorkerThread
+    @NonNull
+    public RcsMessageSnippet getSnippet() throws RcsMessageStoreException {
+        return RcsControllerCall.call(iRcs -> iRcs.getMessageSnippet(mThreadId));
     }
 
-    public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() {
-        @Override
-        public RcsThread createFromParcel(Parcel in) {
-            int type = in.readInt();
-
-            switch (type) {
-                case RCS_1_TO_1_TYPE:
-                    return new Rcs1To1Thread(in);
-                case RCS_GROUP_TYPE:
-                    return new RcsGroupThread(in);
-                default:
-                    Log.e(RcsMessageStore.TAG, "Cannot unparcel RcsThread, wrong type: " + type);
-            }
-            return null;
-        }
-
-        @Override
-        public RcsThread[] newArray(int size) {
-            return new RcsThread[0];
-        }
-    };
-
-    @Override
-    public int describeContents() {
-        return 0;
+    /**
+     * Adds a new {@link RcsIncomingMessage} to this RcsThread and persists it in storage.
+     *
+     * @throws RcsMessageStoreException if the message could not be persisted into storage.
+     */
+    @WorkerThread
+    @NonNull
+    public RcsIncomingMessage addIncomingMessage(
+            @NonNull RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams)
+            throws RcsMessageStoreException {
+        return new RcsIncomingMessage(RcsControllerCall.call(iRcs -> iRcs.addIncomingMessage(
+                mThreadId, rcsIncomingMessageCreationParams)));
     }
 
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mThreadId);
+    /**
+     * Adds a new {@link RcsOutgoingMessage} to this RcsThread and persists it in storage.
+     *
+     * @throws RcsMessageStoreException if the message could not be persisted into storage.
+     */
+    @WorkerThread
+    @NonNull
+    public RcsOutgoingMessage addOutgoingMessage(
+            @NonNull RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams)
+            throws RcsMessageStoreException {
+        int messageId = RcsControllerCall.call(iRcs -> iRcs.addOutgoingMessage(
+                mThreadId, rcsOutgoingMessageCreationParams));
+
+        return new RcsOutgoingMessage(messageId);
+    }
+
+    /**
+     * Deletes an {@link RcsMessage} from this RcsThread and updates the storage.
+     *
+     * @param rcsMessage The message to delete from the thread
+     * @throws RcsMessageStoreException if the message could not be deleted
+     */
+    @WorkerThread
+    public void deleteMessage(@NonNull RcsMessage rcsMessage) throws RcsMessageStoreException {
+        RcsControllerCall.callWithNoReturn(
+                iRcs -> iRcs.deleteMessage(rcsMessage.getId(), rcsMessage.isIncoming(), mThreadId,
+                        isGroup()));
+    }
+
+    /**
+     * Convenience function for loading all the {@link RcsMessage}s in this {@link RcsThread}. For
+     * a more detailed and paginated query, please use
+     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
+     *
+     * @return Loads the {@link RcsMessage}s in this thread and returns them in an immutable list.
+     * @throws RcsMessageStoreException if the messages could not be read from the storage
+     */
+    @WorkerThread
+    @NonNull
+    public RcsMessageQueryResult getMessages() throws RcsMessageStoreException {
+        RcsMessageQueryParams queryParameters =
+                new RcsMessageQueryParams.Builder().setThread(this).build();
+        return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters));
+    }
+
+    /**
+     * @return Returns whether this is a group thread or not
+     */
+    public abstract boolean isGroup();
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public int getThreadId() {
+        return mThreadId;
+    }
+
+    /**
+     * @hide
+     */
+    public int getThreadType() {
+        return isGroup() ? THREAD_TYPE_GROUP : THREAD_TYPE_1_TO_1;
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.java b/telephony/java/android/telephony/ims/RcsThreadEvent.java
deleted file mode 100644
index e10baab..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.os.Parcelable;
-
-/**
- * An event that happened on an {@link RcsThread}.
- * @hide - TODO(sahinc) make this public
- */
-public abstract class RcsThreadEvent implements Parcelable {
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
deleted file mode 100644
index b308fef..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s icon was changed.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadIconChangedEvent extends RcsThreadEvent {
-    public static final Creator<RcsThreadIconChangedEvent> CREATOR =
-            new Creator<RcsThreadIconChangedEvent>() {
-        @Override
-        public RcsThreadIconChangedEvent createFromParcel(Parcel in) {
-            return new RcsThreadIconChangedEvent(in);
-        }
-
-        @Override
-        public RcsThreadIconChangedEvent[] newArray(int size) {
-            return new RcsThreadIconChangedEvent[size];
-        }
-    };
-
-    protected RcsThreadIconChangedEvent(Parcel in) {
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
deleted file mode 100644
index 54a311d..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsThreadNameChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
deleted file mode 100644
index 6f5cfdf..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s name was changed.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadNameChangedEvent extends RcsThreadEvent {
-    public static final Creator<RcsThreadNameChangedEvent> CREATOR =
-            new Creator<RcsThreadNameChangedEvent>() {
-        @Override
-        public RcsThreadNameChangedEvent createFromParcel(Parcel in) {
-            return new RcsThreadNameChangedEvent(in);
-        }
-
-        @Override
-        public RcsThreadNameChangedEvent[] newArray(int size) {
-            return new RcsThreadNameChangedEvent[size];
-        }
-    };
-
-    protected RcsThreadNameChangedEvent(Parcel in) {
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
deleted file mode 100644
index 047a424..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsThreadParticipantJoinedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
deleted file mode 100644
index 5c4073c..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an RCS participant has joined an {@link RcsGroupThread}.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadParticipantJoinedEvent extends RcsThreadEvent {
-    public static final Creator<RcsThreadParticipantJoinedEvent> CREATOR =
-            new Creator<RcsThreadParticipantJoinedEvent>() {
-        @Override
-        public RcsThreadParticipantJoinedEvent createFromParcel(Parcel in) {
-            return new RcsThreadParticipantJoinedEvent(in);
-        }
-
-        @Override
-        public RcsThreadParticipantJoinedEvent[] newArray(int size) {
-            return new RcsThreadParticipantJoinedEvent[size];
-        }
-    };
-
-    protected RcsThreadParticipantJoinedEvent(Parcel in) {
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
deleted file mode 100644
index 52f9bbd..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsThreadParticipantLeftEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
deleted file mode 100644
index 4bf86b9..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an RCS participant has left an {@link RcsGroupThread}.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadParticipantLeftEvent extends RcsThreadEvent {
-    public static final Creator<RcsThreadParticipantLeftEvent> CREATOR =
-            new Creator<RcsThreadParticipantLeftEvent>() {
-        @Override
-        public RcsThreadParticipantLeftEvent createFromParcel(Parcel in) {
-            return new RcsThreadParticipantLeftEvent(in);
-        }
-
-        @Override
-        public RcsThreadParticipantLeftEvent[] newArray(int size) {
-            return new RcsThreadParticipantLeftEvent[size];
-        }
-    };
-
-    protected RcsThreadParticipantLeftEvent(Parcel in) {
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
deleted file mode 100644
index 7bcebfa..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.telephony.ims;
-
-parcelable RcsThreadQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
deleted file mode 100644
index 931e93d..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
+++ /dev/null
@@ -1,52 +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.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A continuation token to provide for {@link RcsMessageStore#getRcsThreads}. Use this token to
- * break large queries into manageable chunks
- * @hide - TODO make this public
- */
-public class RcsThreadQueryContinuationToken implements Parcelable {
-    protected RcsThreadQueryContinuationToken(Parcel in) {
-    }
-
-    public static final Creator<RcsThreadQueryContinuationToken> CREATOR =
-            new Creator<RcsThreadQueryContinuationToken>() {
-                @Override
-                public RcsThreadQueryContinuationToken createFromParcel(Parcel in) {
-                    return new RcsThreadQueryContinuationToken(in);
-                }
-
-                @Override
-                public RcsThreadQueryContinuationToken[] newArray(int size) {
-                    return new RcsThreadQueryContinuationToken[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
deleted file mode 100644
index feb2d4d..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.telephony.ims;
-
-parcelable RcsThreadQueryParameters;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
deleted file mode 100644
index f2c4ab1..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
+++ /dev/null
@@ -1,225 +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.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} in
- * order to select a subset of {@link RcsThread}s present in the message store.
- * @hide TODO - make the Builder and builder() public. The rest should stay internal only.
- */
-public class RcsThreadQueryParameters implements Parcelable {
-    private final boolean mIsGroup;
-    private final Set<RcsParticipant> mRcsParticipants;
-    private final int mLimit;
-    private final boolean mIsAscending;
-
-    RcsThreadQueryParameters(boolean isGroup, Set<RcsParticipant> participants, int limit,
-            boolean isAscending) {
-        mIsGroup = isGroup;
-        mRcsParticipants = participants;
-        mLimit = limit;
-        mIsAscending = isAscending;
-    }
-
-    /**
-     * Returns a new builder to build a query with.
-     * TODO - make public
-     */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    /**
-     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
-     * the list of participants.
-     * @hide
-     */
-    public Set<RcsParticipant> getRcsParticipants() {
-        return mRcsParticipants;
-    }
-
-    /**
-     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
-     * whether group threads should be queried
-     * @hide
-     */
-    public boolean isGroupThread() {
-        return mIsGroup;
-    }
-
-    /**
-     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
-     * the number of tuples the result query should be limited to.
-     */
-    public int getLimit() {
-        return mLimit;
-    }
-
-    /**
-     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
-     * determine the sort order.
-     */
-    public boolean isAscending() {
-        return mIsAscending;
-    }
-
-    /**
-     * A helper class to build the {@link RcsThreadQueryParameters}.
-     */
-    public static class Builder {
-        private boolean mIsGroupThread;
-        private Set<RcsParticipant> mParticipants;
-        private int mLimit = 100;
-        private boolean mIsAscending;
-
-        /**
-         * Package private constructor for {@link RcsThreadQueryParameters.Builder}. To obtain this,
-         * {@link RcsThreadQueryParameters#builder()} needs to be called.
-         */
-        Builder() {
-            mParticipants = new HashSet<>();
-        }
-
-        /**
-         * Limits the query to only return group threads.
-         * @param isGroupThread Whether to limit the query result to group threads.
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder isGroupThread(boolean isGroupThread) {
-            mIsGroupThread = isGroupThread;
-            return this;
-        }
-
-        /**
-         * Limits the query to only return threads that contain the given participant.
-         * @param participant The participant that must be included in all of the returned threads.
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder withParticipant(RcsParticipant participant) {
-            mParticipants.add(participant);
-            return this;
-        }
-
-        /**
-         * Limits the query to only return threads that contain the given list of participants.
-         * @param participants An iterable list of participants that must be included in all of the
-         *                     returned threads.
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder withParticipants(Iterable<RcsParticipant> participants) {
-            for (RcsParticipant participant : participants) {
-                mParticipants.add(participant);
-            }
-            return this;
-        }
-
-        /**
-         * Desired number of threads to be returned from the query. Passing in 0 will return all
-         * existing threads at once. The limit defaults to 100.
-         * @param limit The number to limit the query result to.
-         * @return The same instance of the builder to chain parameters.
-         * @throws InvalidParameterException If the given limit is negative.
-         */
-        @CheckResult
-        public Builder limitResultsTo(int limit) throws InvalidParameterException {
-            if (limit < 0) {
-                throw new InvalidParameterException("The query limit must be non-negative");
-            }
-
-            mLimit = limit;
-            return this;
-        }
-
-        /**
-         * Sorts the results returned from the query via thread IDs.
-         *
-         * TODO - add sorting support for other fields
-         *
-         * @param isAscending whether to sort in ascending order or not
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder sort(boolean isAscending) {
-            mIsAscending = isAscending;
-            return this;
-        }
-
-        /**
-         * Builds the {@link RcsThreadQueryParameters} to use in
-         * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)}
-         *
-         * @return An instance of {@link RcsThreadQueryParameters} to use with the thread query.
-         */
-        public RcsThreadQueryParameters build() {
-            return new RcsThreadQueryParameters(
-                    mIsGroupThread, mParticipants, mLimit, mIsAscending);
-        }
-    }
-
-    /**
-     * Parcelable boilerplate below.
-     */
-    protected RcsThreadQueryParameters(Parcel in) {
-        mIsGroup = in.readBoolean();
-
-        ArrayList<RcsParticipant> participantArrayList = new ArrayList<>();
-        in.readTypedList(participantArrayList, RcsParticipant.CREATOR);
-        mRcsParticipants = new HashSet<>(participantArrayList);
-
-        mLimit = in.readInt();
-        mIsAscending = in.readBoolean();
-    }
-
-    public static final Creator<RcsThreadQueryParameters> CREATOR =
-            new Creator<RcsThreadQueryParameters>() {
-                @Override
-                public RcsThreadQueryParameters createFromParcel(Parcel in) {
-                    return new RcsThreadQueryParameters(in);
-                }
-
-                @Override
-                public RcsThreadQueryParameters[] newArray(int size) {
-                    return new RcsThreadQueryParameters[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeBoolean(mIsGroup);
-        dest.writeTypedList(new ArrayList<>(mRcsParticipants));
-        dest.writeInt(mLimit);
-        dest.writeBoolean(mIsAscending);
-    }
-
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl
similarity index 94%
copy from telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
copy to telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl
index 82d985d..3f351dc 100644
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl
@@ -17,4 +17,4 @@
 
 package android.telephony.ims;
 
-parcelable RcsThreadIconChangedEvent;
+parcelable RcsThreadQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
new file mode 100644
index 0000000..05a5a39
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
@@ -0,0 +1,303 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in
+ * order to select a subset of {@link RcsThread}s present in the message store.
+ */
+public final class RcsThreadQueryParams implements Parcelable {
+    /**
+     * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
+     * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return
+     * {@link RcsGroupThread}s.
+     */
+    public static final int THREAD_TYPE_GROUP = 0x0001;
+
+    /**
+     * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
+     * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return
+     * {@link Rcs1To1Thread}s.
+     */
+    public static final int THREAD_TYPE_1_TO_1 = 0x0002;
+
+    // The type of threads to be filtered with the query
+    private final int mThreadType;
+    // The list of participants that are expected in the resulting threads
+    private final List<Integer> mRcsParticipantIds;
+    // The number of RcsThread's that should be returned with this query
+    private final int mLimit;
+    // The property which the result of the query should be sorted against
+    private final @SortingProperty int mSortingProperty;
+    // Whether the sorting should be done in ascending
+    private final boolean mIsAscending;
+
+    /**
+     * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+     * be sorted in the order of {@link RcsThread} creation time for faster results.
+     */
+    public static final int SORT_BY_CREATION_ORDER = 0;
+
+    /**
+     * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+     * be sorted according to the timestamp of {@link RcsThread#getSnippet()}
+     */
+    public static final int SORT_BY_TIMESTAMP = 1;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
+    public @interface SortingProperty {
+    }
+
+    /**
+     * @hide
+     */
+    public static final String THREAD_QUERY_PARAMETERS_KEY = "thread_query_parameters";
+
+    RcsThreadQueryParams(int threadType, Set<RcsParticipant> participants,
+            int limit, int sortingProperty, boolean isAscending) {
+        mThreadType = threadType;
+        mRcsParticipantIds = convertParticipantSetToIdList(participants);
+        mLimit = limit;
+        mSortingProperty = sortingProperty;
+        mIsAscending = isAscending;
+    }
+
+    private static List<Integer> convertParticipantSetToIdList(Set<RcsParticipant> participants) {
+        List<Integer> ids = new ArrayList<>(participants.size());
+        for (RcsParticipant participant : participants) {
+            ids.add(participant.getId());
+        }
+        return ids;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * the list of participant IDs.
+     *
+     * As we don't expose any integer ID's to API users, this should stay hidden
+     *
+     * @hide - not meant for public use
+     */
+    public List<Integer> getRcsParticipantsIds() {
+        return Collections.unmodifiableList(mRcsParticipantIds);
+    }
+
+    /**
+     * @return Returns the bitmask flag for types of {@link RcsThread}s that this query should
+     * return.
+     */
+    public int getThreadType() {
+        return mThreadType;
+    }
+
+    /**
+     * @return Returns the number of {@link RcsThread}s to be returned from the query. A value
+     * of 0 means there is no set limit.
+     */
+    public int getLimit() {
+        return mLimit;
+    }
+
+    /**
+     * @return Returns the property that will be used to sort the result against.
+     * @see SortingProperty
+     */
+    public @SortingProperty int getSortingProperty() {
+        return mSortingProperty;
+    }
+
+    /**
+     * @return Returns {@code true} if the result set will be sorted in ascending order,
+     * {@code false} if it will be sorted in descending order.
+     */
+    public boolean getSortDirection() {
+        return mIsAscending;
+    }
+
+    /**
+     * A helper class to build the {@link RcsThreadQueryParams}.
+     */
+    public static class Builder {
+        private int mThreadType;
+        private Set<RcsParticipant> mParticipants;
+        private int mLimit = 100;
+        private @SortingProperty int mSortingProperty;
+        private boolean mIsAscending;
+
+        /**
+         * Constructs a {@link RcsThreadQueryParams.Builder} to help build an
+         * {@link RcsThreadQueryParams}
+         */
+        public Builder() {
+            mParticipants = new HashSet<>();
+        }
+
+        /**
+         * Limits the query to only return group threads.
+         *
+         * @param threadType Whether to limit the query result to group threads.
+         * @return The same instance of the builder to chain parameters.
+         * @see RcsThreadQueryParams#THREAD_TYPE_GROUP
+         * @see RcsThreadQueryParams#THREAD_TYPE_1_TO_1
+         */
+        @CheckResult
+        public Builder setThreadType(int threadType) {
+            mThreadType = threadType;
+            return this;
+        }
+
+        /**
+         * Limits the query to only return threads that contain the given participant. If this
+         * property was not set, participants will not be taken into account while querying for
+         * threads.
+         *
+         * @param participant The participant that must be included in all of the returned threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setParticipant(@NonNull RcsParticipant participant) {
+            mParticipants.add(participant);
+            return this;
+        }
+
+        /**
+         * Limits the query to only return threads that contain the given list of participants. If
+         * this property was not set, participants will not be taken into account while querying
+         * for threads.
+         *
+         * @param participants An iterable list of participants that must be included in all of the
+         *                     returned threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setParticipants(@NonNull List<RcsParticipant> participants) {
+            mParticipants.addAll(participants);
+            return this;
+        }
+
+        /**
+         * Desired number of threads to be returned from the query. Passing in 0 will return all
+         * existing threads at once. The limit defaults to 100.
+         *
+         * @param limit The number to limit the query result to.
+         * @return The same instance of the builder to chain parameters.
+         * @throws InvalidParameterException If the given limit is negative.
+         */
+        @CheckResult
+        public Builder setResultLimit(@IntRange(from = 0) int limit)
+                throws InvalidParameterException {
+            if (limit < 0) {
+                throw new InvalidParameterException("The query limit must be non-negative");
+            }
+
+            mLimit = limit;
+            return this;
+        }
+
+        /**
+         * Sets the property where the results should be sorted against. Defaults to
+         * {@link SortingProperty#SORT_BY_CREATION_ORDER}
+         *
+         * @param sortingProperty whether to sort in ascending order or not
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setSortProperty(@SortingProperty int sortingProperty) {
+            mSortingProperty = sortingProperty;
+            return this;
+        }
+
+        /**
+         * Sets whether the results should be sorted ascending or descending
+         *
+         * @param isAscending whether the results should be sorted ascending
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder setSortDirection(boolean isAscending) {
+            mIsAscending = isAscending;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RcsThreadQueryParams} to use in
+         * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
+         *
+         * @return An instance of {@link RcsThreadQueryParams} to use with the thread query.
+         */
+        public RcsThreadQueryParams build() {
+            return new RcsThreadQueryParams(mThreadType, mParticipants, mLimit,
+                    mSortingProperty, mIsAscending);
+        }
+    }
+
+    /**
+     * Parcelable boilerplate below.
+     */
+    private RcsThreadQueryParams(Parcel in) {
+        mThreadType = in.readInt();
+        mRcsParticipantIds = new ArrayList<>();
+        in.readList(mRcsParticipantIds, Integer.class.getClassLoader());
+        mLimit = in.readInt();
+        mSortingProperty = in.readInt();
+        mIsAscending = in.readByte() == 1;
+    }
+
+    public static final Creator<RcsThreadQueryParams> CREATOR =
+            new Creator<RcsThreadQueryParams>() {
+                @Override
+                public RcsThreadQueryParams createFromParcel(Parcel in) {
+                    return new RcsThreadQueryParams(in);
+                }
+
+                @Override
+                public RcsThreadQueryParams[] newArray(int size) {
+                    return new RcsThreadQueryParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mThreadType);
+        dest.writeList(mRcsParticipantIds);
+        dest.writeInt(mLimit);
+        dest.writeInt(mSortingProperty);
+        dest.writeByte((byte) (mIsAscending ? 1 : 0));
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
index 4b06529..b1d5cf4 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
@@ -1,19 +1,19 @@
 /*
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ *
+ * 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.telephony.ims;
 
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
index 47715f8..1cac61d 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 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.
@@ -16,22 +16,28 @@
 
 package android.telephony.ims;
 
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.ims.RcsTypeIdPair;
+
+import java.util.ArrayList;
 import java.util.List;
 
 /**
- * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken,
- * RcsThreadQueryParameters)}
+ * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
  * call. This class allows getting the token for querying the next batch of threads in order to
  * prevent handling large amounts of data at once.
- *
- * @hide
  */
-public class RcsThreadQueryResult implements Parcelable {
-    private RcsThreadQueryContinuationToken mContinuationToken;
-    private List<RcsThread> mRcsThreads;
+public final class RcsThreadQueryResult implements Parcelable {
+    // A token for the caller to continue their query for the next batch of results
+    private RcsQueryContinuationToken mContinuationToken;
+    // The list of thread IDs returned with this query
+    private List<RcsTypeIdPair> mRcsThreadIds;
 
     /**
      * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
@@ -40,31 +46,47 @@
      * @hide
      */
     public RcsThreadQueryResult(
-            RcsThreadQueryContinuationToken continuationToken, List<RcsThread> rcsThreads) {
+            RcsQueryContinuationToken continuationToken,
+            List<RcsTypeIdPair> rcsThreadIds) {
         mContinuationToken = continuationToken;
-        mRcsThreads = rcsThreads;
+        mRcsThreadIds = rcsThreadIds;
     }
 
     /**
      * Returns a token to call
-     * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)}
+     * {@link RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)}
      * to get the next batch of {@link RcsThread}s.
      */
-    public RcsThreadQueryContinuationToken nextChunkToken() {
+    @Nullable
+    public RcsQueryContinuationToken getContinuationToken() {
         return mContinuationToken;
     }
 
     /**
      * Returns all the RcsThreads in the current query result. Call {@link
-     * RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} to get the next batch of
+     * RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)} to get the next batch of
      * {@link RcsThread}s.
      */
+    @NonNull
     public List<RcsThread> getThreads() {
-        return mRcsThreads;
+        List<RcsThread> rcsThreads = new ArrayList<>();
+
+        for (RcsTypeIdPair typeIdPair : mRcsThreadIds) {
+            if (typeIdPair.getType() == THREAD_TYPE_1_TO_1) {
+                rcsThreads.add(new Rcs1To1Thread(typeIdPair.getId()));
+            } else {
+                rcsThreads.add(new RcsGroupThread(typeIdPair.getId()));
+            }
+        }
+
+        return rcsThreads;
     }
 
-    protected RcsThreadQueryResult(Parcel in) {
-        // TODO - implement
+    private RcsThreadQueryResult(Parcel in) {
+        mContinuationToken = in.readParcelable(
+            RcsQueryContinuationToken.class.getClassLoader());
+        mRcsThreadIds = new ArrayList<>();
+        in.readList(mRcsThreadIds, Integer.class.getClassLoader());
     }
 
     public static final Creator<RcsThreadQueryResult> CREATOR =
@@ -87,6 +109,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        // TODO - implement
+        dest.writeParcelable(mContinuationToken, flags);
+        dest.writeList(mRcsThreadIds);
     }
 }
diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
index 0c958ba..2478f8c 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
@@ -16,10 +16,19 @@
 
 package android.telephony.ims.aidl;
 
-import android.telephony.ims.RcsParticipant;
-import android.telephony.ims.Rcs1To1Thread;
-import android.telephony.ims.RcsThreadQueryContinuationToken;
-import android.telephony.ims.RcsThreadQueryParameters;
+import android.net.Uri;
+import android.telephony.ims.RcsEventQueryParams;
+import android.telephony.ims.RcsEventQueryResult;
+import android.telephony.ims.RcsFileTransferCreationParams;
+import android.telephony.ims.RcsIncomingMessageCreationParams;
+import android.telephony.ims.RcsMessageSnippet;
+import android.telephony.ims.RcsMessageQueryParams;
+import android.telephony.ims.RcsMessageQueryResult;
+import android.telephony.ims.RcsOutgoingMessageCreationParams;
+import android.telephony.ims.RcsParticipantQueryParams;
+import android.telephony.ims.RcsParticipantQueryResult;
+import android.telephony.ims.RcsQueryContinuationToken;
+import android.telephony.ims.RcsThreadQueryParams;
 import android.telephony.ims.RcsThreadQueryResult;
 
 /**
@@ -27,23 +36,231 @@
  * {@hide}
  */
 interface IRcs {
+    /////////////////////////
     // RcsMessageStore APIs
-    RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParameters queryParameters);
+    /////////////////////////
+    RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParams queryParams);
 
     RcsThreadQueryResult getRcsThreadsWithToken(
-        in RcsThreadQueryContinuationToken continuationToken);
+        in RcsQueryContinuationToken continuationToken);
 
-    void deleteThread(int threadId);
+    RcsParticipantQueryResult getParticipants(in RcsParticipantQueryParams queryParams);
 
-    Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant);
+    RcsParticipantQueryResult getParticipantsWithToken(
+        in RcsQueryContinuationToken continuationToken);
 
+    RcsMessageQueryResult getMessages(in RcsMessageQueryParams queryParams);
+
+    RcsMessageQueryResult getMessagesWithToken(
+        in RcsQueryContinuationToken continuationToken);
+
+    RcsEventQueryResult getEvents(in RcsEventQueryParams queryParams);
+
+    RcsEventQueryResult getEventsWithToken(
+        in RcsQueryContinuationToken continuationToken);
+
+    // returns true if the thread was successfully deleted
+    boolean deleteThread(int threadId, int threadType);
+
+    // Creates an Rcs1To1Thread and returns its row ID
+    int createRcs1To1Thread(int participantId);
+
+    // Creates an RcsGroupThread and returns its row ID
+    int createGroupThread(in int[] participantIds, String groupName, in Uri groupIcon);
+
+    /////////////////////////
     // RcsThread APIs
-    int getMessageCount(int rcsThreadId);
+    /////////////////////////
 
+    // Creates a new RcsIncomingMessage on the given thread and returns its row ID
+    int addIncomingMessage(int rcsThreadId,
+            in RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams);
+
+    // Creates a new RcsOutgoingMessage on the given thread and returns its row ID
+    int addOutgoingMessage(int rcsThreadId,
+            in RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams);
+
+    // TODO: modify RcsProvider URI's to allow deleting a message without specifying its thread
+    void deleteMessage(int rcsMessageId, boolean isIncoming, int rcsThreadId, boolean isGroup);
+
+    RcsMessageSnippet getMessageSnippet(int rcsThreadId);
+
+    /////////////////////////
+    // Rcs1To1Thread APIs
+    /////////////////////////
+    void set1To1ThreadFallbackThreadId(int rcsThreadId, long fallbackId);
+
+    long get1To1ThreadFallbackThreadId(int rcsThreadId);
+
+    int get1To1ThreadOtherParticipantId(int rcsThreadId);
+
+    /////////////////////////
+    // RcsGroupThread APIs
+    /////////////////////////
+    void setGroupThreadName(int rcsThreadId, String groupName);
+
+    String getGroupThreadName(int rcsThreadId);
+
+    void setGroupThreadIcon(int rcsThreadId, in Uri groupIcon);
+
+    Uri getGroupThreadIcon(int rcsThreadId);
+
+    void setGroupThreadOwner(int rcsThreadId, int participantId);
+
+    int getGroupThreadOwner(int rcsThreadId);
+
+    void setGroupThreadConferenceUri(int rcsThreadId, in Uri conferenceUri);
+
+    Uri getGroupThreadConferenceUri(int rcsThreadId);
+
+    void addParticipantToGroupThread(int rcsThreadId, int participantId);
+
+    void removeParticipantFromGroupThread(int rcsThreadId, int participantId);
+
+    /////////////////////////
     // RcsParticipant APIs
-    RcsParticipant createRcsParticipant(String canonicalAddress);
+    /////////////////////////
 
-    void updateRcsParticipantCanonicalAddress(int id, String canonicalAddress);
+    // Creates a new RcsParticipant and returns its rowId
+    int createRcsParticipant(String canonicalAddress, String alias);
 
-    void updateRcsParticipantAlias(int id, String alias);
+    String getRcsParticipantCanonicalAddress(int participantId);
+
+    String getRcsParticipantAlias(int participantId);
+
+    void setRcsParticipantAlias(int id, String alias);
+
+    String getRcsParticipantContactId(int participantId);
+
+    void setRcsParticipantContactId(int participantId, String contactId);
+
+    /////////////////////////
+    // RcsMessage APIs
+    /////////////////////////
+    void setMessageSubId(int messageId, boolean isIncoming, int subId);
+
+    int getMessageSubId(int messageId, boolean isIncoming);
+
+    void setMessageStatus(int messageId, boolean isIncoming, int status);
+
+    int getMessageStatus(int messageId, boolean isIncoming);
+
+    void setMessageOriginationTimestamp(int messageId, boolean isIncoming, long originationTimestamp);
+
+    long getMessageOriginationTimestamp(int messageId, boolean isIncoming);
+
+    void setGlobalMessageIdForMessage(int messageId, boolean isIncoming, String globalId);
+
+    String getGlobalMessageIdForMessage(int messageId, boolean isIncoming);
+
+    void setMessageArrivalTimestamp(int messageId, boolean isIncoming, long arrivalTimestamp);
+
+    long getMessageArrivalTimestamp(int messageId, boolean isIncoming);
+
+    void setMessageSeenTimestamp(int messageId, boolean isIncoming, long seenTimestamp);
+
+    long getMessageSeenTimestamp(int messageId, boolean isIncoming);
+
+    void setTextForMessage(int messageId, boolean isIncoming, String text);
+
+    String getTextForMessage(int messageId, boolean isIncoming);
+
+    void setLatitudeForMessage(int messageId, boolean isIncoming, double latitude);
+
+    double getLatitudeForMessage(int messageId, boolean isIncoming);
+
+    void setLongitudeForMessage(int messageId, boolean isIncoming, double longitude);
+
+    double getLongitudeForMessage(int messageId, boolean isIncoming);
+
+    // Returns the ID's of the file transfers attached to the given message
+    int[] getFileTransfersAttachedToMessage(int messageId, boolean isIncoming);
+
+    int getSenderParticipant(int messageId);
+
+    /////////////////////////
+    // RcsOutgoingMessageDelivery APIs
+    /////////////////////////
+
+    // Returns the participant ID's that this message is intended to be delivered to
+    int[] getMessageRecipients(int messageId);
+
+    long getOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId);
+
+    void setOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId, long deliveredTimestamp);
+
+    long getOutgoingDeliverySeenTimestamp(int messageId, int participantId);
+
+    void setOutgoingDeliverySeenTimestamp(int messageId, int participantId, long seenTimestamp);
+
+    int getOutgoingDeliveryStatus(int messageId, int participantId);
+
+    void setOutgoingDeliveryStatus(int messageId, int participantId, int status);
+
+    /////////////////////////
+    // RcsFileTransferPart APIs
+    /////////////////////////
+
+    // Performs the initial write to storage and returns the row ID.
+    int storeFileTransfer(int messageId, boolean isIncoming,
+            in RcsFileTransferCreationParams fileTransferCreationParams);
+
+    void deleteFileTransfer(int partId);
+
+    void setFileTransferSessionId(int partId, String sessionId);
+
+    String getFileTransferSessionId(int partId);
+
+    void setFileTransferContentUri(int partId, in Uri contentUri);
+
+    Uri getFileTransferContentUri(int partId);
+
+    void setFileTransferContentType(int partId, String contentType);
+
+    String getFileTransferContentType(int partId);
+
+    void setFileTransferFileSize(int partId, long fileSize);
+
+    long getFileTransferFileSize(int partId);
+
+    void setFileTransferTransferOffset(int partId, long transferOffset);
+
+    long getFileTransferTransferOffset(int partId);
+
+    void setFileTransferStatus(int partId, int transferStatus);
+
+    int getFileTransferStatus(int partId);
+
+    void setFileTransferWidth(int partId, int width);
+
+    int getFileTransferWidth(int partId);
+
+    void setFileTransferHeight(int partId, int height);
+
+    int getFileTransferHeight(int partId);
+
+    void setFileTransferLength(int partId, long length);
+
+    long getFileTransferLength(int partId);
+
+    void setFileTransferPreviewUri(int partId, in Uri uri);
+
+    Uri getFileTransferPreviewUri(int partId);
+
+    void setFileTransferPreviewType(int partId, String type);
+
+    String getFileTransferPreviewType(int partId);
+
+    /////////////////////////
+    // RcsEvent APIs
+    /////////////////////////
+    int createGroupThreadNameChangedEvent(long timestamp, int threadId, int originationParticipantId, String newName);
+
+    int createGroupThreadIconChangedEvent(long timestamp, int threadId, int originationParticipantId, in Uri newIcon);
+
+    int createGroupThreadParticipantJoinedEvent(long timestamp, int threadId, int originationParticipantId, int participantId);
+
+    int createGroupThreadParticipantLeftEvent(long timestamp, int threadId, int originationParticipantId, int participantId);
+
+    int createParticipantAliasChangedEvent(long timestamp, int participantId, String newAlias);
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 77e36bb..603f4e6 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -57,7 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    void onError(@GroupCallError int errorCode, @Nullable String message);
+    default void onError(@GroupCallError int errorCode, @Nullable String message) {}
 
     /**
      * Called to indicate this call has changed state.
@@ -65,8 +65,8 @@
      * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
      * and {@link GroupCall#STATE_STALLED}.
      */
-    void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
-            @GroupCall.GroupCallStateChangeReason int reason);
+    default void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+            @GroupCall.GroupCallStateChangeReason int reason) {}
 
     /**
      * Broadcast Signal Strength updated.
@@ -78,5 +78,6 @@
      * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
      * for this call due to timing, geography or popularity.
      */
-    void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
+    default void onBroadcastSignalStrengthUpdated(
+            @IntRange(from = -1, to = 4) int signalStrength) {}
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 04e7ba1..ac7e172 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -57,7 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    void onError(@GroupCallError int errorCode, @Nullable String message);
+    default void onError(@GroupCallError int errorCode, @Nullable String message) {}
 
     /**
      * Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,8 +70,8 @@
      * @param availableSais A list of lists of available SAIS in neighboring cells, where each list
      *                      contains the available SAIs in an individual cell.
      */
-    void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
-            @NonNull List<List<Integer>> availableSais);
+    default void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+            @NonNull List<List<Integer>> availableSais) {}
 
     /**
      * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
@@ -85,7 +85,7 @@
      * @param interfaceName The interface name for the data link.
      * @param index The index for the data link.
      */
-    void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
+    default void onServiceInterfaceAvailable(@NonNull String interfaceName, int index) {}
 
     /**
      * Called to indicate that the middleware has been initialized and is ready.
@@ -95,5 +95,5 @@
      * delivered via {@link #onError(int, String)} with error code
      * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
      */
-    void onMiddlewareReady();
+    default void onMiddlewareReady() {}
 }
diff --git a/telephony/java/com/android/ims/RcsTypeIdPair.java b/telephony/java/com/android/ims/RcsTypeIdPair.java
new file mode 100644
index 0000000..a517735
--- /dev/null
+++ b/telephony/java/com/android/ims/RcsTypeIdPair.java
@@ -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.
+ */
+package com.android.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A utility class to pass RCS IDs and types in RPC calls
+ *
+ * @hide
+ */
+public class RcsTypeIdPair implements Parcelable {
+    private int mType;
+    private int mId;
+
+    public RcsTypeIdPair(int type, int id) {
+        mType = type;
+        mId = id;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    public void setType(int type) {
+        mType = type;
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    public void setId(int id) {
+        mId = id;
+    }
+
+    public RcsTypeIdPair(Parcel in) {
+        mType = in.readInt();
+        mId = in.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeInt(mId);
+    }
+
+    public static final Creator<RcsTypeIdPair> CREATOR =
+            new Creator<RcsTypeIdPair>() {
+                @Override
+                public RcsTypeIdPair createFromParcel(Parcel in) {
+                    return new RcsTypeIdPair(in);
+                }
+
+                @Override
+                public RcsTypeIdPair[] newArray(int size) {
+                    return new RcsTypeIdPair[size];
+                }
+            };
+}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 04ec3d1..a49d2d9 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -276,6 +276,11 @@
 
     String getSubscriptionProperty(int subId, String propKey, String callingPackage);
 
+    boolean setSubscriptionEnabled(boolean enable, int subId);
+
+    boolean isSubscriptionEnabled(int subId);
+
+    int getEnabledSubscriptionId(int slotIndex);
     /**
      * Get the SIM state for the slot index
      * @return SIM state as the ordinal of IccCardConstants.State
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c7061df..e6a5558 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -765,7 +765,7 @@
      * @param subId the id of the subscription.
      * @return CellNetworkScanResult containing status of scan and networks.
      */
-    CellNetworkScanResult getCellNetworkScanResults(int subId);
+    CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage);
 
     /**
      * Perform a radio network scan and return the id of this scan.
@@ -774,10 +774,11 @@
      * @param request Defines all the configs for network scan.
      * @param messenger Callback messages will be sent using this messenger.
      * @param binder the binder object instantiated in TelephonyManager.
+     * @param callingPackage the calling package
      * @return An id for this scan.
      */
     int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,
-            in IBinder binder);
+            in IBinder binder, in String callingPackage);
 
     /**
      * Stop an existing radio network scan.
@@ -1829,7 +1830,7 @@
      * @hide
      */
     boolean isMultisimCarrierRestricted();
-    
+
     /**
      * Switch configs to enable multi-sim or switch back to single-sim
      * @hide
@@ -1840,8 +1841,15 @@
      * @hide
      */
     int getNumOfActiveSims();
+
     /**
      * Get if reboot is required upon altering modems configurations
+     * @hide
      */
     boolean isRebootRequiredForModemConfigChange();
+
+    /**
+     * Get the mapping from logical slots to physical slots.
+     */
+    int[] getSlotsMapping();
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/BearerData.java
index a4cd56b..694cc69 100644
--- a/telephony/java/com/android/internal/telephony/cdma/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/BearerData.java
@@ -596,6 +596,45 @@
         System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
     }
 
+    private static void encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force)
+            throws CodingException
+    {
+        try {
+            Rlog.d(LOG_TAG, "encode7bitAsciiEms");
+            int udhBytes = udhData.length + 1;  // Add length octet.
+            int udhSeptets = ((udhBytes * 8) + 6) / 7;
+            int paddingBits = (udhSeptets * 7) - (udhBytes * 8);
+            String msg = uData.payloadStr;
+            byte[] payload ;
+            int msgLen = msg.length();
+            BitwiseOutputStream outStream = new BitwiseOutputStream(msgLen +
+                    (paddingBits > 0 ? 1 : 0));
+            outStream.write(paddingBits, 0);
+            for (int i = 0; i < msgLen; i++) {
+                int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
+                if (charCode == -1) {
+                    if (force) {
+                        outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
+                    } else {
+                        throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
+                    }
+                } else {
+                    outStream.write(7, charCode);
+                }
+            }
+            payload = outStream.toByteArray();
+            uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
+            uData.msgEncodingSet = true;
+            uData.numFields = udhSeptets + uData.payloadStr.length();
+            uData.payload = new byte[udhBytes + payload.length];
+            uData.payload[0] = (byte)udhData.length;
+            System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
+            System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
+        } catch (BitwiseOutputStream.AccessException ex) {
+            throw new CodingException("7bit ASCII encode failed: " + ex);
+        }
+    }
+
     private static void encodeEmsUserDataPayload(UserData uData)
         throws CodingException
     {
@@ -605,6 +644,8 @@
                 encode7bitEms(uData, headerData, true);
             } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
                 encode16bitEms(uData, headerData);
+            } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
+                encode7bitAsciiEms(uData, headerData, true);
             } else {
                 throw new CodingException("unsupported EMS user data encoding (" +
                                           uData.msgEncoding + ")");
@@ -1056,15 +1097,18 @@
         throws CodingException
     {
         try {
-            offset *= 8;
+            int offsetBits = offset * 8;
+            int offsetSeptets = (offsetBits + 6) / 7;
+            numFields -= offsetSeptets;
+
             StringBuffer strBuf = new StringBuffer(numFields);
             BitwiseInputStream inStream = new BitwiseInputStream(data);
-            int wantedBits = (offset * 8) + (numFields * 7);
+            int wantedBits = (offsetSeptets * 7) + (numFields * 7);
             if (inStream.available() < wantedBits) {
                 throw new CodingException("insufficient data (wanted " + wantedBits +
                                           " bits, but only have " + inStream.available() + ")");
             }
-            inStream.skip(offset);
+            inStream.skip(offsetSeptets * 7);
             for (int i = 0; i < numFields; i++) {
                 int charCode = inStream.read(7);
                 if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 964a313..1da5eac 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -21,7 +21,6 @@
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SmsCbLocation;
 import android.telephony.SmsCbMessage;
-import android.telephony.TelephonyManager;
 import android.telephony.cdma.CdmaSmsCbProgramData;
 import android.telephony.Rlog;
 import android.util.Log;
@@ -746,8 +745,10 @@
 
     /**
      * Parses a broadcast SMS, possibly containing a CMAS alert.
+     *
+     * @param plmn the PLMN for a broadcast SMS
      */
-    public SmsCbMessage parseBroadcastSms() {
+    public SmsCbMessage parseBroadcastSms(String plmn) {
         BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory);
         if (bData == null) {
             Rlog.w(LOG_TAG, "BearerData.decode() returned null");
@@ -758,7 +759,6 @@
             Rlog.d(LOG_TAG, "MT raw BearerData = " + HexDump.toHexString(mEnvelope.bearerData));
         }
 
-        String plmn = TelephonyManager.getDefault().getNetworkOperator();
         SmsCbLocation location = new SmsCbLocation(plmn);
 
         return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
@@ -858,14 +858,15 @@
         bearerData.userData = userData;
 
         byte[] encodedBearerData = BearerData.encode(bearerData);
+        if (encodedBearerData == null) return null;
         if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
             Rlog.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData);
             Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'");
         }
-        if (encodedBearerData == null) return null;
 
-        int teleservice = bearerData.hasUserDataHeader ?
-                SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
+        int teleservice = (bearerData.hasUserDataHeader
+                && userData.msgEncoding != UserData.ENCODING_7BIT_ASCII)
+                ? SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
 
         SmsEnvelope envelope = new SmsEnvelope();
         envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
diff --git a/test-base/api/TEST_MAPPING b/test-base/api/TEST_MAPPING
new file mode 100644
index 0000000..3535954
--- /dev/null
+++ b/test-base/api/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsAndroidTestBase27ApiSignatureTestCases"
+    }
+  ]
+}
diff --git a/test-mock/api/TEST_MAPPING b/test-mock/api/TEST_MAPPING
new file mode 100644
index 0000000..d1bd9af
--- /dev/null
+++ b/test-mock/api/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsAndroidTestMockCurrentApiSignatureTestCases"
+    },
+    {
+      "name": "CtsCurrentApiSignatureTestCases"
+    }
+  ]
+}
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
deleted file mode 100644
index 18bde85..0000000
--- a/test-runner/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-# additionally, build unit tests in a separate .apk
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test-runner/api/TEST_MAPPING b/test-runner/api/TEST_MAPPING
new file mode 100644
index 0000000..76ade3c
--- /dev/null
+++ b/test-runner/api/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsAndroidTestRunnerCurrentApiSignatureTestCases"
+    },
+    {
+      "name": "CtsCurrentApiSignatureTestCases"
+    }
+  ]
+}
diff --git a/test-runner/tests/Android.bp b/test-runner/tests/Android.bp
new file mode 100644
index 0000000..03c7398
--- /dev/null
+++ b/test-runner/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright 2010, 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: "FrameworkTestRunnerTests",
+
+    // We only want this apk build for tests.
+    //
+    // Run the tests using the following commands:
+    //   adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworkTestRunnerTests/FrameworkTestRunnerTests.apk
+    //   adb shell am instrument \
+    //     -e notAnnotation android.test.suitebuilder.examples.error.RunAsPartOfSeparateTest \
+    //     -w com.android.frameworks.testrunner.tests/android.test.InstrumentationTestRunner \
+    //
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+    ],
+    static_libs: ["junit"],
+
+    // Include all test java files.
+    srcs: ["src/**/*.java"],
+
+    // Because of android.test.mock.
+    platform_apis: true,
+
+}
diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk
deleted file mode 100644
index f97d1c9..0000000
--- a/test-runner/tests/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2010, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-#
-# Run the tests using the following commands:
-#   adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworkTestRunnerTests/FrameworkTestRunnerTests.apk
-#   adb shell am instrument \
-        -e notAnnotation android.test.suitebuilder.examples.error.RunAsPartOfSeparateTest \
-        -w com.android.frameworks.testrunner.tests/android.test.InstrumentationTestRunner
-#
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := FrameworkTestRunnerTests
-# Because of android.test.mock.
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
-
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
new file mode 100644
index 0000000..915a260
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadIconChangedEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadIconChangedEventTest {
+
+    @Test
+    public void testCanUnparcel() {
+        RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+        RcsParticipant rcsParticipant = new RcsParticipant(2);
+        Uri newIconUri = Uri.parse("content://new_icon");
+
+        RcsGroupThreadIconChangedEvent iconChangedEvent =
+                new RcsGroupThreadIconChangedEvent(1234567890, rcsGroupThread, rcsParticipant,
+                        newIconUri);
+
+        Parcel parcel = Parcel.obtain();
+        iconChangedEvent.writeToParcel(parcel, iconChangedEvent.describeContents());
+
+        parcel.setDataPosition(0);
+
+        iconChangedEvent = RcsGroupThreadIconChangedEvent.CREATOR.createFromParcel(parcel);
+
+        assertThat(iconChangedEvent.getNewIcon()).isEqualTo(newIconUri);
+        assertThat(iconChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+        assertThat(iconChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
+        assertThat(iconChangedEvent.getTimestamp()).isEqualTo(1234567890);
+    }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
new file mode 100644
index 0000000..1384c01
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadNameChangedEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadNameChangedEventTest {
+    @Test
+    public void testCanUnparcel() {
+        String newName = "new name";
+
+        RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+        RcsParticipant rcsParticipant = new RcsParticipant(2);
+
+        RcsGroupThreadNameChangedEvent nameChangedEvent =
+                new RcsGroupThreadNameChangedEvent(1234567890, rcsGroupThread, rcsParticipant,
+                        newName);
+
+        Parcel parcel = Parcel.obtain();
+        nameChangedEvent.writeToParcel(parcel, nameChangedEvent.describeContents());
+
+        parcel.setDataPosition(0);
+
+        nameChangedEvent = RcsGroupThreadNameChangedEvent.CREATOR.createFromParcel(
+                parcel);
+
+        assertThat(nameChangedEvent.getNewName()).isEqualTo(newName);
+        assertThat(nameChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+        assertThat(nameChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
+        assertThat(nameChangedEvent.getTimestamp()).isEqualTo(1234567890);
+    }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
new file mode 100644
index 0000000..d0af7db
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadParticipantJoinedEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadParticipantJoinedEventTest {
+
+    @Test
+    public void testCanUnparcel() {
+        RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+        RcsParticipant rcsParticipant = new RcsParticipant(2);
+
+        RcsGroupThreadParticipantJoinedEvent participantJoinedEvent =
+                new RcsGroupThreadParticipantJoinedEvent(1234567890, rcsGroupThread, rcsParticipant,
+                        rcsParticipant);
+
+        Parcel parcel = Parcel.obtain();
+        participantJoinedEvent.writeToParcel(parcel, participantJoinedEvent.describeContents());
+
+        parcel.setDataPosition(0);
+
+        participantJoinedEvent = RcsGroupThreadParticipantJoinedEvent.CREATOR.createFromParcel(
+                parcel);
+
+        assertThat(participantJoinedEvent.getJoinedParticipant().getId()).isEqualTo(2);
+        assertThat(participantJoinedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+        assertThat(participantJoinedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
+        assertThat(participantJoinedEvent.getTimestamp()).isEqualTo(1234567890);
+    }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
new file mode 100644
index 0000000..7ba5fa6
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadParticipantLeftEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadParticipantLeftEventTest {
+    @Test
+    public void testCanUnparcel() {
+        RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+        RcsParticipant rcsParticipant = new RcsParticipant(2);
+
+        RcsGroupThreadParticipantLeftEvent participantLeftEvent =
+                new RcsGroupThreadParticipantLeftEvent(1234567890, rcsGroupThread, rcsParticipant,
+                        rcsParticipant);
+
+        Parcel parcel = Parcel.obtain();
+        participantLeftEvent.writeToParcel(parcel, participantLeftEvent.describeContents());
+
+        parcel.setDataPosition(0);
+
+        // create from parcel
+        parcel.setDataPosition(0);
+        participantLeftEvent = RcsGroupThreadParticipantLeftEvent.CREATOR.createFromParcel(
+                parcel);
+        assertThat(participantLeftEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+        assertThat(participantLeftEvent.getLeavingParticipantId().getId()).isEqualTo(2);
+        assertThat(participantLeftEvent.getTimestamp()).isEqualTo(1234567890);
+    }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
deleted file mode 100644
index 44277ed..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
+++ /dev/null
@@ -1,32 +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 com.android.tests.ims;
-
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.RcsMessageStore;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsMessageStoreTest {
-    //TODO(sahinc): Add meaningful tests once we have more of the implementation in place
-    @Test
-    public void testDeleteThreadDoesntCrash() {
-        RcsMessageStore mRcsMessageStore = new RcsMessageStore();
-        mRcsMessageStore.deleteThread(0);
-    }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
new file mode 100644
index 0000000..3e2bbbf
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.RcsParticipantAliasChangedEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsParticipantAliasChangedEventTest {
+    private static final String OLD_ALIAS = "old alias";
+    private static final String NEW_ALIAS = "new alias";
+    private RcsParticipant mParticipant;
+
+    @Before
+    public void setUp() {
+        mParticipant = new RcsParticipant(3);
+    }
+
+    @Test
+    public void testCanUnparcel() {
+        RcsParticipantAliasChangedEvent aliasChangedEvent =
+                new RcsParticipantAliasChangedEvent(1234567890, mParticipant, NEW_ALIAS);
+
+        Parcel parcel = Parcel.obtain();
+        aliasChangedEvent.writeToParcel(parcel, aliasChangedEvent.describeContents());
+
+        parcel.setDataPosition(0);
+
+        aliasChangedEvent = RcsParticipantAliasChangedEvent.CREATOR.createFromParcel(
+                parcel);
+
+        assertThat(aliasChangedEvent.getParticipantId().getId()).isEqualTo(3);
+        assertThat(aliasChangedEvent.getNewAlias()).isEqualTo(NEW_ALIAS);
+        assertThat(aliasChangedEvent.getTimestamp()).isEqualTo(1234567890);
+    }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
new file mode 100644
index 0000000..6361a39
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipantQueryParams;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsParticipantQueryParamsTest {
+
+    @Test
+    public void testCanUnparcel() {
+        RcsParticipantQueryParams rcsParticipantQueryParams =
+                new RcsParticipantQueryParams.Builder()
+                        .setAliasLike("%alias_")
+                        .setCanonicalAddressLike("_canonical%")
+                        .setSortProperty(RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS)
+                        .setSortDirection(true)
+                        .setResultLimit(432)
+                        .build();
+
+
+        Parcel parcel = Parcel.obtain();
+        rcsParticipantQueryParams.writeToParcel(parcel,
+                rcsParticipantQueryParams.describeContents());
+
+        parcel.setDataPosition(0);
+        rcsParticipantQueryParams = RcsParticipantQueryParams.CREATOR.createFromParcel(
+                parcel);
+
+        assertThat(rcsParticipantQueryParams.getAliasLike()).isEqualTo("%alias_");
+        assertThat(rcsParticipantQueryParams.getCanonicalAddressLike()).contains("_canonical%");
+        assertThat(rcsParticipantQueryParams.getLimit()).isEqualTo(432);
+        assertThat(rcsParticipantQueryParams.getSortingProperty()).isEqualTo(
+                RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS);
+        assertThat(rcsParticipantQueryParams.getSortDirection()).isTrue();
+    }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
deleted file mode 100644
index c402dbf..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
+++ /dev/null
@@ -1,46 +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 com.android.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.RcsParticipant;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsParticipantTest {
-    private static final int ID = 123;
-    private static final String ALIAS = "alias";
-    private static final String CANONICAL_ADDRESS = "+1234567890";
-
-    @Test
-    public void testCanUnparcel() {
-        RcsParticipant rcsParticipant = new RcsParticipant(ID, CANONICAL_ADDRESS);
-        rcsParticipant.setAlias(ALIAS);
-
-        Bundle bundle = new Bundle();
-        bundle.putParcelable("Some key", rcsParticipant);
-        rcsParticipant = bundle.getParcelable("Some key");
-
-        assertThat(rcsParticipant.getId()).isEqualTo(ID);
-        assertThat(rcsParticipant.getAlias()).isEqualTo(ALIAS);
-        assertThat(rcsParticipant.getCanonicalAddress()).isEqualTo(CANONICAL_ADDRESS);
-    }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
deleted file mode 100644
index a890a38..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
+++ /dev/null
@@ -1,53 +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 com.android.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.RcsParticipant;
-import android.telephony.ims.RcsThreadQueryParameters;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsThreadQueryParametersTest {
-    private RcsThreadQueryParameters mRcsThreadQueryParameters;
-    @Mock RcsParticipant mMockParticipant;
-
-    @Test
-    public void testUnparceling() {
-        String key = "some key";
-        mRcsThreadQueryParameters = RcsThreadQueryParameters.builder()
-                .isGroupThread(true)
-                .withParticipant(mMockParticipant)
-                .limitResultsTo(50)
-                .sort(true)
-                .build();
-
-        Bundle bundle = new Bundle();
-        bundle.putParcelable(key, mRcsThreadQueryParameters);
-        mRcsThreadQueryParameters = bundle.getParcelable(key);
-
-        assertThat(mRcsThreadQueryParameters.isGroupThread()).isTrue();
-        assertThat(mRcsThreadQueryParameters.getRcsParticipants()).contains(mMockParticipant);
-        assertThat(mRcsThreadQueryParameters.getLimit()).isEqualTo(50);
-        assertThat(mRcsThreadQueryParameters.isAscending()).isTrue();
-    }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
new file mode 100644
index 0000000..beb4f8a
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.ims;
+
+import static android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP;
+import static android.telephony.ims.RcsThreadQueryParams.THREAD_TYPE_GROUP;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.RcsThreadQueryParams;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsThreadQueryParamsTest {
+
+    @Test
+    public void testCanUnparcel() {
+        RcsParticipant rcsParticipant = new RcsParticipant(1);
+        RcsThreadQueryParams rcsThreadQueryParams = new RcsThreadQueryParams.Builder()
+                .setThreadType(THREAD_TYPE_GROUP)
+                .setParticipant(rcsParticipant)
+                .setResultLimit(50)
+                .setSortProperty(SORT_BY_TIMESTAMP)
+                .setSortDirection(true)
+                .build();
+
+        Parcel parcel = Parcel.obtain();
+        rcsThreadQueryParams.writeToParcel(parcel, rcsThreadQueryParams.describeContents());
+
+        parcel.setDataPosition(0);
+        rcsThreadQueryParams = RcsThreadQueryParams.CREATOR.createFromParcel(parcel);
+
+        assertThat(rcsThreadQueryParams.getThreadType()).isEqualTo(THREAD_TYPE_GROUP);
+        assertThat(rcsThreadQueryParams.getRcsParticipantsIds())
+                .contains(rcsParticipant.getId());
+        assertThat(rcsThreadQueryParams.getLimit()).isEqualTo(50);
+        assertThat(rcsThreadQueryParams.getSortingProperty()).isEqualTo(SORT_BY_TIMESTAMP);
+        assertThat(rcsThreadQueryParams.getSortDirection()).isTrue();
+    }
+}
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index 299fbef..bdde096 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -22,18 +22,15 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
 import android.net.LinkProperties.CompareResult;
 import android.net.LinkProperties.ProvisioningChange;
-import android.net.RouteInfo;
-import android.os.Parcel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.system.OsConstants;
 import android.util.ArraySet;
 
+import com.android.internal.util.TestUtils;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -849,18 +846,6 @@
         assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
     }
 
-    private void assertParcelingIsLossless(LinkProperties source) {
-        Parcel p = Parcel.obtain();
-        source.writeToParcel(p, /* flags */ 0);
-        p.setDataPosition(0);
-        final byte[] marshalled = p.marshall();
-        p = Parcel.obtain();
-        p.unmarshall(marshalled, 0, marshalled.length);
-        p.setDataPosition(0);
-        LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p);
-        assertEquals(source, dest);
-    }
-
     @Test
     public void testLinkPropertiesParcelable() throws Exception {
         LinkProperties source = new LinkProperties();
@@ -882,12 +867,12 @@
 
         source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
 
-        assertParcelingIsLossless(source);
+        TestUtils.assertParcelingIsLossless(source, LinkProperties.CREATOR);
     }
 
     @Test
     public void testParcelUninitialized() throws Exception {
         LinkProperties empty = new LinkProperties();
-        assertParcelingIsLossless(empty);
+        TestUtils.assertParcelingIsLossless(empty, LinkProperties.CREATOR);
     }
 }
diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
new file mode 100644
index 0000000..1f2dd27
--- /dev/null
+++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.net;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.net.SocketKeepalive.InvalidPacketException;
+import android.net.TcpKeepalivePacketData.TcpSocketInfo;
+
+import com.android.internal.util.TestUtils;
+
+import libcore.net.InetAddressUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
+@RunWith(JUnit4.class)
+public final class TcpKeepalivePacketDataTest {
+
+    @Before
+    public void setUp() {}
+
+    @Test
+    public void testV4TcpKeepalivePacket() {
+        final InetAddress srcAddr = InetAddressUtils.parseNumericAddress("192.168.0.1");
+        final InetAddress dstAddr = InetAddressUtils.parseNumericAddress("192.168.0.10");
+        final int srcPort = 1234;
+        final int dstPort = 4321;
+        final int seq = 0x11111111;
+        final int ack = 0x22222222;
+        final int wnd = 8000;
+        final int wndScale = 2;
+        TcpKeepalivePacketData resultData = null;
+        TcpSocketInfo testInfo = new TcpSocketInfo(
+                srcAddr, srcPort, dstAddr, dstPort, seq, ack, wnd, wndScale);
+        try {
+            resultData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo);
+        } catch (InvalidPacketException e) {
+            fail("InvalidPacketException: " + e);
+        }
+
+        assertEquals(testInfo.srcAddress, resultData.srcAddress);
+        assertEquals(testInfo.dstAddress, resultData.dstAddress);
+        assertEquals(testInfo.srcPort, resultData.srcPort);
+        assertEquals(testInfo.dstPort, resultData.dstPort);
+        assertEquals(testInfo.seq, resultData.tcpSeq);
+        assertEquals(testInfo.ack, resultData.tcpAck);
+        assertEquals(testInfo.rcvWndScale, resultData.tcpWndScale);
+
+        TestUtils.assertParcelingIsLossless(resultData, TcpKeepalivePacketData.CREATOR);
+
+        final byte[] packet = resultData.getPacket();
+        // IP version and TOS.
+        ByteBuffer buf = ByteBuffer.wrap(packet);
+        assertEquals(buf.getShort(), 0x4500);
+        // Source IP address.
+        byte[] ip = new byte[4];
+        buf = ByteBuffer.wrap(packet, 12, 4);
+        buf.get(ip);
+        assertArrayEquals(ip, srcAddr.getAddress());
+        // Destination IP address.
+        buf = ByteBuffer.wrap(packet, 16, 4);
+        buf.get(ip);
+        assertArrayEquals(ip, dstAddr.getAddress());
+
+        buf = ByteBuffer.wrap(packet, 20, 12);
+        // Source port.
+        assertEquals(buf.getShort(), srcPort);
+        // Destination port.
+        assertEquals(buf.getShort(), dstPort);
+        // Sequence number.
+        assertEquals(buf.getInt(), seq);
+        // Ack.
+        assertEquals(buf.getInt(), ack);
+        // Window size.
+        buf = ByteBuffer.wrap(packet, 34, 2);
+        assertEquals(buf.getShort(), wnd >> wndScale);
+    }
+
+    //TODO: add ipv6 test when ipv6 supported
+
+    @Test
+    public void testParcel() throws Exception {
+        final InetAddress srcAddr = InetAddresses.parseNumericAddress("192.168.0.1");
+        final InetAddress dstAddr = InetAddresses.parseNumericAddress("192.168.0.10");
+        final int srcPort = 1234;
+        final int dstPort = 4321;
+        final int sequence = 0x11111111;
+        final int ack = 0x22222222;
+        final int wnd = 48_000;
+        final int wndScale = 2;
+        TcpKeepalivePacketData testData = null;
+        TcpKeepalivePacketDataParcelable resultData = null;
+        TcpSocketInfo testInfo = new TcpSocketInfo(
+                srcAddr, srcPort, dstAddr, dstPort, sequence, ack, wnd, wndScale);
+        testData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo);
+        resultData = testData.toStableParcelable();
+        assertArrayEquals(resultData.srcAddress, srcAddr.getAddress());
+        assertArrayEquals(resultData.dstAddress, dstAddr.getAddress());
+        assertEquals(resultData.srcPort, srcPort);
+        assertEquals(resultData.dstPort, dstPort);
+        assertEquals(resultData.seq, sequence);
+        assertEquals(resultData.ack, ack);
+    }
+}
diff --git a/tests/net/java/com/android/internal/util/TestUtils.java b/tests/net/java/com/android/internal/util/TestUtils.java
index 6db01d3..7e5a1d3 100644
--- a/tests/net/java/com/android/internal/util/TestUtils.java
+++ b/tests/net/java/com/android/internal/util/TestUtils.java
@@ -16,12 +16,15 @@
 
 package com.android.internal.util;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 public final class TestUtils {
     private TestUtils() { }
@@ -50,4 +53,18 @@
             fail(handler.toString() + " did not become idle after " + timeoutMs + " ms");
         }
     }
+
+    // TODO : fetch the creator through reflection or something instead of passing it
+    public static <T extends Parcelable, C extends Parcelable.Creator<T>>
+            void assertParcelingIsLossless(T source, C creator) {
+        Parcel p = Parcel.obtain();
+        source.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        final byte[] marshalled = p.marshall();
+        p = Parcel.obtain();
+        p.unmarshall(marshalled, 0, marshalled.length);
+        p.setDataPosition(0);
+        T dest = creator.createFromParcel(p);
+        assertEquals(source, dest);
+    }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index ebfb69e..963b685 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -194,6 +194,7 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -500,17 +501,17 @@
                 public void unwanted() { mDisconnected.open(); }
 
                 @Override
-                public void startPacketKeepalive(Message msg) {
+                public void startSocketKeepalive(Message msg) {
                     int slot = msg.arg1;
                     if (mExpectedKeepaliveSlot != null) {
                         assertEquals((int) mExpectedKeepaliveSlot, slot);
                     }
-                    onPacketKeepaliveEvent(slot, mStartKeepaliveError);
+                    onSocketKeepaliveEvent(slot, mStartKeepaliveError);
                 }
 
                 @Override
-                public void stopPacketKeepalive(Message msg) {
-                    onPacketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
+                public void stopSocketKeepalive(Message msg) {
+                    onSocketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
                 }
 
                 @Override
@@ -553,7 +554,7 @@
 
                 if (mNmValidationRedirectUrl != null) {
                     mNmCallbacks.showProvisioningNotification(
-                            "test_provisioning_notif_action");
+                            "test_provisioning_notif_action", "com.android.test.package");
                     mNmProvNotificationRequested = true;
                 }
             } catch (RemoteException e) {
@@ -3791,10 +3792,17 @@
 
     @Test
     public void testNattSocketKeepalives() throws Exception {
+        final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor();
+        doTestNattSocketKeepalivesWithExecutor(executorSingleThread);
+        executorSingleThread.shutdown();
+
+        final Executor executorInline = (Runnable r) -> r.run();
+        doTestNattSocketKeepalivesWithExecutor(executorInline);
+    }
+
+    private void doTestNattSocketKeepalivesWithExecutor(Executor executor) throws Exception {
         // TODO: 1. Move this outside of ConnectivityServiceTest.
-        //       2. Add helper function to test against newSingleThreadExecutor as well as inline
-        //          executor.
-        //       3. Make test to verify that Nat-T keepalive socket is created by IpSecService.
+        //       2. Make test to verify that Nat-T keepalive socket is created by IpSecService.
         final int srcPort = 12345;
         final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
         final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
@@ -3808,8 +3816,6 @@
         final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
         final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(srcPort);
 
-        final Executor executor = Executors.newSingleThreadExecutor();
-
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName("wlan12");
         lp.addLinkAddress(new LinkAddress(myIPv6, 64));
@@ -3926,6 +3932,11 @@
 
         ka2.stop();
         callback2.expectStopped();
+
+        testSocket.close();
+        testSocket2.close();
+
+        mWiFiNetworkAgent.disconnect();
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index b635607..a4a735d 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -35,6 +35,7 @@
 import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -274,6 +275,11 @@
             isTetheringSupportedCalls++;
             return true;
         }
+
+        @Override
+        public int getDefaultDataSubscriptionId() {
+            return INVALID_SUBSCRIPTION_ID;
+        }
     }
 
     private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index ec286759..193f380 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -21,6 +21,7 @@
 import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
 import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
 import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -140,7 +141,8 @@
         mMockContext = new MockContext(mContext);
         mSM = new TestStateMachine();
         mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, mSystemProperties);
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
+        mEnMgr.updateConfiguration(
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
     }
 
     @After
@@ -168,7 +170,8 @@
     @Test
     public void canRequireProvisioning() {
         setupForRequiredProvisioning();
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
+        mEnMgr.updateConfiguration(
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
         assertTrue(mEnMgr.isTetherProvisioningRequired());
     }
 
@@ -177,7 +180,8 @@
         setupForRequiredProvisioning();
         when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
             .thenReturn(null);
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
+        mEnMgr.updateConfiguration(
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
         // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
         // Therefore provisioning still be required.
         assertTrue(mEnMgr.isTetherProvisioningRequired());
@@ -187,7 +191,8 @@
     public void toleratesCarrierConfigMissing() {
         setupForRequiredProvisioning();
         when(mCarrierConfigManager.getConfig()).thenReturn(null);
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
+        mEnMgr.updateConfiguration(
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
         // We still have a provisioning app configured, so still require provisioning.
         assertTrue(mEnMgr.isTetherProvisioningRequired());
     }
@@ -197,11 +202,13 @@
         setupForRequiredProvisioning();
         when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
             .thenReturn(null);
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
+        mEnMgr.updateConfiguration(
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
         assertFalse(mEnMgr.isTetherProvisioningRequired());
         when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
             .thenReturn(new String[] {"malformedApp"});
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
+        mEnMgr.updateConfiguration(
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
         assertFalse(mEnMgr.isTetherProvisioningRequired());
     }
 
@@ -223,7 +230,8 @@
         assertFalse(mEnMgr.everRunUiEntitlement);
 
         setupForRequiredProvisioning();
-        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog,
+                  INVALID_SUBSCRIPTION_ID));
         // 2. No cache value and don't need to run entitlement check.
         mEnMgr.everRunUiEntitlement = false;
         receiver = new ResultReceiver(null) {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index 5217784..01b904d8 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -22,10 +22,12 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_NOT_REQUIRED;
 import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_REQUIRED;
 import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_UNSPECIFIED;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -44,26 +46,39 @@
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
 
-import java.util.Iterator;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Iterator;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class TetheringConfigurationTest {
     private final SharedLog mLog = new SharedLog("TetheringConfigurationTest");
+
+    private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
     @Mock private Context mContext;
     @Mock private TelephonyManager mTelephonyManager;
     @Mock private Resources mResources;
+    @Mock private Resources mResourcesForSubId;
     private MockContentResolver mContentResolver;
     private Context mMockContext;
     private boolean mHasTelephonyManager;
 
+    private class MockTetheringConfiguration extends TetheringConfiguration {
+        MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
+            super(ctx, log, id);
+        }
+
+        @Override
+        protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
+            return mResourcesForSubId;
+        }
+    }
+
     private class MockContext extends BroadcastInterceptingContext {
         MockContext(Context base) {
             super(base);
@@ -99,6 +114,9 @@
                 .thenReturn(new String[0]);
         when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
                 .thenReturn(new int[0]);
+        when(mResources.getStringArray(
+                com.android.internal.R.array.config_mobile_hotspot_provision_app))
+                .thenReturn(new String[0]);
         mContentResolver = new MockContentResolver();
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         mMockContext = new MockContext(mContext);
@@ -111,7 +129,8 @@
         mHasTelephonyManager = true;
         when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_REQUIRED);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         assertTrue(cfg.isDunRequired);
         assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
         assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
@@ -127,7 +146,8 @@
         mHasTelephonyManager = true;
         when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_NOT_REQUIRED);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         assertFalse(cfg.isDunRequired);
         assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
         assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
@@ -143,7 +163,8 @@
         mHasTelephonyManager = false;
         when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         assertTrue(cfg.isDunRequired);
         assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
         // Just to prove we haven't clobbered Wi-Fi:
@@ -160,7 +181,8 @@
         mHasTelephonyManager = false;
         when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
         assertTrue(upstreamIterator.hasNext());
         assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
@@ -181,7 +203,8 @@
         mHasTelephonyManager = false;
         when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
         assertTrue(upstreamIterator.hasNext());
         assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
@@ -199,7 +222,8 @@
         mHasTelephonyManager = false;
         when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
         assertTrue(upstreamIterator.hasNext());
         assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
@@ -214,7 +238,8 @@
     public void testNewDhcpServerDisabled() {
         Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         assertTrue(cfg.enableLegacyDhcpServer);
     }
 
@@ -222,7 +247,41 @@
     public void testNewDhcpServerEnabled() {
         Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         assertFalse(cfg.enableLegacyDhcpServer);
     }
+
+    @Test
+    public void testGetResourcesBySubId() {
+        setUpResourceForSubId();
+        final TetheringConfiguration cfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(cfg.provisioningApp.length == 0);
+        final int anyValidSubId = 1;
+        final MockTetheringConfiguration mockCfg =
+                new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId);
+        assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]);
+        assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]);
+    }
+
+    private void setUpResourceForSubId() {
+        when(mResourcesForSubId.getStringArray(
+                com.android.internal.R.array.config_tether_dhcp_range)).thenReturn(new String[0]);
+        when(mResourcesForSubId.getStringArray(
+                com.android.internal.R.array.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" });
+        when(mResourcesForSubId.getStringArray(
+                com.android.internal.R.array.config_tether_bluetooth_regexs))
+                .thenReturn(new String[0]);
+        when(mResourcesForSubId.getIntArray(
+                com.android.internal.R.array.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);
+    }
+
 }
diff --git a/tools/preload/Android.bp b/tools/preload/Android.bp
new file mode 100644
index 0000000..809ee47
--- /dev/null
+++ b/tools/preload/Android.bp
@@ -0,0 +1,17 @@
+java_library_host {
+    name: "preload",
+    srcs: [
+        "Compile.java",
+        "LoadedClass.java",
+        "MemoryUsage.java",
+        "Operation.java",
+        "Policy.java",
+        "PrintCsv.java",
+        "PrintHtmlDiff.java",
+        "PrintPsTree.java",
+        "Proc.java",
+        "Record.java",
+        "Root.java",
+        "WritePreloadedClassFile.java",
+    ],
+}
diff --git a/tools/preload/Android.mk b/tools/preload/Android.mk
deleted file mode 100644
index 14a4547..0000000
--- a/tools/preload/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-	Compile.java  \
-	LoadedClass.java \
-	MemoryUsage.java \
-	Operation.java \
-	Policy.java \
-	PrintCsv.java \
-	PrintHtmlDiff.java \
-	PrintPsTree.java \
-	Proc.java \
-	Record.java \
-	Root.java \
-	WritePreloadedClassFile.java
-
-LOCAL_MODULE:= preload
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/preload/loadclass/Android.bp b/tools/preload/loadclass/Android.bp
new file mode 100644
index 0000000..6f12015
--- /dev/null
+++ b/tools/preload/loadclass/Android.bp
@@ -0,0 +1,4 @@
+java_test {
+    name: "loadclass",
+    srcs: ["**/*.java"],
+}
diff --git a/tools/preload/loadclass/Android.mk b/tools/preload/loadclass/Android.mk
deleted file mode 100644
index 65828be..0000000
--- a/tools/preload/loadclass/Android.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE := loadclass
-
-include $(BUILD_JAVA_LIBRARY)