Merge "API updated for ICU4J 64.2"
diff --git a/Android.bp b/Android.bp
index c194929..e7e0774 100644
--- a/Android.bp
+++ b/Android.bp
@@ -303,6 +303,7 @@
         "core/java/android/companion/ICompanionDeviceDiscoveryService.aidl",
         "core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl",
         "core/java/android/companion/IFindDeviceCallback.aidl",
+        "core/java/android/compat/IPlatformCompat.aidl",
         "core/java/android/service/dreams/IDreamManager.aidl",
         "core/java/android/service/dreams/IDreamService.aidl",
         "core/java/android/service/oemlock/IOemLockService.aidl",
@@ -508,6 +509,7 @@
         "telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl",
         "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
         "telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl",
+        "telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl",
         "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",
@@ -1662,6 +1664,7 @@
         last_released: {
             api_file: ":last-released-public-api",
             removed_api_file: "api/removed.txt",
+            baseline_file: ":public-api-incompatibilities-with-last-released",
         },
     },
     jdiff_enabled: true,
@@ -1687,6 +1690,7 @@
         last_released: {
             api_file: ":last-released-system-api",
             removed_api_file: "api/system-removed.txt",
+            baseline_file: ":system-api-incompatibilities-with-last-released"
         },
     },
     jdiff_enabled: true,
diff --git a/services/core/java/com/android/server/compat/IPlatformCompat.aidl b/core/java/android/compat/IPlatformCompat.aidl
similarity index 90%
rename from services/core/java/com/android/server/compat/IPlatformCompat.aidl
rename to core/java/android/compat/IPlatformCompat.aidl
index 8ab08f9..3d8a9d5 100644
--- a/services/core/java/com/android/server/compat/IPlatformCompat.aidl
+++ b/core/java/android/compat/IPlatformCompat.aidl
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.server.compat;
+package android.compat;
 
 import android.content.pm.ApplicationInfo;
 
 /**
- * System private API for talking with the PlatformCompat service.
+ * Platform private API for talking with the PlatformCompat service.
+ *
+ * <p> Should be used for gating and logging from non-app processes.
+ * For app processes please use android.compat.Compatibility API.
+ *
  * {@hide}
  */
 interface IPlatformCompat
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/core/java/android/net/util/MultinetworkPolicyTracker.java
index 30c5cd9..f7e494d 100644
--- a/core/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/core/java/android/net/util/MultinetworkPolicyTracker.java
@@ -16,29 +16,32 @@
 
 package android.net.util;
 
+import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
+import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.net.ConnectivityManager;
 import android.net.Uri;
 import android.os.Handler;
-import android.os.Message;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.util.Slog;
 
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Arrays;
 import java.util.List;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.R;
