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);