-
-import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
-import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
-
 /**
  * A class to encapsulate management of the "Smart Networking" capability of
  * avoiding bad Wi-Fi when, for example upstream connectivity is lost or
@@ -69,6 +72,7 @@
 
     private volatile boolean mAvoidBadWifi = true;
     private volatile int mMeteredMultipathPreference;
+    private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
     public MultinetworkPolicyTracker(Context ctx, Handler handler) {
         this(ctx, handler, null);
@@ -95,6 +99,14 @@
             }
         };
 
+        TelephonyManager.from(ctx).listen(new PhoneStateListener() {
+            @Override
+            public void onActiveDataSubscriptionIdChanged(int subId) {
+                mActiveSubId = subId;
+                reevaluate();
+            }
+        }, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+
         updateAvoidBadWifi();
         updateMeteredMultipathPreference();
     }
@@ -131,7 +143,12 @@
      * Whether the device or carrier configuration disables avoiding bad wifi by default.
      */
     public boolean configRestrictsAvoidBadWifi() {
-        return (mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
+        return (getResourcesForActiveSubId().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
+    }
+
+    @NonNull
+    private Resources getResourcesForActiveSubId() {
+        return SubscriptionManager.getResourcesForSubId(mContext, mActiveSubId);
     }
 
     /**
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 3de1183..bf17671 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -752,6 +752,8 @@
                     Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
                 }
             } else {
+                // Clear the parcel before writing the exception
+                reply.setDataSize(0);
                 reply.setDataPosition(0);
                 reply.writeException(e);
             }
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index 70d049a..fe536a6 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -20,6 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import libcore.util.FP16;
+
 /**
  * <p>The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit
  * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
@@ -148,25 +150,6 @@
      */
     public static final @HalfFloat short POSITIVE_ZERO = (short) 0x0000;
 
-    private static final int FP16_SIGN_SHIFT        = 15;
-    private static final int FP16_SIGN_MASK         = 0x8000;
-    private static final int FP16_EXPONENT_SHIFT    = 10;
-    private static final int FP16_EXPONENT_MASK     = 0x1f;
-    private static final int FP16_SIGNIFICAND_MASK  = 0x3ff;
-    private static final int FP16_EXPONENT_BIAS     = 15;
-    private static final int FP16_COMBINED          = 0x7fff;
-    private static final int FP16_EXPONENT_MAX      = 0x7c00;
-
-    private static final int FP32_SIGN_SHIFT        = 31;
-    private static final int FP32_EXPONENT_SHIFT    = 23;
-    private static final int FP32_EXPONENT_MASK     = 0xff;
-    private static final int FP32_SIGNIFICAND_MASK  = 0x7fffff;
-    private static final int FP32_EXPONENT_BIAS     = 127;
-    private static final int FP32_QNAN_MASK         = 0x400000;
-
-    private static final int FP32_DENORMAL_MAGIC = 126 << 23;
-    private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
-
     private final @HalfFloat short mValue;
 
     /**
@@ -414,16 +397,7 @@
      *          than {@code y}
      */
     public static int compare(@HalfFloat short x, @HalfFloat short y) {
-        if (less(x, y)) return -1;
-        if (greater(x, y)) return 1;
-
-        // Collapse NaNs, akin to halfToIntBits(), but we want to keep
-        // (signed) short value types to preserve the ordering of -0.0
-        // and +0.0
-        short xBits = (x & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : x;
-        short yBits = (y & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : y;
-
-        return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1));
+        return FP16.compare(x, y);
     }
 
     /**
@@ -440,7 +414,7 @@
      * @see #halfToIntBits(short)
      */
     public static @HalfFloat short halfToShortBits(@HalfFloat short h) {
-        return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h;
+        return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h;
     }
 
     /**
@@ -459,7 +433,7 @@
      * @see #intBitsToHalf(int)
      */
     public static int halfToIntBits(@HalfFloat short h) {
-        return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h & 0xffff;
+        return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h & 0xffff;
     }
 
     /**
@@ -505,7 +479,7 @@
      *         of the second parameter
      */
     public static @HalfFloat short copySign(@HalfFloat short magnitude, @HalfFloat short sign) {
-        return (short) ((sign & FP16_SIGN_MASK) | (magnitude & FP16_COMBINED));
+        return (short) ((sign & FP16.SIGN_MASK) | (magnitude & FP16.EXPONENT_SIGNIFICAND_MASK));
     }
 
     /**
@@ -523,7 +497,7 @@
      * @return The absolute value of the specified half-precision float
      */
     public static @HalfFloat short abs(@HalfFloat short h) {
-        return (short) (h & FP16_COMBINED);
+        return (short) (h & FP16.EXPONENT_SIGNIFICAND_MASK);
     }
 
     /**
@@ -538,26 +512,18 @@
      * the result is zero (with the same sign)</li>
      * </ul>
      *
+     * <p class=note>
+     * <strong>Note:</strong> Unlike the identically named
+     * <code class=prettyprint>int java.lang.Math.round(float)</code> method,
+     * this returns a Half value stored in a short, <strong>not</strong> an
+     * actual short integer result.
+     *
      * @param h A half-precision float value
      * @return The value of the specified half-precision float rounded to the nearest
      *         half-precision float value
      */
     public static @HalfFloat short round(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int e = bits & 0x7fff;
-        int result = bits;
-
-        if (e < 0x3c00) {
-            result &= FP16_SIGN_MASK;
-            result |= (0x3c00 & (e >= 0x3800 ? 0xffff : 0x0));
-        } else if (e < 0x6400) {
-            e = 25 - (e >> 10);
-            int mask = (1 << e) - 1;
-            result += (1 << (e - 1));
-            result &= ~mask;
-        }
-
-        return (short) result;
+        return FP16.rint(h);
     }
 
     /**
@@ -577,21 +543,7 @@
      *         greater than or equal to the specified half-precision float value
      */
     public static @HalfFloat short ceil(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int e = bits & 0x7fff;
-        int result = bits;
-
-        if (e < 0x3c00) {
-            result &= FP16_SIGN_MASK;
-            result |= 0x3c00 & -(~(bits >> 15) & (e != 0 ? 1 : 0));
-        } else if (e < 0x6400) {
-            e = 25 - (e >> 10);
-            int mask = (1 << e) - 1;
-            result += mask & ((bits >> 15) - 1);
-            result &= ~mask;
-        }
-
-        return (short) result;
+        return FP16.ceil(h);
     }
 
     /**
@@ -611,21 +563,7 @@
      *         less than or equal to the specified half-precision float value
      */
     public static @HalfFloat short floor(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int e = bits & 0x7fff;
-        int result = bits;
-
-        if (e < 0x3c00) {
-            result &= FP16_SIGN_MASK;
-            result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0);
-        } else if (e < 0x6400) {
-            e = 25 - (e >> 10);
-            int mask = (1 << e) - 1;
-            result += mask & -(bits >> 15);
-            result &= ~mask;
-        }
-
-        return (short) result;
+        return FP16.floor(h);
     }
 
     /**
@@ -644,19 +582,7 @@
      *         half-precision float value
      */
     public static @HalfFloat short trunc(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int e = bits & 0x7fff;
-        int result = bits;
-
-        if (e < 0x3c00) {
-            result &= FP16_SIGN_MASK;
-        } else if (e < 0x6400) {
-            e = 25 - (e >> 10);
-            int mask = (1 << e) - 1;
-            result &= ~mask;
-        }
-
-        return (short) result;
+        return FP16.trunc(h);
     }
 
     /**
@@ -672,15 +598,7 @@
      * @return The smaller of the two specified half-precision values
      */
     public static @HalfFloat short min(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-
-        if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
-            return (x & FP16_SIGN_MASK) != 0 ? x : y;
-        }
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+        return FP16.min(x, y);
     }
 
     /**
@@ -697,15 +615,7 @@
      * @return The larger of the two specified half-precision values
      */
     public static @HalfFloat short max(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-
-        if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
-            return (x & FP16_SIGN_MASK) != 0 ? y : x;
-        }
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+        return FP16.max(x, y);
     }
 
     /**
@@ -719,11 +629,7 @@
      * @return True if x is less than y, false otherwise
      */
     public static boolean less(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+        return FP16.less(x, y);
     }
 
     /**
@@ -737,11 +643,7 @@
      * @return True if x is less than or equal to y, false otherwise
      */
     public static boolean lessEquals(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <=
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+        return FP16.lessEquals(x, y);
     }
 
     /**
@@ -755,11 +657,7 @@
      * @return True if x is greater than y, false otherwise
      */
     public static boolean greater(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+        return FP16.greater(x, y);
     }
 
     /**
@@ -773,11 +671,7 @@
      * @return True if x is greater than y, false otherwise
      */
     public static boolean greaterEquals(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >=
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+        return FP16.greaterEquals(x, y);
     }
 
     /**
@@ -791,10 +685,7 @@
      * @return True if x is equal to y, false otherwise
      */
     public static boolean equals(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return x == y || ((x | y) & FP16_COMBINED) == 0;
+        return FP16.equals(x, y);
     }
 
     /**
@@ -804,7 +695,7 @@
      * @return 1 if the value is positive, -1 if the value is negative
      */
     public static int getSign(@HalfFloat short h) {
-        return (h & FP16_SIGN_MASK) == 0 ? 1 : -1;
+        return (h & FP16.SIGN_MASK) == 0 ? 1 : -1;
     }
 
     /**
@@ -818,7 +709,7 @@
      * @return The unbiased exponent of the specified value
      */
     public static int getExponent(@HalfFloat short h) {
-        return ((h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK) - FP16_EXPONENT_BIAS;
+        return ((h >>> FP16.EXPONENT_SHIFT) & FP16.SHIFTED_EXPONENT_MASK) - FP16.EXPONENT_BIAS;
     }
 
     /**
@@ -829,7 +720,7 @@
      * @return The significand, or significand, of the specified vlaue
      */
     public static int getSignificand(@HalfFloat short h) {
-        return h & FP16_SIGNIFICAND_MASK;
+        return h & FP16.SIGNIFICAND_MASK;
     }
 
     /**
@@ -841,7 +732,7 @@
      *         false otherwise
      */
     public static boolean isInfinite(@HalfFloat short h) {
-        return (h & FP16_COMBINED) == FP16_EXPONENT_MAX;
+        return FP16.isInfinite(h);
     }
 
     /**
@@ -852,7 +743,7 @@
      * @return True if the value is a NaN, false otherwise
      */
     public static boolean isNaN(@HalfFloat short h) {
-        return (h & FP16_COMBINED) > FP16_EXPONENT_MAX;
+        return FP16.isNaN(h);
     }
 
     /**
@@ -866,7 +757,7 @@
      * @return True if the value is normalized, false otherwise
      */
     public static boolean isNormalized(@HalfFloat short h) {
-        return (h & FP16_EXPONENT_MAX) != 0 && (h & FP16_EXPONENT_MAX) != FP16_EXPONENT_MAX;
+        return FP16.isNormalized(h);
     }
 
     /**
@@ -885,35 +776,7 @@
      * @return A normalized single-precision float value
      */
     public static float toFloat(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int s = bits & FP16_SIGN_MASK;
-        int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
-        int m = (bits                        ) & FP16_SIGNIFICAND_MASK;
-
-        int outE = 0;
-        int outM = 0;
-
-        if (e == 0) { // Denormal or 0
-            if (m != 0) {
-                // Convert denorm fp16 into normalized fp32
-                float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m);
-                o -= FP32_DENORMAL_FLOAT;
-                return s == 0 ? o : -o;
-            }
-        } else {
-            outM = m << 13;
-            if (e == 0x1f) { // Infinite or NaN
-                outE = 0xff;
-                if (outM != 0) { // SNaNs are quieted
-                    outM |= FP32_QNAN_MASK;
-                }
-            } else {
-                outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS;
-            }
-        }
-
-        int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM;
-        return Float.intBitsToFloat(out);
+        return FP16.toFloat(h);
     }
 
     /**
@@ -940,44 +803,7 @@
      */
     @SuppressWarnings("StatementWithEmptyBody")
     public static @HalfFloat short toHalf(float f) {
-        int bits = Float.floatToRawIntBits(f);
-        int s = (bits >>> FP32_SIGN_SHIFT    );
-        int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK;
-        int m = (bits                        ) & FP32_SIGNIFICAND_MASK;
-
-        int outE = 0;
-        int outM = 0;
-
-        if (e == 0xff) { // Infinite or NaN
-            outE = 0x1f;
-            outM = m != 0 ? 0x200 : 0;
-        } else {
-            e = e - FP32_EXPONENT_BIAS + FP16_EXPONENT_BIAS;
-            if (e >= 0x1f) { // Overflow
-                outE = 0x31;
-            } else if (e <= 0) { // Underflow
-                if (e < -10) {
-                    // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
-                } else {
-                    // The fp32 value is a normalized float less than MIN_NORMAL,
-                    // we convert to a denorm fp16
-                    m = (m | 0x800000) >> (1 - e);
-                    if ((m & 0x1000) != 0) m += 0x2000;
-                    outM = m >> 13;
-                }
-            } else {
-                outE = e;
-                outM = m >> 13;
-                if ((m & 0x1000) != 0) {
-                    // Round to nearest "0.5" up
-                    int out = (outE << FP16_EXPONENT_SHIFT) | outM;
-                    out++;
-                    return (short) (out | (s << FP16_SIGN_SHIFT));
-                }
-            }
-        }
-
-        return (short) ((s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM);
+        return FP16.toHalf(f);
     }
 
     /**
@@ -1073,40 +899,6 @@
      */
     @NonNull
     public static String toHexString(@HalfFloat short h) {
-        StringBuilder o = new StringBuilder();
-
-        int bits = h & 0xffff;
-        int s = (bits >>> FP16_SIGN_SHIFT    );
-        int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
-        int m = (bits                        ) & FP16_SIGNIFICAND_MASK;
-
-        if (e == 0x1f) { // Infinite or NaN
-            if (m == 0) {
-                if (s != 0) o.append('-');
-                o.append("Infinity");
-            } else {
-                o.append("NaN");
-            }
-        } else {
-            if (s == 1) o.append('-');
-            if (e == 0) {
-                if (m == 0) {
-                    o.append("0x0.0p0");
-                } else {
-                    o.append("0x0.");
-                    String significand = Integer.toHexString(m);
-                    o.append(significand.replaceFirst("0{2,}$", ""));
-                    o.append("p-14");
-                }
-            } else {
-                o.append("0x1.");
-                String significand = Integer.toHexString(m);
-                o.append(significand.replaceFirst("0{2,}$", ""));
-                o.append('p');
-                o.append(Integer.toString(e - FP16_EXPONENT_BIAS));
-            }
-        }
-
-        return o.toString();
+        return FP16.toHexString(h);
     }
 }
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 1d0b9b6..1f49aa1 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -22,6 +22,7 @@
     <uses-permission android:name="android.permission.CONTROL_VPN" />
     <uses-permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
 
     <application android:label="VpnDialogs"
                  android:allowBackup="false">
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 846fcf8..ba4baf3 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -16,6 +16,10 @@
 
 package com.android.vpndialogs;
 
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -31,7 +35,6 @@
 import android.text.style.ClickableSpan;
 import android.util.Log;
 import android.view.View;
-import android.view.WindowManager;
 import android.widget.TextView;
 
 import com.android.internal.app.AlertActivity;
@@ -74,8 +77,9 @@
         setupAlert();
 
         getWindow().setCloseOnTouchOutside(false);
-        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+        getWindow().setType(TYPE_SYSTEM_ALERT);
+        getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
+        getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
     }
 
     @Override
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index 72ce9c4..0933974 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -16,6 +16,8 @@
 
 package com.android.vpndialogs;
 
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
@@ -78,6 +80,7 @@
         setupAlert();
 
         getWindow().setCloseOnTouchOutside(false);
+        getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
         Button button = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
         button.setFilterTouchesWhenObscured(true);
     }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 45584aa..4d8d992 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -12,7 +12,6 @@
     },
     srcs: [
         "java/**/*.java",
-        ":platformcompat_aidl",
         ":dumpstate_aidl",
         ":installd_aidl",
         ":storaged_aidl",
@@ -79,12 +78,4 @@
 prebuilt_etc {
     name: "gps_debug.conf",
     src: "java/com/android/server/location/gps_debug.conf",
-}
-
-filegroup {
-    name: "platformcompat_aidl",
-    srcs: [
-        "java/com/android/server/compat/IPlatformCompat.aidl",
-    ],
-    path: "java",
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 78b2914..e3cb5ad 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4202,6 +4202,7 @@
             }
             checkTime(startTime, "startProcess: done removing from pids map");
             app.setPid(0);
+            app.startSeq = 0;
         }
 
         if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
@@ -4393,6 +4394,14 @@
         app.killedByAm = false;
         app.removed = false;
         app.killed = false;
+        if (app.startSeq != 0) {
+            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+                    + " with non-zero startSeq:" + app.startSeq);
+        }
+        if (app.pid != 0) {
+            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+                    + " with non-zero pid:" + app.pid);
+        }
         final long startSeq = app.startSeq = ++mProcStartSeqCounter;
         app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
         if (mConstants.FLAG_PROCESS_START_ASYNC) {
@@ -4578,8 +4587,11 @@
         // If there is already an app occupying that pid that hasn't been cleaned up
         if (oldApp != null && !app.isolated) {
             // Clean up anything relating to this pid first
-            Slog.w(TAG, "Reusing pid " + pid
-                    + " while app is still mapped to it");
+          Slog.wtf(TAG, "handleProcessStartedLocked process:" + app.processName
+                  + " startSeq:" + app.startSeq
+                  + " pid:" + pid
+                  + " belongs to another existing app:" + oldApp.processName
+                  + " startSeq:" + oldApp.startSeq);
             cleanUpApplicationRecordLocked(oldApp, false, false, -1,
                     true /*replacingPid*/);
         }
@@ -7435,6 +7447,26 @@
             synchronized (mPidsSelfLocked) {
                 app = mPidsSelfLocked.get(pid);
             }
+            if (app != null && (app.startUid != callingUid || app.startSeq != startSeq)) {
+                String processName = null;
+                final ProcessRecord pending = mPendingStarts.get(startSeq);
+                if (pending != null) {
+                    processName = pending.processName;
+                }
+                final String msg = "attachApplicationLocked process:" + processName
+                      + " startSeq:" + startSeq
+                      + " pid:" + pid
+                      + " belongs to another existing app:" + app.processName
+                      + " startSeq:" + app.startSeq;
+                Slog.wtf(TAG, msg);
+                // SafetyNet logging for b/131105245.
+                EventLog.writeEvent(0x534e4554, "131105245", app.startUid, msg);
+                // If there is already an app occupying that pid that hasn't been cleaned up
+                cleanUpApplicationRecordLocked(app, false, false, -1,
+                    true /*replacingPid*/);
+                mPidsSelfLocked.remove(pid);
+                app = null;
+            }
         } else {
             app = null;
         }
@@ -7443,7 +7475,7 @@
         // update the internal state.
         if (app == null && startSeq > 0) {
             final ProcessRecord pending = mPendingStarts.get(startSeq);
-            if (pending != null && pending.startUid == callingUid
+            if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq
                     && handleProcessStartedLocked(pending, pid, pending.usingWrapper,
                             startSeq, true)) {
                 app = pending;
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 151ef49..bd0506b4 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -777,18 +777,24 @@
      * leaves the pinned mode.
      */
     private void lockKeyguardIfNeeded() {
+        if (shouldLockKeyguard()) {
+            mWindowManager.lockNow(null);
+            mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+            getLockPatternUtils().requireCredentialEntry(USER_ALL);
+        }
+    }
+
+    private boolean shouldLockKeyguard() {
+        // This functionality should be kept consistent with
+        // com.android.settings.security.ScreenPinningSettings (see b/127605586)
         try {
-            boolean shouldLockKeyguard = Settings.Secure.getIntForUser(
+            return Settings.Secure.getIntForUser(
                     mContext.getContentResolver(),
-                    Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
-                    USER_CURRENT) != 0;
-            if (shouldLockKeyguard) {
-                mWindowManager.lockNow(null);
-                mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
-                getLockPatternUtils().requireCredentialEntry(USER_ALL);
-            }
+                    Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, USER_CURRENT) != 0;
         } catch (Settings.SettingNotFoundException e) {
-            // No setting, don't lock.
+            // Log to SafetyNet for b/127605586
+            android.util.EventLog.writeEvent(0x534e4554, "127605586", -1, "");
+            return getLockPatternUtils().isSecure(USER_CURRENT);
         }
     }
 
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 3eea194..27050fa 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,6 +16,7 @@
 
 package com.android.server.compat;
 
+import android.compat.IPlatformCompat;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.util.Slog;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 73d160d..86d1212 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1314,11 +1314,15 @@
             mOffload.excludeDownstreamInterface(who.interfaceName());
             mForwardedDownstreams.remove(who);
 
-            // If this is a Wi-Fi interface, tell WifiManager of any errors.
+            // If this is a Wi-Fi interface, tell WifiManager of any errors
+            // or the inactive serving state.
             if (who.interfaceType() == TETHERING_WIFI) {
                 if (who.lastError() != TETHER_ERROR_NO_ERROR) {
                     getWifiManager().updateInterfaceIpState(
                             who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR);
+                } else {
+                    getWifiManager().updateInterfaceIpState(
+                            who.interfaceName(), IFACE_IP_MODE_UNSPECIFIED);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index aac8dad..88a1ef3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8900,10 +8900,10 @@
                     + " better than this " + pkg.getLongVersionCode());
         }
 
-        // Verify certificates against what was last scanned. If it is an updated priv app, we will
-        // force re-collecting certificate.
-        final boolean forceCollect = PackageManagerServiceUtils.isApkVerificationForced(
-                disabledPkgSetting);
+        // Verify certificates against what was last scanned. If there was an upgrade or this is an
+        // updated priv app, we will force re-collecting certificate.
+        final boolean forceCollect = mIsUpgrade ||
+                PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting);
         // Full APK verification can be skipped during certificate collection, only if the file is
         // in verified partition, or can be verified on access (when apk verity is enabled). In both
         // cases, only data in Signing Block is verified instead of the whole file.
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 863a0d8..5fbf241 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -390,7 +390,7 @@
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
 
-        // WHEN calling stopLockTaskMode on the root task
+        // WHEN calling clearLockedTasks on the root task
         mLockTaskController.clearLockedTasks("testClearLockedTasks");
 
         // THEN the lock task mode should be inactive
@@ -404,7 +404,81 @@
     }
 
     @Test
-    public void testUpdateLockTaskPackages() throws Exception {
+    public void testClearLockedTasks_noLockSetting_noPassword_deviceIsUnlocked() throws Exception {
+        // GIVEN There is no setting set for LOCK_TO_APP_EXIT_LOCKED
+        Settings.Secure.clearProviderForTest();
+
+        // AND no password is set
+        when(mLockPatternUtils.getKeyguardStoredPasswordQuality(anyInt()))
+                .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+
+        // AND there is a task record
+        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
+
+        // WHEN calling clearLockedTasks on the root task
+        mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+        // THEN the device should not be locked
+        verify(mWindowManager, never()).lockNow(any());
+    }
+
+    @Test
+    public void testClearLockedTasks_noLockSetting_password_deviceIsLocked() throws Exception {
+        // GIVEN There is no setting set for LOCK_TO_APP_EXIT_LOCKED
+        Settings.Secure.clearProviderForTest();
+
+        // AND a password is set
+        when(mLockPatternUtils.isSecure(anyInt()))
+                .thenReturn(true);
+
+        // AND there is a task record
+        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
+
+        // WHEN calling clearLockedTasks on the root task
+        mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+        // THEN the device should be locked
+        verify(mWindowManager, times(1)).lockNow(any());
+    }
+
+    @Test
+    public void testClearLockedTasks_lockSettingTrue_deviceIsLocked() throws Exception {
+        // GIVEN LOCK_TO_APP_EXIT_LOCKED is set to 1
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId());
+
+        // AND there is a task record
+        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
+
+        // WHEN calling clearLockedTasks on the root task
+        mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+        // THEN the device should be locked
+        verify(mWindowManager, times(1)).lockNow(any());
+    }
+
+    @Test
+    public void testClearLockedTasks_lockSettingFalse_doesNotRequirePassword() throws Exception {
+        // GIVEN LOCK_TO_APP_EXIT_LOCKED is set to 1
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId());
+
+        // AND there is a task record
+        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
+
+        // WHEN calling clearLockedTasks on the root task
+        mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+        // THEN the device should be unlocked
+        verify(mWindowManager, never()).lockNow(any());
+    }
+
+    @Test
+    public void testUpdateLockTaskPackages() {
         String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
         String[] whitelist2 = {TEST_PACKAGE_NAME};
 
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index c45bd6b..4a8bae7 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -33,6 +33,8 @@
  */
 public class Session {
 
+    public static final String LOG_TAG = "Session";
+
     public static final String START_SESSION = "START_SESSION";
     public static final String START_EXTERNAL_SESSION = "START_EXTERNAL_SESSION";
     public static final String CREATE_SUBSESSION = "CREATE_SUBSESSION";
@@ -45,6 +47,9 @@
     public static final String EXTERNAL_INDICATOR = "E-";
     public static final String TRUNCATE_STRING = "...";
 
+    // Prevent infinite recursion by setting a reasonable limit.
+    private static final int SESSION_RECURSION_LIMIT = 25;
+
     /**
      * Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
      * if the Session is canceled.
@@ -226,6 +231,15 @@
 
     // Builds full session id recursively
     private String getFullSessionId() {
+        return getFullSessionId(0);
+    }
+
+    // keep track of calls and bail if we hit the recursion limit
+    private String getFullSessionId(int parentCount) {
+        if (parentCount >= SESSION_RECURSION_LIMIT) {
+            Log.w(LOG_TAG, "getFullSessionId: Hit recursion limit!");
+            return TRUNCATE_STRING + mSessionId;
+        }
         // Cache mParentSession locally to prevent a concurrency problem where
         // Log.endParentSessions() is called while a logging statement is running (Log.i, for
         // example) and setting mParentSession to null in a different thread after the null check
@@ -235,42 +249,57 @@
             return mSessionId;
         } else {
             if (Log.VERBOSE) {
-                return parentSession.getFullSessionId() +
+                return parentSession.getFullSessionId(parentCount + 1)
                         // Append "_X" to subsession to show subsession designation.
-                        SESSION_SEPARATION_CHAR_CHILD + mSessionId;
+                        + SESSION_SEPARATION_CHAR_CHILD + mSessionId;
             } else {
                 // Only worry about the base ID at the top of the tree.
-                return parentSession.getFullSessionId();
+                return parentSession.getFullSessionId(parentCount + 1);
             }
 
         }
     }
 
-    // Print out the full Session tree from any subsession node
-    public String printFullSessionTree() {
-        // Get to the top of the tree
+    private Session getRootSession(String callingMethod) {
+        int currParentCount = 0;
         Session topNode = this;
         while (topNode.getParentSession() != null) {
+            if (currParentCount >= SESSION_RECURSION_LIMIT) {
+                Log.w(LOG_TAG, "getRootSession: Hit recursion limit from " + callingMethod);
+                break;
+            }
             topNode = topNode.getParentSession();
+            currParentCount++;
         }
-        return topNode.printSessionTree();
+        return topNode;
+    }
+
+    // Print out the full Session tree from any subsession node
+    public String printFullSessionTree() {
+        return getRootSession("printFullSessionTree").printSessionTree();
     }
 
     // Recursively move down session tree using DFS, but print out each node when it is reached.
-    public String printSessionTree() {
+    private String printSessionTree() {
         StringBuilder sb = new StringBuilder();
-        printSessionTree(0, sb);
+        printSessionTree(0, sb, 0);
         return sb.toString();
     }
 
-    private void printSessionTree(int tabI, StringBuilder sb) {
+    private void printSessionTree(int tabI, StringBuilder sb, int currChildCount) {
+        // Prevent infinite recursion.
+        if (currChildCount >= SESSION_RECURSION_LIMIT) {
+            Log.w(LOG_TAG, "printSessionTree: Hit recursion limit!");
+            sb.append(TRUNCATE_STRING);
+            return;
+        }
         sb.append(toString());
         for (Session child : mChildSessions) {
             sb.append("\n");
             for (int i = 0; i <= tabI; i++) {
                 sb.append("\t");
             }
-            child.printSessionTree(tabI + 1, sb);
+            child.printSessionTree(tabI + 1, sb, currChildCount + 1);
         }
     }
 
@@ -279,11 +308,17 @@
     // recent) will be truncated to "..."
     public String getFullMethodPath(boolean truncatePath) {
         StringBuilder sb = new StringBuilder();
-        getFullMethodPath(sb, truncatePath);
+        getFullMethodPath(sb, truncatePath, 0);
         return sb.toString();
     }
 
-    private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath) {
+    private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath,
+            int parentCount) {
+        if (parentCount >= SESSION_RECURSION_LIMIT) {
+            Log.w(LOG_TAG, "getFullMethodPath: Hit recursion limit!");
+            sb.append(TRUNCATE_STRING);
+            return;
+        }
         // Return cached value for method path. When returning the truncated path, recalculate the
         // full path without using the cached value.
         if (!TextUtils.isEmpty(mFullMethodPathCache) && !truncatePath) {
@@ -296,7 +331,7 @@
             // Check to see if the session has been renamed yet. If it has not, then the session
             // has not been continued.
             isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName);
-            parentSession.getFullMethodPath(sb, truncatePath);
+            parentSession.getFullMethodPath(sb, truncatePath, parentCount + 1);
             sb.append(SUBSESSION_SEPARATION_CHAR);
         }
         // Encapsulate the external session's method name so it is obvious what part of the session
@@ -319,13 +354,10 @@
             mFullMethodPathCache = sb.toString();
         }
     }
+
     // Recursively move to the top of the tree to see if the parent session is external.
     private boolean isSessionExternal() {
-        if (getParentSession() == null) {
-            return isExternal();
-        } else {
-            return getParentSession().isSessionExternal();
-        }
+        return getRootSession("isSessionExternal").isExternal();
     }
 
     @Override
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 3b4b868..b40cca6 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1442,6 +1442,9 @@
      * foreground call is ended.
      * <p>
      * Requires permission {@link android.Manifest.permission#ANSWER_PHONE_CALLS}.
+     * <p>
+     * Note: this method CANNOT be used to end ongoing emergency calls and will return {@code false}
+     * if an attempt is made to end an emergency call.
      *
      * @return {@code true} if there is a call which will be rejected or terminated, {@code false}
      * otherwise.
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index 8e1324b..6187e67 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -39,11 +39,11 @@
      */
     public static final int CODE_ERROR_UNSPECIFIED = 0;
     /**
-     * The operation has failed because there is no {@link ImsService} available to service it. This
-     * may be due to an {@link ImsService} crash or other illegal state.
+     * The operation has failed because there is no remote process available to service it. This
+     * may be due to a process crash or other illegal state.
      * <p>
      * This is a temporary error and the operation may be retried until the connection to the
-     * {@link ImsService} is restored.
+     * remote process is restored.
      */
     public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1;
 
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
index 691cfba..4b98b79 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
@@ -16,10 +16,38 @@
 
 package android.telephony.ims.aidl;
 
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+
+import java.util.List;
+
 /**
  * See RcsFeature for more information.
  * {@hide}
  */
 interface IImsRcsFeature {
-    //Empty Default Implementation
+    // Not oneway because we need to verify this completes before doing anything else.
+    void setListener(IRcsFeatureListener listener);
+    int queryCapabilityStatus();
+    // Inherited from ImsFeature
+    int getFeatureState();
+    oneway void addCapabilityCallback(IImsCapabilityCallback c);
+    oneway void removeCapabilityCallback(IImsCapabilityCallback c);
+    oneway void changeCapabilitiesConfiguration(in CapabilityChangeRequest r,
+            IImsCapabilityCallback c);
+    oneway void queryCapabilityConfiguration(int capability, int radioTech,
+            IImsCapabilityCallback c);
+    // RcsPresenceExchangeImplBase specific api
+    oneway void requestCapabilities(in List<Uri> uris, int operationToken);
+    oneway void updateCapabilities(in RcsContactUceCapability capabilities, int operationToken);
+    // RcsSipOptionsImplBase specific api
+    oneway void sendCapabilityRequest(in Uri contactUri,
+            in RcsContactUceCapability capabilities, int operationToken);
+    oneway void respondToCapabilityRequest(in String contactUri,
+            in RcsContactUceCapability ownCapabilities, int operationToken);
+    oneway void respondToCapabilityRequestWithError(in Uri contactUri, int code, in String reason,
+            int operationToken);
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl
new file mode 100644
index 0000000..881b477
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.aidl;
+
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+
+import java.util.List;
+
+/**
+ * Listener interface for updates from the RcsFeature back to the framework.
+ * {@hide}
+ */
+interface IRcsFeatureListener {
+    //RcsCapabilityExchange specific
+    oneway void onCommandUpdate(int commandCode, int operationToken);
+    // RcsPresenceExchangeImplBase Specific
+    oneway void onNetworkResponse(int code, in String reason, int operationToken);
+    oneway void onCapabilityRequestResponsePresence(in List<RcsContactUceCapability> infos,
+    int operationToken);
+    oneway void onNotifyUpdateCapabilities();
+    oneway void onUnpublish();
+    // RcsSipOptionsImplBase specific
+    oneway void onCapabilityRequestResponseOptions(int code, in String reason,
+            in RcsContactUceCapability info, int operationToken);
+    oneway void onRemoteCapabilityRequest(in Uri contactUri, in RcsContactUceCapability remoteInfo,
+            int operationToken);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 8f89899..3a9979d 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -33,12 +33,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.Map;
-import java.util.Set;
-import java.util.WeakHashMap;
 
 /**
  * Base class for all IMS features that are supported by the framework. Use a concrete subclass
@@ -52,35 +48,6 @@
     private static final String LOG_TAG = "ImsFeature";
 
     /**
-     * Action to broadcast when ImsService is up.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_UP =
-            "com.android.ims.IMS_SERVICE_UP";
-
-    /**
-     * Action to broadcast when ImsService is down.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_DOWN =
-            "com.android.ims.IMS_SERVICE_DOWN";
-
-    /**
-     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
-     * A long value; the phone ID corresponding to the IMS service coming up or down.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String EXTRA_PHONE_ID = "android:phone_id";
-
-    /**
      * Invalid feature value
      * @hide
      */
@@ -335,8 +302,8 @@
     /** @hide */
     protected final Object mLock = new Object();
 
-    private final Set<IImsFeatureStatusCallback> mStatusCallbacks =
-            Collections.newSetFromMap(new WeakHashMap<>());
+    private final RemoteCallbackList<IImsFeatureStatusCallback> mStatusCallbacks =
+            new RemoteCallbackList<>();
     private @ImsState int mState = STATE_UNAVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
     private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks =
@@ -397,9 +364,7 @@
             // If we have just connected, send queued status.
             c.notifyImsFeatureStatus(getFeatureState());
             // Add the callback if the callback completes successfully without a RemoteException.
-            synchronized (mLock) {
-                mStatusCallbacks.add(c);
-            }
+            mStatusCallbacks.register(c);
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
         }
@@ -411,29 +376,21 @@
      */
     @VisibleForTesting
     public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
-        synchronized (mLock) {
-            mStatusCallbacks.remove(c);
-        }
+        mStatusCallbacks.unregister(c);
     }
 
     /**
      * Internal method called by ImsFeature when setFeatureState has changed.
      */
     private void notifyFeatureState(@ImsState int state) {
-        synchronized (mLock) {
-            for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
-                    iter.hasNext(); ) {
-                IImsFeatureStatusCallback callback = iter.next();
-                try {
-                    Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
-                    callback.notifyImsFeatureStatus(state);
-                } catch (RemoteException e) {
-                    // remove if the callback is no longer alive.
-                    iter.remove();
-                    Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
-                }
+        mStatusCallbacks.broadcast((c) -> {
+            try {
+                c.notifyImsFeatureStatus(state);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping "
+                        + "callback.");
             }
-        }
+        });
     }
 
     /**
@@ -452,10 +409,23 @@
     /**
      * @hide
      */
-    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
+    final void removeCapabilityCallback(IImsCapabilityCallback c) {
         mCapabilityCallbacks.unregister(c);
     }
 
+    /**@hide*/
+    final void queryCapabilityConfigurationInternal(int capability, int radioTech,
+            IImsCapabilityCallback c) {
+        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
+        try {
+            if (c != null) {
+                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
+        }
+    }
+
     /**
      * @return the cached capabilities status for this feature.
      * @hide
@@ -484,31 +454,36 @@
     /**
      * Called by the ImsFeature when the capabilities status has changed.
      *
-     * @param c A {@link Capabilities} containing the new Capabilities status.
+     * @param caps the new {@link Capabilities} status of the {@link ImsFeature}.
      *
      * @hide
      */
-    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
+    protected final void notifyCapabilitiesStatusChanged(Capabilities caps) {
         synchronized (mLock) {
-            mCapabilityStatus = c.copy();
+            mCapabilityStatus = caps.copy();
         }
-        int count = mCapabilityCallbacks.beginBroadcast();
-        try {
-            for (int i = 0; i < count; i++) {
-                try {
-                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
-                            c.mCapabilities);
-                } catch (RemoteException e) {
-                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
-                            "callback.");
-                }
+        mCapabilityCallbacks.broadcast((callback) -> {
+            try {
+                callback.onCapabilitiesStatusChanged(caps.mCapabilities);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping "
+                        + "callback.");
             }
-        } finally {
-            mCapabilityCallbacks.finishBroadcast();
-        }
+        });
     }
 
     /**
+     * Provides the ImsFeature with the ability to return the framework Capability Configuration
+     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+     * includes a capability A to enable or disable, this method should return the correct enabled
+     * status for capability A.
+     * @param capability The capability that we are querying the configuration for.
+     * @return true if the capability is enabled, false otherwise.
+     * @hide
+     */
+    public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
+
+    /**
      * Features should override this method to receive Capability preference change requests from
      * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
      * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 898ca48..056a0ab 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -37,7 +37,6 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.ImsSmsImplBase;
 import android.telephony.ims.stub.ImsUtImplBase;
-import android.util.Log;
 
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsEcbm;
@@ -154,17 +153,13 @@
         @Override
         public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
                 IImsCapabilityCallback c) {
-            synchronized (mLock) {
-                MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
-            }
+            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
         }
 
         @Override
         public void queryCapabilityConfiguration(int capability, int radioTech,
                 IImsCapabilityCallback c) {
-            synchronized (mLock) {
-                queryCapabilityConfigurationInternal(capability, radioTech, c);
-            }
+            queryCapabilityConfigurationInternal(capability, radioTech, c);
         }
 
         @Override
@@ -381,18 +376,6 @@
         }
     }
 
-    private void queryCapabilityConfigurationInternal(int capability, int radioTech,
-            IImsCapabilityCallback c) {
-        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
-        try {
-            if (c != null) {
-                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
-            }
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
-        }
-    }
-
     /**
      * The current capability status that this MmTelFeature has defined is available. This
      * configuration will be used by the platform to figure out which capabilities are CURRENTLY
@@ -512,6 +495,7 @@
      * @param capability The capability that we are querying the configuration for.
      * @return true if the capability is enabled, false otherwise.
      */
+    @Override
     public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         // Base implementation - Override to provide functionality
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 5fae3ee..f69b434 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -16,14 +16,32 @@
 
 package android.telephony.ims.feature;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
 import android.telephony.ims.stub.RcsSipOptionsImplBase;
+import android.util.Log;
+
+import com.android.internal.util.FunctionalUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
 
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
@@ -33,10 +51,132 @@
 @SystemApi
 public class RcsFeature extends ImsFeature {
 
-    /**{@inheritDoc}*/
-    private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
-        // Empty Default Implementation.
-    };
+    private static final String LOG_TAG = "RcsFeature";
+
+    private static final class RcsFeatureBinder extends IImsRcsFeature.Stub {
+        // Reference the outer class in order to have better test coverage metrics instead of
+        // creating a inner class referencing the outer class directly.
+        private final RcsFeature mReference;
+        private final Executor mExecutor;
+
+        RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
+            mReference = classRef;
+            mExecutor = executor;
+        }
+
+        @Override
+        public void setListener(IRcsFeatureListener listener) {
+            mReference.setListener(listener);
+        }
+
+        @Override
+        public int queryCapabilityStatus() throws RemoteException {
+            return executeMethodAsyncForResult(
+                    () -> mReference.queryCapabilityStatus().mCapabilities,
+                    "queryCapabilityStatus");
+        }
+
+        @Override
+        public void addCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.addCapabilityCallback(c), "addCapabilityCallback");
+        }
+
+        @Override
+        public void removeCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.removeCapabilityCallback(c),
+                    "removeCapabilityCallback");
+        }
+
+        @Override
+        public void changeCapabilitiesConfiguration(CapabilityChangeRequest r,
+                IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.requestChangeEnabledCapabilities(r, c),
+                    "changeCapabilitiesConfiguration");
+        }
+
+        @Override
+        public void queryCapabilityConfiguration(int capability, int radioTech,
+                IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.queryCapabilityConfigurationInternal(capability,
+                    radioTech, c), "queryCapabilityConfiguration");
+        }
+
+        @Override
+        public int getFeatureState() throws RemoteException {
+            return executeMethodAsyncForResult(mReference::getFeatureState, "getFeatureState");
+        }
+
+        // RcsPresenceExchangeImplBase specific APIS
+        @Override
+        public void requestCapabilities(List<Uri> uris, int operationToken) throws RemoteException {
+            executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
+                    .requestCapabilities(uris, operationToken), "requestCapabilities");
+        }
+        @Override
+        public void updateCapabilities(RcsContactUceCapability capabilities, int operationToken)
+                throws RemoteException {
+            executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
+                            .updateCapabilities(capabilities, operationToken),
+                    "updateCapabilities");
+
+        }
+        // RcsSipOptionsImplBase specific APIS
+        @Override
+        public void sendCapabilityRequest(Uri contactUri, RcsContactUceCapability capabilities,
+                int operationToken) throws RemoteException {
+            executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+                            .sendCapabilityRequest(contactUri, capabilities, operationToken),
+                    "sendCapabilityRequest");
+
+        }
+        @Override
+        public void respondToCapabilityRequest(String contactUri,
+                RcsContactUceCapability ownCapabilities, int operationToken)
+                throws RemoteException {
+            executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+                            .respondToCapabilityRequest(contactUri, ownCapabilities,
+                                    operationToken), "respondToCapabilityRequest");
+
+        }
+        @Override
+        public void respondToCapabilityRequestWithError(Uri contactUri, int code, String reason,
+                int operationToken) throws RemoteException {
+            executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+                            .respondToCapabilityRequestWithError(contactUri, code, reason,
+                                    operationToken), "respondToCapabilityRequestWithError");
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(FunctionalUtils.ThrowingRunnable r, String errorLogName)
+                throws RemoteException {
+            // call with a clean calling identity on the executor and wait indefinitely for the
+            // future to return.
+            try {
+                CompletableFuture.runAsync(
+                        () -> Binder.withCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        private <T> T executeMethodAsyncForResult(FunctionalUtils.ThrowingSupplier<T> r,
+                String errorLogName) throws RemoteException {
+            // call with a clean calling identity on the executor and wait indefinitely for the
+            // future to return.
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> Binder.withCleanCallingIdentity(r), mExecutor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+    }
 
     /**
      * Contains the capabilities defined and supported by a {@link RcsFeature} in the
@@ -81,27 +221,66 @@
 
         /**@hide*/
         public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
+            super(capabilities);
+        }
 
+        /**@hide*/
+        private RcsImsCapabilities(Capabilities c) {
+            super(c.getMask());
         }
 
         /**@hide*/
         @Override
         public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
-
+            super.addCapabilities(capabilities);
         }
 
         /**@hide*/
         @Override
         public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
-
+            super.removeCapabilities(capabilities);
         }
 
         /**@hide*/
         @Override
         public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
-            return false;
+            return super.isCapable(capabilities);
         }
     }
+
+    private final RcsFeatureBinder mImsRcsBinder;
+    private IRcsFeatureListener mListenerBinder;
+    private RcsPresenceExchangeImplBase mPresExchange;
+    private RcsSipOptionsImplBase mSipOptions;
+
+    /**
+     * Create a new RcsFeature.
+     * <p>
+     * Method stubs called from the framework will be called asynchronously. To specify the
+     * {@link Executor} that the methods stubs will be called, use
+     * {@link RcsFeature#RcsFeature(Executor)} instead.
+     */
+    public RcsFeature() {
+        super();
+        // Run on the Binder threads that call them.
+        mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run);
+    }
+
+    /**
+     * Create a new RcsFeature using the Executor specified for methods being called by the
+     * framework.
+     * @param executor The executor for the framework to use when making calls to this service.
+     * @hide
+     */
+    public RcsFeature(@NonNull Executor executor) {
+        super();
+        if (executor == null) {
+            throw new IllegalArgumentException("executor can not be null.");
+        }
+        // Run on the Binder thread by default.
+        mImsRcsBinder = new RcsFeatureBinder(this, executor);
+    }
+
     /**
      * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is
      * set, the {@link RcsFeature} has brought up the capability and is ready for framework
@@ -111,7 +290,7 @@
      */
     @Override
     public final RcsImsCapabilities queryCapabilityStatus() {
-        throw new UnsupportedOperationException();
+        return new RcsImsCapabilities(super.queryCapabilityStatus());
     }
 
     /**
@@ -120,8 +299,11 @@
      * Call {@link #queryCapabilityStatus()} to return the current capability status.
      * @hide
      */
-    public final void notifyCapabilitiesStatusChanged(RcsImsCapabilities c) {
-        throw new UnsupportedOperationException();
+    public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
+        if (c == null) {
+            throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
+        }
+        super.notifyCapabilitiesStatusChanged(c);
     }
 
     /**
@@ -133,8 +315,10 @@
      * @hide
      */
     public boolean queryCapabilityConfiguration(
-            @RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
-        throw new UnsupportedOperationException();
+            @RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        // Base Implementation - Override to provide functionality
+        return false;
     }
     /**
      * Called from the framework when the {@link RcsImsCapabilities} that have been configured for
@@ -155,7 +339,7 @@
     @Override
     public void changeEnabledCapabilities(CapabilityChangeRequest request,
             CapabilityCallbackProxy c) {
-        throw new UnsupportedOperationException();
+        // Base Implementation - Override to provide functionality
     }
 
     /**
@@ -192,13 +376,6 @@
         return new RcsPresenceExchangeImplBase();
     }
 
-    /**
-     * Construct a new {@link RcsFeature} instance.
-     */
-    public RcsFeature() {
-        super();
-    }
-
     /**{@inheritDoc}*/
     @Override
     public void onFeatureRemoved() {
@@ -218,4 +395,40 @@
     public final IImsRcsFeature getBinder() {
         return mImsRcsBinder;
     }
+
+    /**@hide*/
+    public IRcsFeatureListener getListener() {
+        synchronized (mLock) {
+            return mListenerBinder;
+        }
+    }
+
+    private void setListener(IRcsFeatureListener listener) {
+        synchronized (mLock) {
+            mListenerBinder = listener;
+            if (mListenerBinder != null) {
+                onFeatureReady();
+            }
+        }
+    }
+
+    private RcsPresenceExchangeImplBase getPresenceExchangeInternal() {
+        synchronized (mLock) {
+            if (mPresExchange == null) {
+                mPresExchange = getPresenceExchangeImpl();
+                mPresExchange.initialize(this);
+            }
+            return mPresExchange;
+        }
+    }
+
+    private RcsSipOptionsImplBase getOptionsExchangeInternal() {
+        synchronized (mLock) {
+            if (mSipOptions == null) {
+                mSipOptions = getOptionsExchangeImpl();
+                mSipOptions.initialize(this);
+            }
+            return mSipOptions;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
index 289fd4c..fda295a 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
@@ -17,6 +17,11 @@
 package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -72,6 +77,24 @@
     })
     public @interface CommandCode {}
 
+
+    private RcsFeature mFeature;
+
+    /** @hide */
+    public final void initialize(RcsFeature feature) {
+        mFeature = feature;
+    }
+
+    /** @hide */
+    protected final IRcsFeatureListener getListener() throws ImsException {
+        IRcsFeatureListener listener = mFeature.getListener();
+        if (listener == null) {
+            throw new ImsException("Connection to Framework has not been established, wait for "
+                    + "onFeatureReady().", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+        return mFeature.getListener();
+    }
+
     /**
      * Provides the framework with an update as to whether or not a command completed successfully
      * locally. This includes capabilities requests and updates from the network. If it does not
@@ -82,8 +105,18 @@
      * @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further
      *             updates will be sent for this command using the associated operationToken.
      * @param operationToken the token associated with the pending command.
+     * @throws ImsException If this {@link RcsCapabilityExchange} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
-    public final void onCommandUpdate(@CommandCode int code, int operationToken) {
-        throw new UnsupportedOperationException();
+    public final void onCommandUpdate(@CommandCode int code, int operationToken)
+            throws ImsException {
+        try {
+            getListener().onCommandUpdate(code, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
index 4402470..055fca5 100644
--- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -19,7 +19,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
 import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -113,54 +117,95 @@
      * Provide the framework with a subsequent network response update to
      * {@link #updateCapabilities(RcsContactUceCapability, int)} and
      * {@link #requestCapabilities(List, int)} operations.
+     *
      * @param code The SIP response code sent from the network for the operation token specified.
      * @param reason The optional reason response from the network. If the network provided no
      *         reason with the code, the string should be empty.
      * @param operationToken The token associated with the operation this service is providing a
      *         response for.
+     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
     public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason,
-            int operationToken) {
-        throw new UnsupportedOperationException();
+            int operationToken) throws ImsException {
+        try {
+            getListener().onNetworkResponse(code, reason, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
      * Provides the framework with the requested contacts’ capabilities requested by the framework
-     * using {@link #requestCapabilities(List, int)} .
+     * using {@link #requestCapabilities(List, int)}.
+     *
+     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
     public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos,
-            int operationToken) {
-        throw new UnsupportedOperationException();
+            int operationToken) throws ImsException {
+        try {
+            getListener().onCapabilityRequestResponsePresence(infos, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
      * Trigger the framework to provide a capability update using
-     * {@link #updateCapabilities(RcsContactUceCapability, int)}. This is typically used when trying
-     * to generate an initial PUBLISH for a new subscription to the network.
+     * {@link #updateCapabilities(RcsContactUceCapability, int)}.
      * <p>
-     * The device will cache all presence publications after boot until this method is called once.
+     * This is typically used when trying to generate an initial PUBLISH for a new subscription to
+     * the network. The device will cache all presence publications after boot until this method is
+     * called once.
+     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
-    public final void onNotifyUpdateCapabilites() {
-        throw new UnsupportedOperationException();
+    public final void onNotifyUpdateCapabilites() throws ImsException {
+        try {
+            getListener().onNotifyUpdateCapabilities();
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
      * Notify the framework that the device’s capabilities have been unpublished from the network.
+     *
+     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
-    public final void onUnpublish() {
-        throw new UnsupportedOperationException();
+    public final void onUnpublish() throws ImsException {
+        try {
+            getListener().onUnpublish();
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
-     * The user capabilities of one or multiple contacts have been requested.
+     * The user capabilities of one or multiple contacts have been requested by the framework.
      * <p>
-     * This must be followed up with one call to {@link #onCommandUpdate(int, int)} with an update
-     * as to whether or not the command completed as well as subsequent network
-     * updates using {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
-     * {@link #onCapabilityRequestResponse(List, int)}  should be called with
-     * the presence information for the contacts specified.
-     * @param uris A {@link List} of the URIs that the framework is requesting the UCE capabilities
-     *          for.
+     * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
+     * indicate whether or not this operation succeeded.  If this operation succeeds, network
+     * response updates should be sent to the framework using
+     * {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
+     * {@link #onCapabilityRequestResponse(List, int)} should be called with the presence
+     * information for the contacts specified.
+     * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
+     *             capabilities for.
      * @param operationToken The token associated with this operation. Updates to this request using
      *         {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and
      *         {@link #onCapabilityRequestResponse(List, int)}  must use the same operation token
@@ -169,14 +214,20 @@
     public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "requestCapabilities called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 
     /**
-     * The capabilities of this device have been updated and should be published
-     * to the network. The framework will expect one {@link #onCommandUpdate(int, int)} call to
-     * indicate whether or not this operation failed first as well as network response
-     * updates to this update using {@link #onNetworkResponse(int, String, int)}.
+     * The capabilities of this device have been updated and should be published to the network.
+     * <p>
+     * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
+     * indicate whether or not this operation succeeded. If this operation succeeds, network
+     * response updates should be sent to the framework using
+     * {@link #onNetworkResponse(int, String, int)}.
      * @param capabilities The capabilities for this device.
      * @param operationToken The token associated with this operation. Any subsequent
      *         {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)}
@@ -186,6 +237,10 @@
             int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "updateCapabilities called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
index 3343074..1c68fc0 100644
--- a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
@@ -20,7 +20,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
 import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -87,10 +91,19 @@
      * @param info the contact's UCE capabilities associated with the capability request.
      * @param operationToken The token associated with the original capability request, set by
      *        {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
     public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason,
-            @Nullable RcsContactUceCapability info, int operationToken) {
-        throw new UnsupportedOperationException();
+            @Nullable RcsContactUceCapability info, int operationToken) throws ImsException {
+        try {
+            getListener().onCapabilityRequestResponseOptions(code, reason, info, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
@@ -104,10 +117,19 @@
      * @param operationToken An unique operation token that you have generated that will be returned
      *         by the framework in
      *         {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}.
+     * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
     public final void onRemoteCapabilityRequest(@NonNull Uri contactUri,
-            @NonNull RcsContactUceCapability remoteInfo, int operationToken) {
-        throw new UnsupportedOperationException();
+            @NonNull RcsContactUceCapability remoteInfo, int operationToken) throws ImsException {
+        try {
+            getListener().onRemoteCapabilityRequest(contactUri, remoteInfo, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
@@ -127,7 +149,11 @@
             @NonNull RcsContactUceCapability capabilities, int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 
     /**
@@ -145,7 +171,11 @@
             @NonNull RcsContactUceCapability ownCapabilities, int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 
     /**
@@ -164,6 +194,10 @@
             @SipResponseCode int code, @NonNull String reason, int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/BearerData.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsSubaddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/CdmaSmsSubaddress.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 8c522f4..c030c3e 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -469,6 +469,8 @@
         if (emulateInterfaceStatusChanged) {
             assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
             verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
+            verify(mWifiManager).updateInterfaceIpState(
+                    TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         }
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
@@ -534,6 +536,8 @@
         verify(mNMService, times(1)).startTethering(any(String[].class));
         verifyNoMoreInteractions(mNMService);
         verify(mWifiManager).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+        verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
         verifyNoMoreInteractions(mWifiManager);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
@@ -554,6 +558,8 @@
                 .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
         verify(mNMService, times(1)).stopTethering();
         verify(mNMService, times(1)).setIpForwardingEnabled(false);
+        verify(mWifiManager, times(3)).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
         // Asking for the last error after the per-interface state machine
@@ -739,6 +745,8 @@
 
         assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
+        verify(mWifiManager).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
     }
@@ -768,6 +776,8 @@
         verify(mNMService, times(1)).startTethering(any(String[].class));
         verifyNoMoreInteractions(mNMService);
         verify(mWifiManager).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+        verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         verifyNoMoreInteractions(mWifiManager);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
@@ -805,6 +815,8 @@
                 .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
         verify(mNMService, times(1)).stopTethering();
         verify(mNMService, times(1)).setIpForwardingEnabled(false);
+        verify(mWifiManager, times(3)).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
         // Asking for the last error after the per-interface state machine
@@ -842,6 +854,8 @@
         verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
         verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
         verify(mWifiManager).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+        verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
         assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);