summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/Android.bp12
-rw-r--r--core/api/current.txt31
-rw-r--r--core/api/system-current.txt15
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/app/BroadcastOptions.java584
-rw-r--r--core/java/android/nfc/NfcAdapter.java53
-rw-r--r--core/java/android/os/BundleMerger.java388
-rw-r--r--core/java/android/os/Parcelable.java7
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java101
-rw-r--r--core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl21
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl5
-rw-r--r--core/res/res/values/config_telephony.xml5
-rw-r--r--core/res/res/values/strings.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/view/OWNERS1
-rw-r--r--core/tests/fuzzers/ParcelFuzzer/ReadUtils.java15
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java83
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java3
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java107
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java51
-rw-r--r--services/core/java/com/android/server/notification/OWNERS8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java28
-rw-r--r--services/tests/PackageManagerServiceTests/TEST_MAPPING32
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/dreams/OWNERS1
-rw-r--r--telecomm/java/android/telecom/Connection.java2
-rw-r--r--telephony/common/com/android/internal/telephony/TelephonyPermissions.java24
-rw-r--r--telephony/java/android/service/euicc/EuiccService.java19
-rw-r--r--telephony/java/android/service/euicc/GetEuiccProfileInfoListResult.java12
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java92
-rw-r--r--telephony/java/android/telephony/DataFailCause.java64
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java415
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java38
-rw-r--r--telephony/java/com/android/internal/telephony/ISub.aidl11
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl7
37 files changed, 1833 insertions, 416 deletions
diff --git a/api/Android.bp b/api/Android.bp
index 20528f2d67ee..4cb52bc2d29a 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -257,3 +257,15 @@ genrule {
out: ["combined-removed-dex.txt"],
cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)",
}
+
+java_genrule {
+ name: "api_fingerprint",
+ srcs: [
+ ":frameworks-base-api-current.txt",
+ ":frameworks-base-api-system-current.txt",
+ ":frameworks-base-api-module-lib-current.txt",
+ ":frameworks-base-api-system-server-current.txt",
+ ],
+ out: ["api_fingerprint.txt"],
+ cmd: "cat $(in) | md5sum | cut -d' ' -f1 > $(out)",
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index c381b16dc414..729d8dfa906a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5003,6 +5003,31 @@ package android.app {
field @NonNull public static final android.os.Parcelable.Creator<android.app.BackgroundServiceStartNotAllowedException> CREATOR;
}
+ public class BroadcastOptions {
+ method public void clearDeferralPolicy();
+ method public void clearDeliveryGroupMatchingFilter();
+ method public void clearDeliveryGroupMatchingKey();
+ method public void clearDeliveryGroupPolicy();
+ method @NonNull public static android.app.BroadcastOptions fromBundle(@NonNull android.os.Bundle);
+ method public int getDeferralPolicy();
+ method @Nullable public android.content.IntentFilter getDeliveryGroupMatchingFilter();
+ method @Nullable public String getDeliveryGroupMatchingKey();
+ method public int getDeliveryGroupPolicy();
+ method public boolean isShareIdentityEnabled();
+ method @NonNull public static android.app.BroadcastOptions makeBasic();
+ method @NonNull public android.app.BroadcastOptions setDeferralPolicy(int);
+ method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter);
+ method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
+ method @NonNull public android.app.BroadcastOptions setDeliveryGroupPolicy(int);
+ method @NonNull public android.app.BroadcastOptions setShareIdentityEnabled(boolean);
+ method @NonNull public android.os.Bundle toBundle();
+ field public static final int DEFERRAL_POLICY_DEFAULT = 0; // 0x0
+ field public static final int DEFERRAL_POLICY_NONE = 1; // 0x1
+ field public static final int DEFERRAL_POLICY_UNTIL_ACTIVE = 2; // 0x2
+ field public static final int DELIVERY_GROUP_POLICY_ALL = 0; // 0x0
+ field public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1; // 0x1
+ }
+
public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
ctor public DatePickerDialog(@NonNull android.content.Context);
ctor public DatePickerDialog(@NonNull android.content.Context, @StyleRes int);
@@ -41276,6 +41301,8 @@ package android.telephony {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public android.os.PersistableBundle getConfigForSubId(int, @NonNull java.lang.String...);
method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyConfigChangedForSubId(int);
+ method public void registerCarrierConfigChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.CarrierConfigManager.CarrierConfigChangeListener);
+ method public void unregisterCarrierConfigChangeListener(@NonNull android.telephony.CarrierConfigManager.CarrierConfigChangeListener);
field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1
field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2
@@ -41590,6 +41617,10 @@ package android.telephony {
field public static final String KEY_PREFIX = "bsf.";
}
+ public static interface CarrierConfigManager.CarrierConfigChangeListener {
+ method public void onCarrierConfigChanged(int, int, int, int);
+ }
+
public static final class CarrierConfigManager.Gps {
field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool";
field public static final String KEY_PREFIX = "gps.";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 89cae4bd55e4..f7fd03a8722a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -761,25 +761,18 @@ package android.app {
public class BroadcastOptions {
method public void clearRequireCompatChange();
- method public boolean isDeferUntilActive();
- method public boolean isPendingIntentBackgroundActivityLaunchAllowed();
- method public static android.app.BroadcastOptions makeBasic();
+ method public int getPendingIntentBackgroundActivityStartMode();
+ method @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed();
method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
- method @NonNull public android.app.BroadcastOptions setDeferUntilActive(boolean);
- method public void setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter);
- method public void setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
- method public void setDeliveryGroupPolicy(int);
method public void setDontSendToRestrictedApps(boolean);
- method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+ method @Deprecated public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+ method @NonNull public android.app.BroadcastOptions setPendingIntentBackgroundActivityStartMode(int);
method public void setRequireAllOfPermissions(@Nullable String[]);
method public void setRequireCompatChange(long, boolean);
method public void setRequireNoneOfPermissions(@Nullable String[]);
method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppAllowlist(long, int, int, @Nullable String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long);
- method public android.os.Bundle toBundle();
- field public static final int DELIVERY_GROUP_POLICY_ALL = 0; // 0x0
- field public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1; // 0x1
}
public class DownloadManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 2d86051d074e..11ae86edfb2b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -264,6 +264,7 @@ package android.app {
}
public class BroadcastOptions {
+ ctor public BroadcastOptions();
ctor public BroadcastOptions(@NonNull android.os.Bundle);
method @Deprecated public int getMaxManifestReceiverApiLevel();
method public long getTemporaryAppAllowlistDuration();
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 16c5b0845107..bccbb381bfb1 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -27,10 +27,12 @@ import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
+import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
+import android.os.BundleMerger;
import android.os.PowerExemptionManager;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.PowerExemptionManager.TempAllowListType;
@@ -45,28 +47,43 @@ import java.util.Objects;
* Helper class for building an options Bundle that can be used with
* {@link android.content.Context#sendBroadcast(android.content.Intent)
* Context.sendBroadcast(Intent)} and related methods.
- * {@hide}
*/
-@SystemApi
public class BroadcastOptions extends ComponentOptions {
+ private @Flags int mFlags;
private long mTemporaryAppAllowlistDuration;
private @TempAllowListType int mTemporaryAppAllowlistType;
private @ReasonCode int mTemporaryAppAllowlistReasonCode;
private @Nullable String mTemporaryAppAllowlistReason;
private int mMinManifestReceiverApiLevel = 0;
private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
- private boolean mDontSendToRestrictedApps = false;
- private boolean mAllowBackgroundActivityStarts;
private String[] mRequireAllOfPermissions;
private String[] mRequireNoneOfPermissions;
private long mRequireCompatChangeId = CHANGE_INVALID;
- private boolean mRequireCompatChangeEnabled = true;
- private boolean mIsAlarmBroadcast = false;
private long mIdForResponseEvent;
private @DeliveryGroupPolicy int mDeliveryGroupPolicy;
private @Nullable String mDeliveryGroupMatchingKey;
+ private @Nullable BundleMerger mDeliveryGroupExtrasMerger;
private @Nullable IntentFilter mDeliveryGroupMatchingFilter;
- private boolean mIsDeferUntilActive = false;
+ private @DeferralPolicy int mDeferralPolicy;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_DONT_SEND_TO_RESTRICTED_APPS,
+ FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+ FLAG_REQUIRE_COMPAT_CHANGE_ENABLED,
+ FLAG_IS_ALARM_BROADCAST,
+ FLAG_SHARE_IDENTITY,
+ FLAG_INTERACTIVE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
+ private static final int FLAG_DONT_SEND_TO_RESTRICTED_APPS = 1 << 0;
+ private static final int FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1 << 1;
+ private static final int FLAG_REQUIRE_COMPAT_CHANGE_ENABLED = 1 << 2;
+ private static final int FLAG_IS_ALARM_BROADCAST = 1 << 3;
+ private static final int FLAG_SHARE_IDENTITY = 1 << 4;
+ private static final int FLAG_INTERACTIVE = 1 << 5;
/**
* Change ID which is invalid.
@@ -96,6 +113,11 @@ public class BroadcastOptions extends ComponentOptions {
public static final long CHANGE_ALWAYS_DISABLED = 210856463L;
/**
+ * Corresponds to {@link #mFlags}.
+ */
+ private static final String KEY_FLAGS = "android:broadcast.flags";
+
+ /**
* How long to temporarily put an app on the power allowlist when executing this broadcast
* to it.
*/
@@ -124,18 +146,6 @@ public class BroadcastOptions extends ComponentOptions {
= "android:broadcast.maxManifestReceiverApiLevel";
/**
- * Corresponds to {@link #setDontSendToRestrictedApps}.
- */
- private static final String KEY_DONT_SEND_TO_RESTRICTED_APPS =
- "android:broadcast.dontSendToRestrictedApps";
-
- /**
- * Corresponds to {@link #setBackgroundActivityStartsAllowed}.
- */
- private static final String KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS =
- "android:broadcast.allowBackgroundActivityStarts";
-
- /**
* Corresponds to {@link #setRequireAllOfPermissions}
* @hide
*/
@@ -193,13 +203,44 @@ public class BroadcastOptions extends ComponentOptions {
"android:broadcast.idForResponseEvent";
/**
+ * Corresponds to {@link #setDeliveryGroupPolicy(int)}.
+ */
+ private static final String KEY_DELIVERY_GROUP_POLICY =
+ "android:broadcast.deliveryGroupPolicy";
+
+ /**
+ * Corresponds to {@link #setDeliveryGroupMatchingKey(String, String)}.
+ */
+ private static final String KEY_DELIVERY_GROUP_KEY =
+ "android:broadcast.deliveryGroupMatchingKey";
+
+ /**
+ * Corresponds to {@link #setDeliveryGroupExtrasMerger(BundleMerger)}.
+ */
+ private static final String KEY_DELIVERY_GROUP_EXTRAS_MERGER =
+ "android:broadcast.deliveryGroupExtrasMerger";
+
+ /**
+ * Corresponds to {@link #setDeliveryGroupMatchingFilter(IntentFilter)}.
+ */
+ private static final String KEY_DELIVERY_GROUP_MATCHING_FILTER =
+ "android:broadcast.deliveryGroupMatchingFilter";
+
+ /**
+ * Corresponds to {@link #setDeferralPolicy(int)}
+ */
+ private static final String KEY_DEFERRAL_POLICY =
+ "android:broadcast.deferralPolicy";
+
+ /**
* The list of delivery group policies which specify how multiple broadcasts belonging to
* the same delivery group has to be handled.
* @hide
*/
- @IntDef(flag = true, prefix = { "DELIVERY_GROUP_POLICY_" }, value = {
+ @IntDef(prefix = { "DELIVERY_GROUP_POLICY_" }, value = {
DELIVERY_GROUP_POLICY_ALL,
DELIVERY_GROUP_POLICY_MOST_RECENT,
+ DELIVERY_GROUP_POLICY_MERGED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeliveryGroupPolicy {}
@@ -207,27 +248,80 @@ public class BroadcastOptions extends ComponentOptions {
/**
* Delivery group policy that indicates that all the broadcasts in the delivery group
* need to be delivered as is.
- *
- * @hide
*/
- @SystemApi
public static final int DELIVERY_GROUP_POLICY_ALL = 0;
/**
* Delivery group policy that indicates that only the most recent broadcast in the delivery
* group need to be delivered and the rest can be dropped.
+ */
+ public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1;
+
+ /**
+ * Delivery group policy that indicates that the extras data from the broadcasts in the
+ * delivery group need to be merged into a single broadcast and the rest can be dropped.
*
* @hide
*/
- @SystemApi
- public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1;
+ public static final int DELIVERY_GROUP_POLICY_MERGED = 2;
+
+ /** {@hide} */
+ @IntDef(prefix = { "DEFERRAL_POLICY_" }, value = {
+ DEFERRAL_POLICY_DEFAULT,
+ DEFERRAL_POLICY_NONE,
+ DEFERRAL_POLICY_UNTIL_ACTIVE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeferralPolicy {}
+
+ /**
+ * Deferral policy that indicates no desire has been expressed, and that the
+ * system should use a reasonable default behavior.
+ */
+ public static final int DEFERRAL_POLICY_DEFAULT = 0;
- public static BroadcastOptions makeBasic() {
+ /**
+ * Deferral policy that indicates a strong desire that no receiver of this
+ * broadcast should be deferred.
+ */
+ public static final int DEFERRAL_POLICY_NONE = 1;
+
+ /**
+ * Deferral policy that indicates a strong desire that each receiver of this
+ * broadcast should be deferred until that receiver's process is in an
+ * active (non-cached) state. Whether an app's process state is considered
+ * active is independent of its standby bucket.
+ * <p>
+ * This policy only applies to runtime registered receivers of a broadcast,
+ * and does not apply to ordered broadcasts, alarm broadcasts, interactive
+ * broadcasts, or manifest broadcasts.
+ * <p>
+ * This policy means that a runtime registered receiver will not typically
+ * execute until that receiver's process is brought to an active state by
+ * some other action, such as a job, alarm, or service binding. As a result,
+ * the receiver may be delayed indefinitely.
+ * <p>
+ * When this policy is set on an unordered broadcast with a completion
+ * callback, the completion callback will run once all eligible processes
+ * have finished receiving the broadcast. Processes in inactive process
+ * state are not considered eligible and may not receive the broadcast prior
+ * to the completion callback.
+ */
+ public static final int DEFERRAL_POLICY_UNTIL_ACTIVE = 2;
+
+ /**
+ * Creates a basic {@link BroadcastOptions} with no options initially set.
+ *
+ * @return an instance of {@code BroadcastOptions} against which options can be set
+ */
+ public static @NonNull BroadcastOptions makeBasic() {
BroadcastOptions opts = new BroadcastOptions();
return opts;
}
- private BroadcastOptions() {
+ /** @hide */
+ @TestApi
+ public BroadcastOptions() {
super();
resetTemporaryAppAllowlist();
}
@@ -237,6 +331,7 @@ public class BroadcastOptions extends ComponentOptions {
public BroadcastOptions(@NonNull Bundle opts) {
super(opts);
// Match the logic in toBundle().
+ mFlags = opts.getInt(KEY_FLAGS, 0);
if (opts.containsKey(KEY_TEMPORARY_APP_ALLOWLIST_DURATION)) {
mTemporaryAppAllowlistDuration = opts.getLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION);
mTemporaryAppAllowlistType = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE);
@@ -249,15 +344,18 @@ public class BroadcastOptions extends ComponentOptions {
mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
Build.VERSION_CODES.CUR_DEVELOPMENT);
- mDontSendToRestrictedApps = opts.getBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, false);
- mAllowBackgroundActivityStarts = opts.getBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS,
- false);
mRequireAllOfPermissions = opts.getStringArray(KEY_REQUIRE_ALL_OF_PERMISSIONS);
mRequireNoneOfPermissions = opts.getStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS);
mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID);
- mRequireCompatChangeEnabled = opts.getBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, true);
mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
- mIsAlarmBroadcast = opts.getBoolean(KEY_ALARM_BROADCAST, false);
+ mDeliveryGroupPolicy = opts.getInt(KEY_DELIVERY_GROUP_POLICY,
+ DELIVERY_GROUP_POLICY_ALL);
+ mDeliveryGroupMatchingKey = opts.getString(KEY_DELIVERY_GROUP_KEY);
+ mDeliveryGroupExtrasMerger = opts.getParcelable(KEY_DELIVERY_GROUP_EXTRAS_MERGER,
+ BundleMerger.class);
+ mDeliveryGroupMatchingFilter = opts.getParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER,
+ IntentFilter.class);
+ mDeferralPolicy = opts.getInt(KEY_DEFERRAL_POLICY, DEFERRAL_POLICY_DEFAULT);
}
/**
@@ -265,8 +363,10 @@ public class BroadcastOptions extends ComponentOptions {
* power allowlist when this broadcast is being delivered to it.
* @param duration The duration in milliseconds; 0 means to not place on allowlist.
* @deprecated use {@link #setTemporaryAppAllowlist(long, int, int, String)} instead.
+ * @hide
*/
@Deprecated
+ @SystemApi
@RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
@@ -280,6 +380,8 @@ public class BroadcastOptions extends ComponentOptions {
* Set a duration for which the system should temporary place an application on the
* power allowlist when this broadcast is being delivered to it, specify the temp allowlist
* type.
+ * @hide
+ *
* @param duration the duration in milliseconds.
* 0 means to not place on allowlist, and clears previous call to this method.
* @param type one of {@link TempAllowListType}.
@@ -290,6 +392,7 @@ public class BroadcastOptions extends ComponentOptions {
* @param reason A human-readable reason explaining why the app is temp allowlisted. Only
* used for logging purposes. Could be null or empty string.
*/
+ @SystemApi
@RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
@@ -319,26 +422,6 @@ public class BroadcastOptions extends ComponentOptions {
}
/**
- * Set PendingIntent activity is allowed to be started in the background if the caller
- * can start background activities.
- * @hide
- */
- @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
- public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
- super.setPendingIntentBackgroundActivityLaunchAllowed(allowed);
- }
-
- /**
- * Get PendingIntent activity is allowed to be started in the background if the caller
- * can start background activities.
- * @hide
- */
- @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
- public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
- return super.isPendingIntentBackgroundActivityLaunchAllowed();
- }
-
- /**
* Return {@link #setTemporaryAppAllowlist}.
* @hide
*/
@@ -450,9 +533,15 @@ public class BroadcastOptions extends ComponentOptions {
* Sets whether pending intent can be sent for an application with background restrictions
* @param dontSendToRestrictedApps if true, pending intent will not be sent for an application
* with background restrictions. Default value is {@code false}
+ * @hide
*/
+ @SystemApi
public void setDontSendToRestrictedApps(boolean dontSendToRestrictedApps) {
- mDontSendToRestrictedApps = dontSendToRestrictedApps;
+ if (dontSendToRestrictedApps) {
+ mFlags |= FLAG_DONT_SEND_TO_RESTRICTED_APPS;
+ } else {
+ mFlags &= ~FLAG_DONT_SEND_TO_RESTRICTED_APPS;
+ }
}
/**
@@ -460,24 +549,31 @@ public class BroadcastOptions extends ComponentOptions {
* @return #setDontSendToRestrictedApps
*/
public boolean isDontSendToRestrictedApps() {
- return mDontSendToRestrictedApps;
+ return (mFlags & FLAG_DONT_SEND_TO_RESTRICTED_APPS) != 0;
}
/**
* Sets the process will be able to start activities from background for the duration of
* the broadcast dispatch. Default value is {@code false}
+ * @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND)
public void setBackgroundActivityStartsAllowed(boolean allowBackgroundActivityStarts) {
- mAllowBackgroundActivityStarts = allowBackgroundActivityStarts;
+ if (allowBackgroundActivityStarts) {
+ mFlags |= FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS;
+ } else {
+ mFlags &= ~FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS;
+ }
}
/**
* @hide
* @return #setAllowBackgroundActivityStarts
*/
+ @Deprecated
public boolean allowsBackgroundActivityStarts() {
- return mAllowBackgroundActivityStarts;
+ return (mFlags & FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0;
}
/**
@@ -528,6 +624,7 @@ public class BroadcastOptions extends ComponentOptions {
* <p>
* This requirement applies to both manifest registered and runtime
* registered receivers.
+ * @hide
*
* @param changeId the {@link ChangeId} to inspect
* @param enabled the required enabled state of the inspected
@@ -535,18 +632,24 @@ public class BroadcastOptions extends ComponentOptions {
* @see CompatChanges#isChangeEnabled
* @see #clearRequireCompatChange()
*/
+ @SystemApi
public void setRequireCompatChange(long changeId, boolean enabled) {
mRequireCompatChangeId = changeId;
- mRequireCompatChangeEnabled = enabled;
+ if (enabled) {
+ mFlags |= FLAG_REQUIRE_COMPAT_CHANGE_ENABLED;
+ } else {
+ mFlags &= ~FLAG_REQUIRE_COMPAT_CHANGE_ENABLED;
+ }
}
/**
* Clear any previously defined requirement for this broadcast requested via
* {@link #setRequireCompatChange(long, boolean)}.
+ * @hide
*/
+ @SystemApi
public void clearRequireCompatChange() {
- mRequireCompatChangeId = CHANGE_INVALID;
- mRequireCompatChangeEnabled = true;
+ setRequireCompatChange(CHANGE_INVALID, true);
}
/**
@@ -558,7 +661,11 @@ public class BroadcastOptions extends ComponentOptions {
* @param senderIsAlarm Whether the broadcast is alarm-triggered.
*/
public void setAlarmBroadcast(boolean senderIsAlarm) {
- mIsAlarmBroadcast = senderIsAlarm;
+ if (senderIsAlarm) {
+ mFlags |= FLAG_IS_ALARM_BROADCAST;
+ } else {
+ mFlags &= ~FLAG_IS_ALARM_BROADCAST;
+ }
}
/**
@@ -567,7 +674,44 @@ public class BroadcastOptions extends ComponentOptions {
* @hide
*/
public boolean isAlarmBroadcast() {
- return mIsAlarmBroadcast;
+ return (mFlags & FLAG_IS_ALARM_BROADCAST) != 0;
+ }
+
+ /**
+ * Sets whether the identity of the broadcasting app should be shared with all receivers
+ * that will receive this broadcast.
+ *
+ * <p>Use this option when broadcasting to a receiver that needs to know the identity of the
+ * broadcaster; with this set to {@code true}, the receiver will have access to the broadcasting
+ * app's package name and uid.
+ *
+ * <p>Defaults to {@code false} if not set.
+ *
+ * @param shareIdentityEnabled whether the broadcasting app's identity should be shared with the
+ * receiver
+ * @return {@code this} {@link BroadcastOptions} instance
+ * @see BroadcastReceiver#getSentFromUid()
+ * @see BroadcastReceiver#getSentFromPackage()
+ */
+ public @NonNull BroadcastOptions setShareIdentityEnabled(boolean shareIdentityEnabled) {
+ if (shareIdentityEnabled) {
+ mFlags |= FLAG_SHARE_IDENTITY;
+ } else {
+ mFlags &= ~FLAG_SHARE_IDENTITY;
+ }
+ return this;
+ }
+
+ /**
+ * Returns whether the broadcasting app has opted-in to sharing its identity with the receiver.
+ *
+ * @return {@code true} if the broadcasting app has opted in to sharing its identity
+ * @see #setShareIdentityEnabled(boolean)
+ * @see BroadcastReceiver#getSentFromUid()
+ * @see BroadcastReceiver#getSentFromPackage()
+ */
+ public boolean isShareIdentityEnabled() {
+ return (mFlags & FLAG_SHARE_IDENTITY) != 0;
}
/**
@@ -606,8 +750,8 @@ public class BroadcastOptions extends ComponentOptions {
@TestApi
public boolean testRequireCompatChange(int uid) {
if (mRequireCompatChangeId != CHANGE_INVALID) {
- return CompatChanges.isChangeEnabled(mRequireCompatChangeId,
- uid) == mRequireCompatChangeEnabled;
+ final boolean requireEnabled = (mFlags & FLAG_REQUIRE_COMPAT_CHANGE_ENABLED) != 0;
+ return CompatChanges.isChangeEnabled(mRequireCompatChangeId, uid) == requireEnabled;
} else {
return true;
}
@@ -637,15 +781,73 @@ public class BroadcastOptions extends ComponentOptions {
return mIdForResponseEvent;
}
+ /** {@hide} */
+ @Deprecated
+ public @NonNull BroadcastOptions setDeferUntilActive(boolean shouldDefer) {
+ if (shouldDefer) {
+ setDeferralPolicy(DEFERRAL_POLICY_UNTIL_ACTIVE);
+ } else {
+ setDeferralPolicy(DEFERRAL_POLICY_NONE);
+ }
+ return this;
+ }
+
+ /** {@hide} */
+ @Deprecated
+ public boolean isDeferUntilActive() {
+ return (mDeferralPolicy == DEFERRAL_POLICY_UNTIL_ACTIVE);
+ }
+
+ /**
+ * Sets deferral policy for this broadcast that specifies how this broadcast
+ * can be deferred for delivery at some future point.
+ */
+ public @NonNull BroadcastOptions setDeferralPolicy(@DeferralPolicy int deferralPolicy) {
+ mDeferralPolicy = deferralPolicy;
+ return this;
+ }
+
+ /**
+ * Gets deferral policy for this broadcast that specifies how this broadcast
+ * can be deferred for delivery at some future point.
+ */
+ public @DeferralPolicy int getDeferralPolicy() {
+ return mDeferralPolicy;
+ }
+
+ /**
+ * Clears any deferral policy for this broadcast that specifies how this
+ * broadcast can be deferred for delivery at some future point.
+ */
+ public void clearDeferralPolicy() {
+ mDeferralPolicy = DEFERRAL_POLICY_DEFAULT;
+ }
+
/**
* Set delivery group policy for this broadcast to specify how multiple broadcasts belonging to
* the same delivery group has to be handled.
- *
- * @hide
*/
- @SystemApi
- public void setDeliveryGroupPolicy(@DeliveryGroupPolicy int policy) {
+ @NonNull
+ public BroadcastOptions setDeliveryGroupPolicy(@DeliveryGroupPolicy int policy) {
mDeliveryGroupPolicy = policy;
+ return this;
+ }
+
+ /**
+ * Get the delivery group policy for this broadcast that specifies how multiple broadcasts
+ * belonging to the same delivery group has to be handled.
+ */
+ public @DeliveryGroupPolicy int getDeliveryGroupPolicy() {
+ return mDeliveryGroupPolicy;
+ }
+
+ /**
+ * Clears any previously set delivery group policies using
+ * {@link #setDeliveryGroupMatchingKey(String, String)} and resets the delivery group policy to
+ * the default value ({@link #DELIVERY_GROUP_POLICY_ALL}).
+ */
+ public void clearDeliveryGroupPolicy() {
+ mDeliveryGroupPolicy = DELIVERY_GROUP_POLICY_ALL;
}
/**
@@ -658,16 +860,36 @@ public class BroadcastOptions extends ComponentOptions {
* <p> If neither matching key using this API nor matching filter using
* {@link #setDeliveryGroupMatchingFilter(IntentFilter)} is specified, then by default
* {@link Intent#filterEquals(Intent)} will be used to identify the delivery group.
+ */
+ @NonNull
+ public BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String namespace,
+ @NonNull String key) {
+ Preconditions.checkArgument(!namespace.contains(":"),
+ "namespace should not contain ':'");
+ Preconditions.checkArgument(!key.contains(":"),
+ "key should not contain ':'");
+ mDeliveryGroupMatchingKey = namespace + ":" + key;
+ return this;
+ }
+
+ /**
+ * Return the namespace and key that is used to identify the delivery group that this
+ * broadcast belongs to.
*
- * @hide
+ * @return the delivery group namespace and key that was previously set using
+ * {@link #setDeliveryGroupMatchingKey(String, String)}, concatenated with a {@code :}.
*/
- @SystemApi
- public void setDeliveryGroupMatchingKey(@NonNull String namespace, @NonNull String key) {
- Preconditions.checkArgument(!namespace.contains("/"),
- "namespace should not contain '/'");
- Preconditions.checkArgument(!key.contains("/"),
- "key should not contain '/'");
- mDeliveryGroupMatchingKey = namespace + "/" + key;
+ @Nullable
+ public String getDeliveryGroupMatchingKey() {
+ return mDeliveryGroupMatchingKey;
+ }
+
+ /**
+ * Clears the namespace and key that was previously set using
+ * {@link #setDeliveryGroupMatchingKey(String, String)}.
+ */
+ public void clearDeliveryGroupMatchingKey() {
+ mDeliveryGroupMatchingKey = null;
}
/**
@@ -680,47 +902,158 @@ public class BroadcastOptions extends ComponentOptions {
* <p> If neither matching key using {@link #setDeliveryGroupMatchingKey(String, String)} nor
* matching filter using this API is specified, then by default
* {@link Intent#filterEquals(Intent)} will be used to identify the delivery group.
+ */
+ @NonNull
+ public BroadcastOptions setDeliveryGroupMatchingFilter(@NonNull IntentFilter matchingFilter) {
+ mDeliveryGroupMatchingFilter = Objects.requireNonNull(matchingFilter);
+ return this;
+ }
+
+ /**
+ * Return the {@link IntentFilter} object that is used to identify the delivery group
+ * that this broadcast belongs to.
+ *
+ * @return the {@link IntentFilter} object that was previously set using
+ * {@link #setDeliveryGroupMatchingFilter(IntentFilter)}.
+ */
+ @Nullable
+ public IntentFilter getDeliveryGroupMatchingFilter() {
+ return mDeliveryGroupMatchingFilter;
+ }
+
+ /**
+ * Clears the {@link IntentFilter} object that was previously set using
+ * {@link #setDeliveryGroupMatchingFilter(IntentFilter)}.
+ */
+ public void clearDeliveryGroupMatchingFilter() {
+ mDeliveryGroupMatchingFilter = null;
+ }
+
+ /**
+ * Set the {@link BundleMerger} that specifies how to merge the extras data from
+ * broadcasts in a delivery group.
+ *
+ * <p>Note that this value will be ignored if the delivery group policy is not set as
+ * {@link #DELIVERY_GROUP_POLICY_MERGED}.
*
* @hide
*/
- @SystemApi
- public void setDeliveryGroupMatchingFilter(@NonNull IntentFilter matchingFilter) {
- mDeliveryGroupMatchingFilter = Objects.requireNonNull(matchingFilter);
+ @NonNull
+ public BroadcastOptions setDeliveryGroupExtrasMerger(@NonNull BundleMerger extrasMerger) {
+ mDeliveryGroupExtrasMerger = Objects.requireNonNull(extrasMerger);
+ return this;
}
/**
- * Sets whether the broadcast should not run until the process is in an active process state
- * (ie, a process exists for the app and the app is not in a cached process state).
+ * Return the {@link BundleMerger} that specifies how to merge the extras data from
+ * broadcasts in a delivery group.
*
- * Whether an app's process state is considered active is independent of its standby bucket.
+ * @return the {@link BundleMerger} object that was previously set using
+ * {@link #setDeliveryGroupExtrasMerger(BundleMerger)}.
+ * @hide
+ */
+ @Nullable
+ public BundleMerger getDeliveryGroupExtrasMerger() {
+ return mDeliveryGroupExtrasMerger;
+ }
+
+ /**
+ * Clear the {@link BundleMerger} object that was previously set using
+ * {@link #setDeliveryGroupExtrasMerger(BundleMerger)}.
+ * @hide
+ */
+ public void clearDeliveryGroupExtrasMerger() {
+ mDeliveryGroupExtrasMerger = null;
+ }
+
+ /**
+ * Sets whether the broadcast should be considered as having originated from
+ * some direct interaction by the user such as a notification tap or button
+ * press. This signal is used internally to ensure the broadcast is
+ * delivered quickly with low latency.
*
- * A broadcast that is deferred until the process is active will not execute until the process
- * is brought to an active state by some other action, like a job, alarm, or service binding. As
- * a result, the broadcast may be delayed indefinitely. This deferral only applies to runtime
- * registered receivers of a broadcast. Any manifest receivers will run immediately, similar to
- * how a manifest receiver would start a new process in order to run a broadcast receiver.
+ * @hide
+ */
+ public @NonNull BroadcastOptions setInteractive(boolean interactive) {
+ if (interactive) {
+ mFlags |= FLAG_INTERACTIVE;
+ } else {
+ mFlags &= ~FLAG_INTERACTIVE;
+ }
+ return this;
+ }
+
+ /**
+ * Returns whether the broadcast should be considered as having originated
+ * from some direct interaction by the user such as a notification tap or
+ * button press.
*
- * Ordered broadcasts, alarm broadcasts, interactive broadcasts, and manifest broadcasts are
- * never deferred.
+ * @hide
+ */
+ public boolean isInteractive() {
+ return (mFlags & FLAG_INTERACTIVE) != 0;
+ }
+
+ /**
+ * Set PendingIntent activity is allowed to be started in the background if the caller
+ * can start background activities.
*
- * Unordered broadcasts and unordered broadcasts with completion callbacks may be
- * deferred. Completion callbacks for broadcasts deferred until active are
- * best-effort. Completion callbacks will run when all eligible processes have finished
- * executing the broadcast. Processes in inactive process states that defer the broadcast are
- * not considered eligible and may not execute the broadcast prior to the completion callback.
+ * @deprecated use #setPendingIntentBackgroundActivityStartMode(int) to set the full range
+ * of states
+ * @hide
+ */
+ @SystemApi
+ @Override
+ @Deprecated public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
+ super.setPendingIntentBackgroundActivityLaunchAllowed(allowed);
+ }
+
+ /**
+ * Get PendingIntent activity is allowed to be started in the background if the caller can start
+ * background activities.
*
+ * @deprecated use {@link #getPendingIntentBackgroundActivityStartMode()} since for apps
+ * targeting {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or higher this value might
+ * not match the actual behavior if the value was not explicitly set.
* @hide
*/
@SystemApi
- public @NonNull BroadcastOptions setDeferUntilActive(boolean shouldDefer) {
- mIsDeferUntilActive = shouldDefer;
+ @Override
+ @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
+ return super.isPendingIntentBackgroundActivityLaunchAllowed();
+ }
+
+
+ /**
+ * Sets the mode for allowing or denying the senders privileges to start background activities
+ * to the PendingIntent.
+ *
+ * This is typically used when executing {@link PendingIntent#send(Bundle)} or similar
+ * methods. A privileged sender of a PendingIntent should only grant
+ * MODE_BACKGROUND_ACTIVITY_START_ALLOWED if the PendingIntent is from a trusted source and/or
+ * executed on behalf the user.
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ // @Override // to narrow down the return type
+ public BroadcastOptions setPendingIntentBackgroundActivityStartMode(int state) {
+ // super.setPendingIntentBackgroundActivityStartMode(state);
return this;
}
- /** @hide */
+ /**
+ * Gets the mode for allowing or denying the senders privileges to start background activities
+ * to the PendingIntent.
+ *
+ * @see #setPendingIntentBackgroundActivityStartMode(int)
+ * @hide
+ */
@SystemApi
- public boolean isDeferUntilActive() {
- return mIsDeferUntilActive;
+ // @Override // to narrow down the return type
+ public int getPendingIntentBackgroundActivityStartMode() {
+ return 0;
+ // return super.getPendingIntentBackgroundActivityStartMode();
}
/**
@@ -730,31 +1063,29 @@ public class BroadcastOptions extends ComponentOptions {
* Note that the returned Bundle is still owned by the BroadcastOptions
* object; you must not modify it, but can supply it to the sendBroadcast
* methods that take an options Bundle.
+ *
+ * @throws IllegalStateException if the broadcast option values are inconsistent. For example,
+ * if the delivery group policy is specified as "MERGED" but no
+ * extras merger is supplied.
*/
@Override
- public Bundle toBundle() {
+ public @NonNull Bundle toBundle() {
Bundle b = super.toBundle();
+ if (mFlags != 0) {
+ b.putInt(KEY_FLAGS, mFlags);
+ }
if (isTemporaryAppAllowlistSet()) {
b.putLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION, mTemporaryAppAllowlistDuration);
b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE, mTemporaryAppAllowlistType);
b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, mTemporaryAppAllowlistReasonCode);
b.putString(KEY_TEMPORARY_APP_ALLOWLIST_REASON, mTemporaryAppAllowlistReason);
}
- if (mIsAlarmBroadcast) {
- b.putBoolean(KEY_ALARM_BROADCAST, true);
- }
if (mMinManifestReceiverApiLevel != 0) {
b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel);
}
if (mMaxManifestReceiverApiLevel != Build.VERSION_CODES.CUR_DEVELOPMENT) {
b.putInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, mMaxManifestReceiverApiLevel);
}
- if (mDontSendToRestrictedApps) {
- b.putBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, true);
- }
- if (mAllowBackgroundActivityStarts) {
- b.putBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS, true);
- }
if (mRequireAllOfPermissions != null) {
b.putStringArray(KEY_REQUIRE_ALL_OF_PERMISSIONS, mRequireAllOfPermissions);
}
@@ -763,11 +1094,44 @@ public class BroadcastOptions extends ComponentOptions {
}
if (mRequireCompatChangeId != CHANGE_INVALID) {
b.putLong(KEY_REQUIRE_COMPAT_CHANGE_ID, mRequireCompatChangeId);
- b.putBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, mRequireCompatChangeEnabled);
}
if (mIdForResponseEvent != 0) {
b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent);
}
- return b.isEmpty() ? null : b;
+ if (mDeliveryGroupPolicy != DELIVERY_GROUP_POLICY_ALL) {
+ b.putInt(KEY_DELIVERY_GROUP_POLICY, mDeliveryGroupPolicy);
+ }
+ if (mDeliveryGroupMatchingKey != null) {
+ b.putString(KEY_DELIVERY_GROUP_KEY, mDeliveryGroupMatchingKey);
+ }
+ if (mDeliveryGroupPolicy == DELIVERY_GROUP_POLICY_MERGED) {
+ if (mDeliveryGroupExtrasMerger != null) {
+ b.putParcelable(KEY_DELIVERY_GROUP_EXTRAS_MERGER,
+ mDeliveryGroupExtrasMerger);
+ } else {
+ throw new IllegalStateException("Extras merger cannot be empty "
+ + "when delivery group policy is 'MERGED'");
+ }
+ }
+ if (mDeliveryGroupMatchingFilter != null) {
+ b.putParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER, mDeliveryGroupMatchingFilter);
+ }
+ if (mDeferralPolicy != DEFERRAL_POLICY_DEFAULT) {
+ b.putInt(KEY_DEFERRAL_POLICY, mDeferralPolicy);
+ }
+ return b;
+ }
+
+ /**
+ * Returns a {@link BroadcastOptions} parsed from the given {@link Bundle},
+ * typically generated from {@link #toBundle()}.
+ */
+ public static @NonNull BroadcastOptions fromBundle(@NonNull Bundle options) {
+ return new BroadcastOptions(options);
+ }
+
+ /** {@hide} */
+ public static @Nullable BroadcastOptions fromBundleNullable(@Nullable Bundle options) {
+ return (options != null) ? new BroadcastOptions(options) : null;
}
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 1bb44af81cec..656cd997acbe 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -419,6 +419,7 @@ public final class NfcAdapter {
static boolean sIsInitialized = false;
static boolean sHasNfcFeature;
static boolean sHasBeamFeature;
+ static boolean sHasCeFeature;
// Final after first constructor, except for
// attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -616,11 +617,13 @@ public final class NfcAdapter {
pm = context.getPackageManager();
sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
sHasBeamFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM);
- boolean hasHceFeature =
+ sHasCeFeature =
pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
- || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF);
+ || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)
+ || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)
+ || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE);
/* is this device meant to have NFC */
- if (!sHasNfcFeature && !hasHceFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
Log.v(TAG, "this device does not have NFC support");
throw new UnsupportedOperationException();
}
@@ -643,7 +646,7 @@ public final class NfcAdapter {
throw new UnsupportedOperationException();
}
}
- if (hasHceFeature) {
+ if (sHasCeFeature) {
try {
sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface();
} catch (RemoteException e) {
@@ -1846,7 +1849,7 @@ public final class NfcAdapter {
@SystemApi
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public boolean enableSecureNfc(boolean enable) {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -1871,10 +1874,13 @@ public final class NfcAdapter {
* Checks if the device supports Secure NFC functionality.
*
* @return True if device supports Secure NFC, false otherwise
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
*/
public boolean isSecureNfcSupported() {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -1900,11 +1906,14 @@ public final class NfcAdapter {
* such as their relative positioning on the device.
*
* @return Information on the nfc antenna(s) on the device.
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
*/
@Nullable
public NfcAntennaInfo getNfcAntennaInfo() {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -1929,12 +1938,15 @@ public final class NfcAdapter {
* Checks Secure NFC feature is enabled.
*
* @return True if Secure NFC is enabled, false otherwise
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
* @throws UnsupportedOperationException if device doesn't support
* Secure NFC functionality. {@link #isSecureNfcSupported}
*/
public boolean isSecureNfcEnabled() {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -2281,14 +2293,17 @@ public final class NfcAdapter {
* always on.
* @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is
* disabled), if false the NFCC will follow completely the Nfc adapter state.
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
* @return void
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
public boolean setControllerAlwaysOn(boolean value) {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -2313,7 +2328,10 @@ public final class NfcAdapter {
* Checks NFC controller always on feature is enabled.
*
* @return True if NFC controller always on is enabled, false otherwise
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
* @hide
*/
@SystemApi
@@ -2341,13 +2359,16 @@ public final class NfcAdapter {
* Checks if the device supports NFC controller always on functionality.
*
* @return True if device supports NFC controller always on, false otherwise
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
public boolean isControllerAlwaysOnSupported() {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
diff --git a/core/java/android/os/BundleMerger.java b/core/java/android/os/BundleMerger.java
new file mode 100644
index 000000000000..857aaf57f640
--- /dev/null
+++ b/core/java/android/os/BundleMerger.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.function.BinaryOperator;
+
+/**
+ * Configured rules for merging two {@link Bundle} instances.
+ * <p>
+ * By default, values from both {@link Bundle} instances are blended together on
+ * a key-wise basis, and conflicting value definitions for a key are dropped.
+ * <p>
+ * Nuanced strategies for handling conflicting value definitions can be applied
+ * using {@link #setMergeStrategy(String, int)} and
+ * {@link #setDefaultMergeStrategy(int)}.
+ * <p>
+ * When conflicting values have <em>inconsistent</em> data types (such as trying
+ * to merge a {@link String} and a {@link Integer}), both conflicting values are
+ * rejected and the key becomes undefined, regardless of the requested strategy.
+ *
+ * @hide
+ */
+public class BundleMerger implements Parcelable {
+ private static final String TAG = "BundleMerger";
+
+ private @Strategy int mDefaultStrategy = STRATEGY_REJECT;
+
+ private final ArrayMap<String, Integer> mStrategies = new ArrayMap<>();
+
+ /**
+ * Merge strategy that rejects both conflicting values.
+ */
+ public static final int STRATEGY_REJECT = 0;
+
+ /**
+ * Merge strategy that selects the first of conflicting values.
+ */
+ public static final int STRATEGY_FIRST = 1;
+
+ /**
+ * Merge strategy that selects the last of conflicting values.
+ */
+ public static final int STRATEGY_LAST = 2;
+
+ /**
+ * Merge strategy that selects the "minimum" of conflicting values which are
+ * {@link Comparable} with each other.
+ */
+ public static final int STRATEGY_COMPARABLE_MIN = 3;
+
+ /**
+ * Merge strategy that selects the "maximum" of conflicting values which are
+ * {@link Comparable} with each other.
+ */
+ public static final int STRATEGY_COMPARABLE_MAX = 4;
+
+ /**
+ * Merge strategy that numerically adds both conflicting values.
+ */
+ public static final int STRATEGY_NUMBER_ADD = 10;
+
+ /**
+ * Merge strategy that numerically increments the first conflicting value by
+ * {@code 1} and ignores the last conflicting value.
+ */
+ public static final int STRATEGY_NUMBER_INCREMENT_FIRST = 20;
+
+ /**
+ * Merge strategy that numerically increments the first conflicting value by
+ * {@code 1} and also numerically adds both conflicting values.
+ */
+ public static final int STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD = 25;
+
+ /**
+ * Merge strategy that combines conflicting values using a boolean "and"
+ * operation.
+ */
+ public static final int STRATEGY_BOOLEAN_AND = 30;
+
+ /**
+ * Merge strategy that combines conflicting values using a boolean "or"
+ * operation.
+ */
+ public static final int STRATEGY_BOOLEAN_OR = 40;
+
+ /**
+ * Merge strategy that combines two conflicting array values by appending
+ * the last array after the first array.
+ */
+ public static final int STRATEGY_ARRAY_APPEND = 50;
+
+ /**
+ * Merge strategy that combines two conflicting {@link ArrayList} values by
+ * appending the last {@link ArrayList} after the first {@link ArrayList}.
+ */
+ public static final int STRATEGY_ARRAY_LIST_APPEND = 60;
+
+ @IntDef(flag = false, prefix = { "STRATEGY_" }, value = {
+ STRATEGY_REJECT,
+ STRATEGY_FIRST,
+ STRATEGY_LAST,
+ STRATEGY_COMPARABLE_MIN,
+ STRATEGY_COMPARABLE_MAX,
+ STRATEGY_NUMBER_ADD,
+ STRATEGY_NUMBER_INCREMENT_FIRST,
+ STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD,
+ STRATEGY_BOOLEAN_AND,
+ STRATEGY_BOOLEAN_OR,
+ STRATEGY_ARRAY_APPEND,
+ STRATEGY_ARRAY_LIST_APPEND,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Strategy {}
+
+ /**
+ * Create a empty set of rules for merging two {@link Bundle} instances.
+ */
+ public BundleMerger() {
+ }
+
+ private BundleMerger(@NonNull Parcel in) {
+ mDefaultStrategy = in.readInt();
+ final int N = in.readInt();
+ for (int i = 0; i < N; i++) {
+ mStrategies.put(in.readString(), in.readInt());
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mDefaultStrategy);
+ final int N = mStrategies.size();
+ out.writeInt(N);
+ for (int i = 0; i < N; i++) {
+ out.writeString(mStrategies.keyAt(i));
+ out.writeInt(mStrategies.valueAt(i));
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Configure the default merge strategy to be used when there isn't a
+ * more-specific strategy defined for a particular key via
+ * {@link #setMergeStrategy(String, int)}.
+ */
+ public void setDefaultMergeStrategy(@Strategy int strategy) {
+ mDefaultStrategy = strategy;
+ }
+
+ /**
+ * Configure the merge strategy to be used for the given key.
+ * <p>
+ * Subsequent calls for the same key will overwrite any previously
+ * configured strategy.
+ */
+ public void setMergeStrategy(@NonNull String key, @Strategy int strategy) {
+ mStrategies.put(key, strategy);
+ }
+
+ /**
+ * Return the merge strategy to be used for the given key, as defined by
+ * {@link #setMergeStrategy(String, int)}.
+ * <p>
+ * If no specific strategy has been configured for the given key, this
+ * returns {@link #setDefaultMergeStrategy(int)}.
+ */
+ public @Strategy int getMergeStrategy(@NonNull String key) {
+ return (int) mStrategies.getOrDefault(key, mDefaultStrategy);
+ }
+
+ /**
+ * Return a {@link BinaryOperator} which applies the strategies configured
+ * in this object to merge the two given {@link Bundle} arguments.
+ */
+ public BinaryOperator<Bundle> asBinaryOperator() {
+ return this::merge;
+ }
+
+ /**
+ * Apply the strategies configured in this object to merge the two given
+ * {@link Bundle} arguments.
+ *
+ * @return the merged {@link Bundle} result. If one argument is {@code null}
+ * it will return the other argument. If both arguments are null it
+ * will return {@code null}.
+ */
+ @SuppressWarnings("deprecation")
+ public @Nullable Bundle merge(@Nullable Bundle first, @Nullable Bundle last) {
+ if (first == null && last == null) {
+ return null;
+ }
+ if (first == null) {
+ first = Bundle.EMPTY;
+ }
+ if (last == null) {
+ last = Bundle.EMPTY;
+ }
+
+ // Start by bulk-copying all values without attempting to unpack any
+ // custom parcelables; we'll circle back to handle conflicts below
+ final Bundle res = new Bundle();
+ res.putAll(first);
+ res.putAll(last);
+
+ final ArraySet<String> conflictingKeys = new ArraySet<>();
+ conflictingKeys.addAll(first.keySet());
+ conflictingKeys.retainAll(last.keySet());
+ for (int i = 0; i < conflictingKeys.size(); i++) {
+ final String key = conflictingKeys.valueAt(i);
+ final int strategy = getMergeStrategy(key);
+ final Object firstValue = first.get(key);
+ final Object lastValue = last.get(key);
+ try {
+ res.putObject(key, merge(strategy, firstValue, lastValue));
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to merge key " + key + " with " + firstValue + " and "
+ + lastValue + " using strategy " + strategy, e);
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Merge the two given values. If only one of the values is defined, it
+ * always wins, otherwise the given strategy is applied.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static @Nullable Object merge(@Strategy int strategy,
+ @Nullable Object first, @Nullable Object last) {
+ if (first == null) return last;
+ if (last == null) return first;
+
+ if (first.getClass() != last.getClass()) {
+ throw new IllegalArgumentException("Merging requires consistent classes; first "
+ + first.getClass() + " last " + last.getClass());
+ }
+
+ switch (strategy) {
+ case STRATEGY_REJECT:
+ // Only actually reject when the values are different
+ if (Objects.deepEquals(first, last)) {
+ return first;
+ } else {
+ return null;
+ }
+ case STRATEGY_FIRST:
+ return first;
+ case STRATEGY_LAST:
+ return last;
+ case STRATEGY_COMPARABLE_MIN:
+ return comparableMin(first, last);
+ case STRATEGY_COMPARABLE_MAX:
+ return comparableMax(first, last);
+ case STRATEGY_NUMBER_ADD:
+ return numberAdd(first, last);
+ case STRATEGY_NUMBER_INCREMENT_FIRST:
+ return numberIncrementFirst(first, last);
+ case STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD:
+ return numberAdd(numberIncrementFirst(first, last), last);
+ case STRATEGY_BOOLEAN_AND:
+ return booleanAnd(first, last);
+ case STRATEGY_BOOLEAN_OR:
+ return booleanOr(first, last);
+ case STRATEGY_ARRAY_APPEND:
+ return arrayAppend(first, last);
+ case STRATEGY_ARRAY_LIST_APPEND:
+ return arrayListAppend(first, last);
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static @NonNull Object comparableMin(@NonNull Object first, @NonNull Object last) {
+ return ((Comparable<Object>) first).compareTo(last) < 0 ? first : last;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static @NonNull Object comparableMax(@NonNull Object first, @NonNull Object last) {
+ return ((Comparable<Object>) first).compareTo(last) >= 0 ? first : last;
+ }
+
+ private static @NonNull Object numberAdd(@NonNull Object first, @NonNull Object last) {
+ if (first instanceof Integer) {
+ return ((Integer) first) + ((Integer) last);
+ } else if (first instanceof Long) {
+ return ((Long) first) + ((Long) last);
+ } else if (first instanceof Float) {
+ return ((Float) first) + ((Float) last);
+ } else if (first instanceof Double) {
+ return ((Double) first) + ((Double) last);
+ } else {
+ throw new IllegalArgumentException("Unable to add " + first.getClass());
+ }
+ }
+
+ private static @NonNull Number numberIncrementFirst(@NonNull Object first,
+ @NonNull Object last) {
+ if (first instanceof Integer) {
+ return ((Integer) first) + 1;
+ } else if (first instanceof Long) {
+ return ((Long) first) + 1L;
+ } else {
+ throw new IllegalArgumentException("Unable to add " + first.getClass());
+ }
+ }
+
+ private static @NonNull Object booleanAnd(@NonNull Object first, @NonNull Object last) {
+ return ((Boolean) first) && ((Boolean) last);
+ }
+
+ private static @NonNull Object booleanOr(@NonNull Object first, @NonNull Object last) {
+ return ((Boolean) first) || ((Boolean) last);
+ }
+
+ private static @NonNull Object arrayAppend(@NonNull Object first, @NonNull Object last) {
+ if (!first.getClass().isArray()) {
+ throw new IllegalArgumentException("Unable to append " + first.getClass());
+ }
+ final Class<?> clazz = first.getClass().getComponentType();
+ final int firstLength = Array.getLength(first);
+ final int lastLength = Array.getLength(last);
+ final Object res = Array.newInstance(clazz, firstLength + lastLength);
+ System.arraycopy(first, 0, res, 0, firstLength);
+ System.arraycopy(last, 0, res, firstLength, lastLength);
+ return res;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static @NonNull Object arrayListAppend(@NonNull Object first, @NonNull Object last) {
+ if (!(first instanceof ArrayList)) {
+ throw new IllegalArgumentException("Unable to append " + first.getClass());
+ }
+ final ArrayList<Object> firstList = (ArrayList<Object>) first;
+ final ArrayList<Object> lastList = (ArrayList<Object>) last;
+ final ArrayList<Object> res = new ArrayList<>(firstList.size() + lastList.size());
+ res.addAll(firstList);
+ res.addAll(lastList);
+ return res;
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<BundleMerger> CREATOR =
+ new Parcelable.Creator<BundleMerger>() {
+ @Override
+ public BundleMerger createFromParcel(Parcel in) {
+ return new BundleMerger(in);
+ }
+
+ @Override
+ public BundleMerger[] newArray(int size) {
+ return new BundleMerger[size];
+ }
+ };
+}
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index a2b0486c1df5..f2b60a4e3988 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -16,8 +16,8 @@
package android.os;
-import android.annotation.NonNull;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import java.lang.annotation.Retention;
@@ -26,8 +26,9 @@ import java.lang.annotation.RetentionPolicy;
/**
* Interface for classes whose instances can be written to
* and restored from a {@link Parcel}. Classes implementing the Parcelable
- * interface must also have a non-null static field called <code>CREATOR</code>
- * of a type that implements the {@link Parcelable.Creator} interface.
+ * interface must also have a non-null public static field called
+ * <code>CREATOR</code> of a type that implements the {@link Parcelable.Creator}
+ * interface.
*
* <p>A typical implementation of Parcelable is:</p>
*
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index a3696e398668..4bcdf0d0d50d 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -45,6 +45,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.listeners.ListenerExecutor;
+import com.android.internal.telephony.ICarrierConfigChangeListener;
import com.android.internal.telephony.ICarrierPrivilegesCallback;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -54,8 +55,10 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -89,6 +92,14 @@ public class TelephonyRegistryManager {
IOnSubscriptionsChangedListener> mOpportunisticSubscriptionChangedListenerMap
= new HashMap<>();
+ /**
+ * A mapping between {@link CarrierConfigManager.CarrierConfigChangeListener} and its callback
+ * ICarrierConfigChangeListener.
+ */
+ private final ConcurrentHashMap<CarrierConfigManager.CarrierConfigChangeListener,
+ ICarrierConfigChangeListener>
+ mCarrierConfigChangeListenerMap = new ConcurrentHashMap<>();
+
/** @hide **/
public TelephonyRegistryManager(@NonNull Context context) {
@@ -1409,4 +1420,94 @@ public class TelephonyRegistryManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Register a {@link android.telephony.CarrierConfigManager.CarrierConfigChangeListener} to get
+ * notification when carrier configurations have changed.
+ *
+ * @param executor The executor on which the callback will be executed.
+ * @param listener The CarrierConfigChangeListener to be registered with.
+ */
+ public void addCarrierConfigChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CarrierConfigManager.CarrierConfigChangeListener listener) {
+ Objects.requireNonNull(executor, "Executor should be non-null.");
+ Objects.requireNonNull(listener, "Listener should be non-null.");
+ if (mCarrierConfigChangeListenerMap.get(listener) != null) {
+ Log.e(TAG, "registerCarrierConfigChangeListener: listener already present");
+ return;
+ }
+
+ ICarrierConfigChangeListener callback = new ICarrierConfigChangeListener.Stub() {
+ @Override
+ public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+ int specificCarrierId) {
+ Log.d(TAG, "onCarrierConfigChanged call in ICarrierConfigChangeListener callback");
+ final long identify = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> listener.onCarrierConfigChanged(slotIndex, subId,
+ carrierId, specificCarrierId));
+ } finally {
+ Binder.restoreCallingIdentity(identify);
+ }
+ }
+ };
+
+ try {
+ sRegistry.addCarrierConfigChangeListener(callback,
+ mContext.getOpPackageName(), mContext.getAttributionTag());
+ mCarrierConfigChangeListenerMap.put(listener, callback);
+ } catch (RemoteException re) {
+ // system server crashes
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregister to stop the notification when carrier configurations changed.
+ *
+ * @param listener The CarrierConfigChangeListener to be unregistered with.
+ */
+ public void removeCarrierConfigChangedListener(
+ @NonNull CarrierConfigManager.CarrierConfigChangeListener listener) {
+ Objects.requireNonNull(listener, "Listener should be non-null.");
+ if (mCarrierConfigChangeListenerMap.get(listener) == null) {
+ Log.e(TAG, "removeCarrierConfigChangedListener: listener was not present");
+ return;
+ }
+
+ try {
+ sRegistry.removeCarrierConfigChangeListener(
+ mCarrierConfigChangeListenerMap.get(listener), mContext.getOpPackageName());
+ mCarrierConfigChangeListenerMap.remove(listener);
+ } catch (RemoteException re) {
+ // System sever crashes
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notify the registrants the carrier configurations have changed.
+ *
+ * @param slotIndex The SIM slot index on which to monitor and get notification.
+ * @param subId The subscription on the SIM slot. May be
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ * @param carrierId The optional carrier Id, may be
+ * {@link TelephonyManager#UNKNOWN_CARRIER_ID}.
+ * @param specificCarrierId The optional specific carrier Id, may be {@link
+ * TelephonyManager#UNKNOWN_CARRIER_ID}.
+ */
+ public void notifyCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+ int specificCarrierId) {
+ // Only validate slotIndex, all others are optional and allowed to be invalid
+ if (!SubscriptionManager.isValidPhoneId(slotIndex)) {
+ Log.e(TAG, "notifyCarrierConfigChanged, ignored: invalid slotIndex " + slotIndex);
+ return;
+ }
+ try {
+ sRegistry.notifyCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl b/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl
new file mode 100644
index 000000000000..0f7ab0a3d4fb
--- /dev/null
+++ b/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+oneway interface ICarrierConfigChangeListener {
+ void onCarrierConfigChanged(int slotIndex, int subId, int carrierId, int specificCarrierId);
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index c7fa757ac0b7..747c40df9492 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,6 +32,7 @@ import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
+import com.android.internal.telephony.ICarrierConfigChangeListener;
import com.android.internal.telephony.ICarrierPrivilegesCallback;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -109,4 +110,8 @@ interface ITelephonyRegistry {
int phoneId, in List<String> privilegedPackageNames, in int[] privilegedUids);
void notifyCarrierServiceChanged(int phoneId, in String packageName, int uid);
+ void addCarrierConfigChangeListener(ICarrierConfigChangeListener listener,
+ String pkg, String featureId);
+ void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, String pkg);
+ void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, int specificCarrierId);
}
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index a1d73ff25cb8..94cf1b2ada0e 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -117,4 +117,9 @@
<!-- Whether using the new SubscriptionManagerService or the old SubscriptionController -->
<bool name="config_using_subscription_manager_service">false</bool>
<java-symbol type="bool" name="config_using_subscription_manager_service" />
+
+ <!-- Whether asynchronously update the subscription database or not. Async mode increases
+ the performance, but sync mode reduces the chance of database/cache out-of-sync. -->
+ <bool name="config_subscription_database_async_update">true</bool>
+ <java-symbol type="bool" name="config_subscription_database_async_update" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8764906c66cf..2c2993bbb00a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6336,4 +6336,7 @@ ul.</string>
<!-- Title for preference of the system default locale. [CHAR LIMIT=50]-->
<string name="system_locale_title">System default</string>
+
+ <!-- Display content to tell the user the sim card name and number-->
+ <string name="default_card_name">CARD <xliff:g id="cardNumber" example="1">%d</xliff:g></string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8477afa0f7fa..b16d9e5135a6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4884,4 +4884,5 @@
<java-symbol type="dimen" name="status_bar_height_default" />
<java-symbol type="bool" name="system_server_plays_face_haptics" />
+ <java-symbol type="string" name="default_card_name"/>
</resources>
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index a142e27a1ab0..2ca99943a8a0 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -1,4 +1,5 @@
# Accessibility
+per-file AccessibilityInteractionControllerTest.java = file:/services/accessibility/OWNERS
per-file WindowInfoTest.java = file:/services/accessibility/OWNERS
# Input
diff --git a/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java b/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java
index 0eff5f24f7e0..b5e5b258b7d6 100644
--- a/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java
+++ b/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java
@@ -97,7 +97,7 @@ public class ReadUtils {
public static ReadOperation[] READ_OPERATIONS =
new ReadOperation[] {
(parcel, provider) -> {
- parcel.setDataPosition(provider.consumeInt());
+ parcel.setDataPosition(provider.consumeInt(0, Integer.MAX_VALUE));
},
(parcel, provider) -> {
parcel.setDataCapacity(provider.consumeInt());
@@ -155,6 +155,7 @@ public class ReadUtils {
byte[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new byte[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -166,6 +167,7 @@ public class ReadUtils {
char[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new char[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -177,6 +179,7 @@ public class ReadUtils {
int[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new int[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -188,6 +191,7 @@ public class ReadUtils {
double[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new double[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -199,6 +203,7 @@ public class ReadUtils {
float[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new float[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -210,6 +215,7 @@ public class ReadUtils {
boolean[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new boolean[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -221,6 +227,7 @@ public class ReadUtils {
long[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new long[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -232,6 +239,7 @@ public class ReadUtils {
IBinder[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new IBinder[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -274,6 +282,7 @@ public class ReadUtils {
SingleDataParcelable[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new SingleDataParcelable[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -293,6 +302,7 @@ public class ReadUtils {
EmptyParcelable[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new EmptyParcelable[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -312,6 +322,7 @@ public class ReadUtils {
GenericDataParcelable[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new GenericDataParcelable[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -334,6 +345,7 @@ public class ReadUtils {
SomeParcelable[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new SomeParcelable[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
@@ -390,6 +402,7 @@ public class ReadUtils {
TestInterface[] array;
if (provider.consumeBoolean()) {
int pos = parcel.dataPosition();
+ if (pos < 0) return;
array = new TestInterface[Math.min(MAX_LEN, parcel.readInt())];
parcel.setDataPosition(pos);
} else {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index ca86021cd629..a69d3f0276f3 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -91,6 +91,7 @@ import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.ICarrierConfigChangeListener;
import com.android.internal.telephony.ICarrierPrivilegesCallback;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IPhoneStateListener;
@@ -154,6 +155,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
ICarrierPrivilegesCallback carrierPrivilegesCallback;
+ ICarrierConfigChangeListener carrierConfigChangeListener;
int callerUid;
int callerPid;
@@ -182,6 +184,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return carrierPrivilegesCallback != null;
}
+ boolean matchCarrierConfigChangeListener() {
+ return carrierConfigChangeListener != null;
+ }
+
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
@@ -200,6 +206,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " onOpportunisticSubscriptionsChangedListenererCallback="
+ onOpportunisticSubscriptionsChangedListenerCallback
+ " carrierPrivilegesCallback=" + carrierPrivilegesCallback
+ + " carrierConfigChangeListener=" + carrierConfigChangeListener
+ " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}";
}
}
@@ -2955,6 +2962,82 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
+ @Override
+ public void addCarrierConfigChangeListener(ICarrierConfigChangeListener listener,
+ String pkg, String featureId) {
+ final int callerUserId = UserHandle.getCallingUserId();
+ mAppOps.checkPackage(Binder.getCallingUid(), pkg);
+ if (VDBG) {
+ log("addCarrierConfigChangeListener pkg=" + pii(pkg) + " uid=" + Binder.getCallingUid()
+ + " myUserId=" + UserHandle.myUserId() + " callerUerId" + callerUserId
+ + " listener=" + listener + " listener.asBinder=" + listener.asBinder());
+ }
+
+ synchronized (mRecords) {
+ IBinder b = listener.asBinder();
+ boolean doesLimitApply = doesLimitApplyForListeners(Binder.getCallingUid(),
+ Process.myUid());
+ Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply);
+
+ if (r == null) {
+ loge("Can not create Record instance!");
+ return;
+ }
+
+ r.context = mContext;
+ r.carrierConfigChangeListener = listener;
+ r.callingPackage = pkg;
+ r.callingFeatureId = featureId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
+ r.eventList = new ArraySet<>();
+ if (DBG) {
+ log("addCarrierConfigChangeListener: Register r=" + r);
+ }
+ }
+ }
+
+ @Override
+ public void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener,
+ String pkg) {
+ if (DBG) log("removeCarrierConfigChangeListener listener=" + listener + ", pkg=" + pkg);
+ mAppOps.checkPackage(Binder.getCallingUid(), pkg);
+ remove(listener.asBinder());
+ }
+
+ @Override
+ public void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId,
+ int specificCarrierId) {
+ if (!validatePhoneId(phoneId)) {
+ throw new IllegalArgumentException("Invalid phoneId: " + phoneId);
+ }
+ if (!checkNotifyPermission("notifyCarrierConfigChanged")) {
+ loge("Caller has no notify permission!");
+ return;
+ }
+ if (VDBG) {
+ log("notifyCarrierConfigChanged: phoneId=" + phoneId + ", subId=" + subId
+ + ", carrierId=" + carrierId + ", specificCarrierId=" + specificCarrierId);
+ }
+
+ synchronized (mRecords) {
+ mRemoveList.clear();
+ for (Record r : mRecords) {
+ // Listeners are "global", neither per-slot nor per-sub, so no idMatch check here
+ if (!r.matchCarrierConfigChangeListener()) {
+ continue;
+ }
+ try {
+ r.carrierConfigChangeListener.onCarrierConfigChanged(phoneId, subId, carrierId,
+ specificCarrierId);
+ } catch (RemoteException re) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
@NeverCompile // Avoid size overhead of debugging code.
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index aec60deba933..96fcd9d937f1 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -487,7 +487,8 @@ public class CameraServiceProxy extends SystemService
if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) {
for (ActivityManager.RecentTaskInfo task : recentTasks.getList()) {
- if (packageName.equals(task.topActivityInfo.packageName)) {
+ if (task.topActivityInfo != null && packageName.equals(
+ task.topActivityInfo.packageName)) {
taskInfo = new TaskInfo();
taskInfo.frontTaskId = task.taskId;
taskInfo.isResizeable =
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1ce917cb7841..6d3f8fd77232 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -21,6 +21,7 @@ import static android.Manifest.permission.CONTROL_VPN;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
@@ -29,6 +30,7 @@ import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO;
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_AUTO;
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.UserHandle.PER_USER_RANGE;
+import static android.telephony.CarrierConfigManager.KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
@@ -79,7 +81,9 @@ import android.net.NetworkInfo.DetailedState;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.NetworkScore;
+import android.net.NetworkSpecifier;
import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
@@ -127,12 +131,16 @@ import android.security.keystore.KeyProperties;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyPermission;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Log;
import android.util.Range;
+import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -268,6 +276,10 @@ public class Vpn {
private final ConnectivityManager mConnectivityManager;
private final AppOpsManager mAppOpsManager;
private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager;
+ private final TelephonyManager mTelephonyManager;
+ private final CarrierConfigManager mCarrierConfigManager;
+ private final SubscriptionManager mSubscriptionManager;
+
// The context is for specific user which is created from mUserId
private final Context mUserIdContext;
@VisibleForTesting final Dependencies mDeps;
@@ -314,6 +326,14 @@ public class Vpn {
private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS);
/**
+ * Cached Map of <subscription ID, keepalive delay ms> since retrieving the PersistableBundle
+ * and the target value from CarrierConfigManager is somewhat expensive as it has hundreds of
+ * fields. This cache is cleared when the carrier config changes to ensure data freshness.
+ */
+ @GuardedBy("this")
+ private final SparseArray<Integer> mCachedKeepalivePerSubId = new SparseArray<>();
+
+ /**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
* only applies to {@link VpnService} connections.
*/
@@ -626,6 +646,10 @@ public class Vpn {
mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
mConnectivityDiagnosticsManager =
mContext.getSystemService(ConnectivityDiagnosticsManager.class);
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+
mDeps = deps;
mNms = netService;
mNetd = netd;
@@ -2893,6 +2917,24 @@ public class Vpn {
*/
private int mRetryCount = 0;
+ private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+ new CarrierConfigManager.CarrierConfigChangeListener() {
+ @Override
+ public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+ int specificCarrierId) {
+ synchronized (Vpn.this) {
+ mCachedKeepalivePerSubId.remove(subId);
+
+ // Ignore stale runner.
+ if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
+
+ maybeMigrateIkeSession(mActiveNetwork);
+ }
+ // TODO: update the longLivedTcpConnectionsExpensive value in the
+ // networkcapabilities of the VPN network.
+ }
+ };
+
IkeV2VpnRunner(
@NonNull Ikev2VpnProfile profile, @NonNull ScheduledThreadPoolExecutor executor) {
super(TAG);
@@ -2918,6 +2960,9 @@ public class Vpn {
setVpnNetworkPreference(mSessionKey,
createUserAndRestrictedProfilesRanges(mUserId,
mConfig.allowedApplications, mConfig.disallowedApplications));
+
+ mCarrierConfigManager.registerCarrierConfigChangeListener(mExecutor,
+ mCarrierConfigChangeListener);
}
@Override
@@ -3257,8 +3302,6 @@ public class Vpn {
mUnderlyingLinkProperties = null;
mUnderlyingNetworkCapabilities = null;
mRetryCount = 0;
-
- startOrMigrateIkeSession(network);
}
@NonNull
@@ -3356,9 +3399,36 @@ public class Vpn {
}
private int guessNattKeepaliveTimerForNetwork() {
- // TODO : guess the keepalive delay based on carrier if auto keepalive timer is
- // enabled
- return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ final int subId = getCellSubIdForNetworkCapabilities(mUnderlyingNetworkCapabilities);
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Log.d(TAG, "Underlying network is not a cellular network");
+ return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ }
+
+ synchronized (Vpn.this) {
+ if (mCachedKeepalivePerSubId.contains(subId)) {
+ Log.d(TAG, "Get cached keepalive config");
+ return mCachedKeepalivePerSubId.get(subId);
+ }
+
+ final TelephonyManager perSubTm = mTelephonyManager.createForSubscriptionId(subId);
+ if (perSubTm.getSimApplicationState() != TelephonyManager.SIM_STATE_LOADED) {
+ Log.d(TAG, "SIM card is not ready on sub " + subId);
+ return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ }
+
+ final PersistableBundle carrierConfig =
+ mCarrierConfigManager.getConfigForSubId(subId);
+ if (!CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
+ return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+ }
+
+ final int natKeepalive =
+ carrierConfig.getInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT);
+ mCachedKeepalivePerSubId.put(subId, natKeepalive);
+ Log.d(TAG, "Get customized keepalive=" + natKeepalive);
+ return natKeepalive;
+ }
}
boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork) {
@@ -3456,7 +3526,15 @@ public class Vpn {
/** Called when the NetworkCapabilities of underlying network is changed */
public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
+ final NetworkCapabilities oldNc = mUnderlyingNetworkCapabilities;
mUnderlyingNetworkCapabilities = nc;
+ if (oldNc == null) {
+ // A new default network is available.
+ startOrMigrateIkeSession(mActiveNetwork);
+ } else if (!nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) {
+ // Renew carrierConfig values.
+ maybeMigrateIkeSession(mActiveNetwork);
+ }
}
/** Called when the LinkProperties of underlying network is changed */
@@ -3812,6 +3890,8 @@ public class Vpn {
resetIkeState();
+ mCarrierConfigManager.unregisterCarrierConfigChangeListener(
+ mCarrierConfigChangeListener);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
mDiagnosticsCallback);
@@ -4804,6 +4884,8 @@ public class Vpn {
pw.println("Reset session scheduled");
}
}
+ pw.println("mCachedKeepalivePerSubId=" + mCachedKeepalivePerSubId);
+
pw.println("mUnderlyNetworkChanges (most recent first):");
pw.increaseIndent();
mUnderlyNetworkChanges.reverseDump(pw);
@@ -4815,4 +4897,19 @@ public class Vpn {
pw.decreaseIndent();
}
}
+
+ private static int getCellSubIdForNetworkCapabilities(@Nullable NetworkCapabilities nc) {
+ if (nc == null) return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ if (!nc.hasTransport(TRANSPORT_CELLULAR)) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ final NetworkSpecifier specifier = nc.getNetworkSpecifier();
+ if (specifier instanceof TelephonyNetworkSpecifier) {
+ return ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+ }
+
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index e2c4cbdc829c..dcf19062834c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1650,15 +1650,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
final Intent viewIntent = buildViewDataUsageIntent(res, policy.template);
- // TODO: Resolve to single code path.
- if (UserManager.isHeadlessSystemUserMode()) {
- builder.setContentIntent(PendingIntent.getActivityAsUser(
- mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE,
- /* options= */ null, UserHandle.CURRENT));
- } else {
- builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
- }
+ setContentIntent(builder, viewIntent);
break;
}
case TYPE_LIMIT: {
@@ -1679,15 +1671,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
builder.setSmallIcon(R.drawable.stat_notify_disabled_data);
final Intent intent = buildNetworkOverLimitIntent(res, policy.template);
- // TODO: Resolve to single code path.
- if (UserManager.isHeadlessSystemUserMode()) {
- builder.setContentIntent(PendingIntent.getActivityAsUser(
- mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE,
- /* options= */ null, UserHandle.CURRENT));
- } else {
- builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
- }
+ setContentIntent(builder, intent);
break;
}
case TYPE_LIMIT_SNOOZED: {
@@ -1711,15 +1695,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
builder.setChannelId(SystemNotificationChannels.NETWORK_STATUS);
final Intent intent = buildViewDataUsageIntent(res, policy.template);
- // TODO: Resolve to single code path.
- if (UserManager.isHeadlessSystemUserMode()) {
- builder.setContentIntent(PendingIntent.getActivityAsUser(
- mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE,
- /* options= */ null, UserHandle.CURRENT));
- } else {
- builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
- }
+ setContentIntent(builder, intent);
break;
}
case TYPE_RAPID: {
@@ -1739,15 +1715,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
final Intent viewIntent = buildViewDataUsageIntent(res, policy.template);
- // TODO: Resolve to single code path.
- if (UserManager.isHeadlessSystemUserMode()) {
- builder.setContentIntent(PendingIntent.getActivityAsUser(
- mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE,
- /* options= */ null, UserHandle.CURRENT));
- } else {
- builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
- }
+ setContentIntent(builder, viewIntent);
break;
}
default: {
@@ -1765,6 +1733,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mActiveNotifs.add(notificationId);
}
+ private void setContentIntent(Notification.Builder builder, Intent intent) {
+ if (UserManager.isHeadlessSystemUserMode()) {
+ builder.setContentIntent(PendingIntent.getActivityAsUser(
+ mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE,
+ /* options= */ null, UserHandle.CURRENT));
+ } else {
+ builder.setContentIntent(PendingIntent.getActivity(
+ mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
+ }
+ }
+
private void cancelNotification(NotificationId notificationId) {
mContext.getSystemService(NotificationManager.class).cancel(notificationId.getTag(),
notificationId.getId());
diff --git a/services/core/java/com/android/server/notification/OWNERS b/services/core/java/com/android/server/notification/OWNERS
index 5a19656b36a6..6c4dd6d13d92 100644
--- a/services/core/java/com/android/server/notification/OWNERS
+++ b/services/core/java/com/android/server/notification/OWNERS
@@ -1,4 +1,6 @@
-dsandler@android.com
+# Bug component: 34005
+
juliacr@google.com
-beverlyt@google.com
-pixel@google.com
+yurilin@google.com
+jeffdq@google.com
+dsandler@android.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index cae0234bdc7a..49cab3da1966 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1711,7 +1711,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
: null;
}
- private void clearLastParentBeforePip() {
+ void clearLastParentBeforePip() {
if (mLastParentBeforePip != null) {
mLastParentBeforePip.mChildPipActivity = null;
mLastParentBeforePip = null;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8993840a529e..a7c2733686b7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2569,6 +2569,9 @@ class Task extends TaskFragment {
EventLogTags.writeWmTaskRemoved(mTaskId, reason);
clearPinnedTaskIfNeed();
+ if (mChildPipActivity != null) {
+ mChildPipActivity.clearLastParentBeforePip();
+ }
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 2c0e909a5cc0..ea07fe77e588 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -42,6 +42,7 @@ import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Pair;
import android.view.Display;
+import android.view.IWindow;
import android.view.IWindowManager;
import android.view.ViewDebug;
@@ -553,6 +554,22 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private void dumpLocalWindowAsync(IWindow client, ParcelFileDescriptor pfd) {
+ // Make it asynchronous to avoid writer from being blocked
+ // by waiting for the buffer to be consumed in the same process.
+ IoThread.getExecutor().execute(() -> {
+ synchronized (mInternal.mGlobalLock) {
+ try {
+ client.executeCommand(ViewDebug.REMOTE_COMMAND_DUMP_ENCODED, null, pfd);
+ } catch (Exception e) {
+ // Ignore RemoteException for local call. Just print trace for other
+ // exceptions caused by RC with tolerable low possibility.
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
private int runDumpVisibleWindowViews(PrintWriter pw) {
if (!mInternal.checkCallingPermission(android.Manifest.permission.DUMP,
"runDumpVisibleWindowViews()")) {
@@ -575,16 +592,7 @@ public class WindowManagerShellCommand extends ShellCommand {
pipe = new ByteTransferPipe();
final ParcelFileDescriptor pfd = pipe.getWriteFd();
if (w.isClientLocal()) {
- // Make it asynchronous to avoid writer from being blocked
- // by waiting for the buffer to be consumed in the same process.
- IoThread.getExecutor().execute(() -> {
- try {
- w.mClient.executeCommand(
- ViewDebug.REMOTE_COMMAND_DUMP_ENCODED, null, pfd);
- } catch (RemoteException e) {
- // Ignore for local call.
- }
- });
+ dumpLocalWindowAsync(w.mClient, pfd);
} else {
w.mClient.executeCommand(
ViewDebug.REMOTE_COMMAND_DUMP_ENCODED, null, pfd);
diff --git a/services/tests/PackageManagerServiceTests/TEST_MAPPING b/services/tests/PackageManagerServiceTests/TEST_MAPPING
index af0008c29aaf..63f26b6c8816 100644
--- a/services/tests/PackageManagerServiceTests/TEST_MAPPING
+++ b/services/tests/PackageManagerServiceTests/TEST_MAPPING
@@ -9,6 +9,38 @@
"name": "PackageManagerServiceHostTests"
}
],
+ "kernel-presubmit": [
+ {
+ "name": "PackageManagerServiceHostTests",
+ "options": [
+ {
+ // TODO(b/197552347) (crashes postsubmit)
+ "exclude-filter": "com.android.server.pm.test.OverlayActorVisibilityTest#testVisibilityByOverlayable"
+ },
+ {
+ // TODO(b/204133664)
+ "exclude-filter": "com.android.server.pm.test.SdCardEjectionTests"
+ },
+ {
+ // TODO(b/272575212)
+ "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptDataBinaryXml"
+ },
+ {
+ "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptDataTextXml"
+ },
+ {
+ "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptHeaderBinaryXml"
+ },
+ {
+ "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptHeaderTextXml"
+ },
+ {
+ // TODO(b/272714903)
+ "exclude-filter": "com.android.server.pm.test.OverlayPathsUninstallSystemUpdatesTest#verify"
+ }
+ ]
+ }
+ ],
"imports": [
{
"path": "frameworks/base/services/tests/PackageManagerServiceTests/unit"
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 80de823a6a1b..3f83d4efc31a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -736,8 +736,8 @@ public class AlarmManagerServiceTest {
verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
onFinishedCaptor.capture(), any(Handler.class), isNull(),
optionsCaptor.capture());
- assertTrue(optionsCaptor.getValue()
- .getBoolean(BroadcastOptions.KEY_ALARM_BROADCAST, false));
+ final BroadcastOptions options = new BroadcastOptions(optionsCaptor.getValue());
+ assertTrue(options.isAlarmBroadcast());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/dreams/OWNERS b/services/tests/servicestests/src/com/android/server/dreams/OWNERS
new file mode 100644
index 000000000000..2f19cf557966
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/dreams/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/dreams/OWNERS
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 49ad58550db8..7c60f81259c7 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -82,7 +82,7 @@ import java.util.concurrent.ConcurrentHashMap;
* must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
* longer used and associated resources may be recovered.
* <p>
- * Subclasses of {@code Connection} override the {@code on*} methods to provide the the
+ * Subclasses of {@code Connection} override the {@code on*} methods to provide the
* {@link ConnectionService}'s implementation of calling functionality. The {@code on*} methods are
* called by Telecom to inform an instance of a {@code Connection} of actions specific to that
* {@code Connection} instance.
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 1ba997f4c334..fdf694303dbc 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -743,11 +743,23 @@ public final class TelephonyPermissions {
/**
* Given a list of permissions, check to see if the caller has at least one of them granted. If
- * not, check to see if the caller has carrier privileges. If the caller does not have any of
+ * not, check to see if the caller has carrier privileges. If the caller does not have any of
* these permissions, throw a SecurityException.
*/
public static void enforceAnyPermissionGrantedOrCarrierPrivileges(Context context, int subId,
int uid, String message, String... permissions) {
+ enforceAnyPermissionGrantedOrCarrierPrivileges(
+ context, subId, uid, false, message, permissions);
+ }
+
+ /**
+ * Given a list of permissions, check to see if the caller has at least one of them granted. If
+ * not, check to see if the caller has carrier privileges on the specified subscription (or any
+ * subscription if {@code allowCarrierPrivilegeOnAnySub} is {@code true}. If the caller does not
+ * have any of these permissions, throw a {@link SecurityException}.
+ */
+ public static void enforceAnyPermissionGrantedOrCarrierPrivileges(Context context, int subId,
+ int uid, boolean allowCarrierPrivilegeOnAnySub, String message, String... permissions) {
if (permissions.length == 0) return;
boolean isGranted = false;
for (String perm : permissions) {
@@ -758,7 +770,12 @@ public final class TelephonyPermissions {
}
if (isGranted) return;
- if (checkCarrierPrivilegeForSubId(context, subId)) return;
+
+ if (allowCarrierPrivilegeOnAnySub) {
+ if (checkCarrierPrivilegeForAnySubId(context, Binder.getCallingUid())) return;
+ } else {
+ if (checkCarrierPrivilegeForSubId(context, subId)) return;
+ }
StringBuilder b = new StringBuilder(message);
b.append(": Neither user ");
@@ -769,7 +786,8 @@ public final class TelephonyPermissions {
b.append(" or ");
b.append(permissions[i]);
}
- b.append(" or carrier privileges");
+ b.append(" or carrier privileges. subId=" + subId + ", allowCarrierPrivilegeOnAnySub="
+ + allowCarrierPrivilegeOnAnySub);
throw new SecurityException(b.toString());
}
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index dc695d6a6a4b..e19117bc805f 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -730,6 +730,25 @@ public abstract class EuiccService extends Service {
}
/**
+ * Result code to string
+ *
+ * @param result The result code.
+ * @return The result code in string format.
+ *
+ * @hide
+ */
+ public static String resultToString(@Result int result) {
+ switch (result) {
+ case RESULT_OK: return "OK";
+ case RESULT_MUST_DEACTIVATE_SIM : return "MUST_DEACTIVATE_SIM";
+ case RESULT_RESOLVABLE_ERRORS: return "RESOLVABLE_ERRORS";
+ case RESULT_FIRST_USER: return "FIRST_USER";
+ default:
+ return "UNKNOWN(" + result + ")";
+ }
+ }
+
+ /**
* Wrapper around IEuiccService that forwards calls to implementations of {@link EuiccService}.
*/
private class IEuiccServiceWrapper extends IEuiccService.Stub {
diff --git a/telephony/java/android/service/euicc/GetEuiccProfileInfoListResult.java b/telephony/java/android/service/euicc/GetEuiccProfileInfoListResult.java
index 9add38e40d9b..46a049c12661 100644
--- a/telephony/java/android/service/euicc/GetEuiccProfileInfoListResult.java
+++ b/telephony/java/android/service/euicc/GetEuiccProfileInfoListResult.java
@@ -123,4 +123,16 @@ public final class GetEuiccProfileInfoListResult implements Parcelable {
public int describeContents() {
return 0;
}
+
+ /**
+ * @hide
+ *
+ * @return String representation of {@link GetEuiccProfileInfoListResult}
+ */
+ @Override
+ public String toString() {
+ return "[GetEuiccProfileInfoListResult: result=" + EuiccService.resultToString(result)
+ + ", isRemovable=" + mIsRemovable + ", mProfiles=" + Arrays.toString(mProfiles)
+ + "]";
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7cccc5dd06c0..656a70a09048 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.Manifest;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -53,6 +54,7 @@ import com.android.telephony.Rlog;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -9375,7 +9377,8 @@ public class CarrierConfigManager {
* Gets the configuration values of the specified keys for a particular subscription.
*
* <p>If an invalid subId is used, the returned configuration will contain default values for
- * the specified keys.
+ * the specified keys. If the value for the key can't be found, the returned configuration will
+ * filter the key out.
*
* <p>After using this method to get the configuration bundle,
* {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be called to confirm whether
@@ -9393,8 +9396,8 @@ public class CarrierConfigManager {
* @param subId The subscription ID on which the carrier config should be retrieved.
* @param keys The carrier config keys to retrieve values.
* @return A {@link PersistableBundle} with key/value mapping for the specified configuration
- * on success, or an empty (but never null) bundle on failure (for example, when no value for
- * the specified key can be found).
+ * on success, or an empty (but never null) bundle on failure (for example, when the calling app
+ * has no permission).
*/
@RequiresPermission(anyOf = {
Manifest.permission.READ_PHONE_STATE,
@@ -9512,6 +9515,8 @@ public class CarrierConfigManager {
* Gets the configuration values of the specified config keys applied for the default
* subscription.
*
+ * <p>If the value for the key can't be found, the returned bundle will filter the key out.
+ *
* <p>After using this method to get the configuration bundle, {@link
* #isConfigForIdentifiedCarrier(PersistableBundle)} should be called to confirm whether any
* carrier specific configuration has been applied.
@@ -9726,4 +9731,85 @@ public class CarrierConfigManager {
configs.putPersistableBundle(key, (PersistableBundle) value);
}
}
+
+ /**
+ * Listener interface to get a notification when the carrier configurations have changed.
+ *
+ * Use this listener to receive timely updates when the carrier configuration changes. System
+ * components should prefer this listener over {@link #ACTION_CARRIER_CONFIG_CHANGED}
+ * whenever possible.
+ *
+ * To register the listener, call
+ * {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}.
+ * To unregister, call
+ * {@link #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener)}.
+ *
+ * Note that on registration, registrants will NOT receive a notification on last carrier config
+ * change. Only carrier configs change AFTER the registration will be sent to registrants. And
+ * unlike {@link #ACTION_CARRIER_CONFIG_CHANGED}, notification wouldn't send when the device is
+ * unlocked. Registrants only receive the notification when there has been real carrier config
+ * changes.
+ *
+ * @see #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)
+ * @see #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener)
+ * @see #ACTION_CARRIER_CONFIG_CHANGED
+ * @see #getConfig(String...)
+ * @see #getConfigForSubId(int, String...)
+ */
+ public interface CarrierConfigChangeListener {
+ /**
+ * Called when carrier configurations have changed.
+ *
+ * @param logicalSlotIndex The logical SIM slot index on which to monitor and get
+ * notification. It is guaranteed to be valid.
+ * @param subscriptionId The subscription on the SIM slot. May be
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ * @param carrierId The optional carrier Id, may be
+ * {@link TelephonyManager#UNKNOWN_CARRIER_ID}.
+ * See {@link TelephonyManager#getSimCarrierId()}.
+ * @param specificCarrierId The optional fine-grained carrierId, may be {@link
+ * TelephonyManager#UNKNOWN_CARRIER_ID}. A specific carrierId may
+ * be different from the carrierId above in a MVNO scenario. See
+ * detail in {@link TelephonyManager#getSimSpecificCarrierId()}.
+ */
+ void onCarrierConfigChanged(int logicalSlotIndex, int subscriptionId, int carrierId,
+ int specificCarrierId);
+ }
+
+ /**
+ * Register a {@link CarrierConfigChangeListener} to get a notification when carrier
+ * configurations have changed.
+ *
+ * @param executor The executor on which the listener will be called.
+ * @param listener The CarrierConfigChangeListener called when carrier configs has changed.
+ */
+ public void registerCarrierConfigChangeListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull CarrierConfigChangeListener listener) {
+ Objects.requireNonNull(executor, "Executor should be non-null.");
+ Objects.requireNonNull(listener, "Listener should be non-null.");
+
+ TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (trm == null) {
+ throw new IllegalStateException("Telephony registry service is null");
+ }
+ trm.addCarrierConfigChangedListener(executor, listener);
+ }
+
+ /**
+ * Unregister the {@link CarrierConfigChangeListener} to stop notification on carrier
+ * configurations change.
+ *
+ * @param listener The CarrierConfigChangeListener which was registered with method
+ * {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}.
+ */
+ public void unregisterCarrierConfigChangeListener(
+ @NonNull CarrierConfigChangeListener listener) {
+ Objects.requireNonNull(listener, "Listener should be non-null.");
+
+ TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (trm == null) {
+ throw new IllegalStateException("Telephony registry service is null");
+ }
+ trm.removeCarrierConfigChangedListener(listener);
+ }
}
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c4d760f8db52..dd7e2d71e8f3 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -997,7 +997,8 @@ public final class DataFailCause {
*/
public static final int IWLAN_CONGESTION = 0x3C8C;
- /** IKE configuration error resulting in failure */
+ // Below IWLAN error codes are defined by the UE and do not relate to any 3GPP spec value
+ /** IKE configuration error resulting in failure */
public static final int IWLAN_IKEV2_CONFIG_FAILURE = 0x4000;
/**
* Sent in the response to an IKE_AUTH message when, for some reason,
@@ -1014,6 +1015,57 @@ public final class DataFailCause {
public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 0x4005;
/** Expected to update or bring down an ePDG tunnel, but no tunnel found*/
public static final int IWLAN_TUNNEL_NOT_FOUND = 0x4006;
+ /**
+ * Failed to apply tunnel transform
+ *
+ * @hide
+ */
+ public static final int IWLAN_TUNNEL_TRANSFORM_FAILED = 0x4007;
+ /**
+ * IWLAN PDN setup failed due to Wi-Fi lost during IKE tunnel setup,
+ * match exception reported by IKE module
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_NETWORK_LOST_EXCEPTION = 0x4008;
+ /**
+ * Carrier-specific error codes during IKEv2 SA setup
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_PRIVATE_PROTOCOL_ERROR = 0x4009;
+ /**
+ * IKE Session closed before child session opened
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED = 0x400A;
+ /**
+ * IKE Init timeout, no response from EPDG
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_INIT_TIMEOUT = 0x400B;
+ /**
+ * DPD message does not get an ack after the re-tx attempts and duration, i.e., times out.
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_DPD_TIMEOUT = 0x400C;
+ /**
+ * The Wi-Fi to Wi-Fi handover of the IMS PDN fails because the network does not respond to the
+ * MOBIKE/rekey mobility message in the expected manner
+ *
+ * @hide
+ */
+ public static final int IWLAN_IKE_MOBILITY_TIMEOUT = 0x400D;
+ /**
+ * IKE client sent "IKE AUTH request 3" to the network but got "Internal address failure" from
+ * the network since no internal addresses can be assigned.
+ *
+ * @hide
+ */
+ public static final int IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE = 0x400E;
// OEM sepecific error codes. To be used by OEMs when they don't
// want to reveal error code which would be replaced by ERROR_UNSPECIFIED
@@ -1508,6 +1560,16 @@ public final class DataFailCause {
sFailCauseMap.put(IWLAN_DNS_RESOLUTION_NAME_FAILURE, "IWLAN_DNS_RESOLUTION_NAME_FAILURE");
sFailCauseMap.put(IWLAN_DNS_RESOLUTION_TIMEOUT, "IWLAN_DNS_RESOLUTION_TIMEOUT");
sFailCauseMap.put(IWLAN_TUNNEL_NOT_FOUND, "IWLAN_TUNNEL_NOT_FOUND");
+ sFailCauseMap.put(IWLAN_TUNNEL_TRANSFORM_FAILED, "IWLAN_TUNNEL_TRANSFORM_FAILED");
+ sFailCauseMap.put(IWLAN_IKE_INIT_TIMEOUT, "IWLAN_IKE_INIT_TIMEOUT");
+ sFailCauseMap.put(IWLAN_IKE_NETWORK_LOST_EXCEPTION, "IWLAN_IKE_NETWORK_LOST_EXCEPTION");
+ sFailCauseMap.put(IWLAN_IKE_PRIVATE_PROTOCOL_ERROR, "IWLAN_IKE_PRIVATE_PROTOCOL_ERROR");
+ sFailCauseMap.put(IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED,
+ "IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED");
+ sFailCauseMap.put(IWLAN_IKE_DPD_TIMEOUT, "IWLAN_IKE_DPD_TIMEOUT");
+ sFailCauseMap.put(IWLAN_IKE_MOBILITY_TIMEOUT, "IWLAN_IKE_MOBILITY_TIMEOUT");
+ sFailCauseMap.put(IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE,
+ "IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE");
sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 5c1a00604020..59b822ebecbf 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -58,6 +58,7 @@ import android.os.UserHandle;
import android.provider.Telephony.SimInfo;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
+import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
@@ -347,7 +348,7 @@ public class SubscriptionManager {
/**
* A content {@link Uri} used to receive updates on advanced calling user setting
- * @see ImsMmTelManager#isAdvancedCallingSettingEnabled().
+ *
* <p>
* Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
* subscription advanced calling enabled
@@ -358,6 +359,9 @@ public class SubscriptionManager {
* delivery of updates to the {@link Uri}.
* To be notified of changes to a specific subId, append subId to the URI
* {@link Uri#withAppendedPath(Uri, String)}.
+ *
+ * @see ImsMmTelManager#isAdvancedCallingSettingEnabled()
+ *
* @hide
*/
@NonNull
@@ -731,6 +735,15 @@ public class SubscriptionManager {
/** Indicates that data roaming is disabled for a subscription */
public static final int DATA_ROAMING_DISABLE = SimInfo.DATA_ROAMING_DISABLE;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"DATA_ROAMING_"},
+ value = {
+ DATA_ROAMING_ENABLE,
+ DATA_ROAMING_DISABLE
+ })
+ public @interface DataRoamingMode {}
+
/**
* TelephonyProvider column name for subscription carrier id.
* @see TelephonyManager#getSimCarrierId()
@@ -1133,7 +1146,7 @@ public class SubscriptionManager {
*
* An opportunistic subscription will default to data-centric.
*
- * {@see SubscriptionInfo#isOpportunistic}
+ * @see SubscriptionInfo#isOpportunistic
*/
public static final int USAGE_SETTING_DEFAULT = 0;
@@ -1743,8 +1756,7 @@ public class SubscriptionManager {
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
- * to the calling app are returned.
+ * {@link TelephonyManager#hasCarrierPrivileges}).
*
* @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
* <ul>
@@ -1762,7 +1774,6 @@ public class SubscriptionManager {
* </li>
* </ul>
*/
- @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
return getActiveSubscriptionInfoList(/* userVisibleonly */true);
@@ -1919,7 +1930,7 @@ public class SubscriptionManager {
*
* <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
*
- * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+ * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID.
*
* @hide
*/
@@ -1949,7 +1960,7 @@ public class SubscriptionManager {
*
* @param cardId the card ID of the eUICC.
*
- * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+ * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID.
*
* @hide
*/
@@ -1966,17 +1977,12 @@ public class SubscriptionManager {
}
/**
+ * Get the active subscription count.
*
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
- * or that the calling app has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, the count will include
- * only those subscriptions accessible to the caller.
+ * @return The current number of active subscriptions.
*
- * @return the current number of active subscriptions. There is no guarantee the value
- * returned by this method will be the same as the length of the list returned by
- * {@link #getActiveSubscriptionInfoList}.
+ * @see #getActiveSubscriptionInfoList()
*/
- @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public int getActiveSubscriptionInfoCount() {
int result = 0;
@@ -2078,10 +2084,15 @@ public class SubscriptionManager {
}
/**
- * Remove SubscriptionInfo record from the SubscriptionInfo database
+ * Remove subscription info record from the subscription database.
+ *
* @param uniqueId This is the unique identifier for the subscription within the specific
- * subscription type.
- * @param subscriptionType the {@link #SUBSCRIPTION_TYPE}
+ * subscription type.
+ * @param subscriptionType the type of subscription to be removed.
+ *
+ * @throws NullPointerException if {@code uniqueId} is {@code null}.
+ * @throws SecurityException if callers do not hold the required permission.
+ *
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -2116,7 +2127,7 @@ public class SubscriptionManager {
/**
* Set SIM icon tint color for subscription ID
* @param tint the RGB value of icon tint color of the SIM
- * @param subId the unique Subscritpion ID in database
+ * @param subId the unique subscription ID in database
* @return the number of records updated
* @hide
*/
@@ -2124,7 +2135,7 @@ public class SubscriptionManager {
public int setIconTint(@ColorInt int tint, int subId) {
if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
return setSubscriptionPropertyHelper(subId, "setIconTint",
- (iSub)-> iSub.setIconTint(tint, subId)
+ (iSub)-> iSub.setIconTint(subId, tint)
);
}
@@ -2432,20 +2443,6 @@ public class SubscriptionManager {
return getActiveSubscriptionInfo(getDefaultDataSubscriptionId());
}
- /** @hide */
- public void clearSubscriptionInfo() {
- try {
- ISub iSub = TelephonyManager.getSubscriptionService();
- if (iSub != null) {
- iSub.clearSubInfo();
- }
- } catch (RemoteException ex) {
- // ignore it
- }
-
- return;
- }
-
/**
* Check if the supplied subscription ID is valid.
*
@@ -2589,48 +2586,27 @@ public class SubscriptionManager {
}
/**
- * Returns a constant indicating the state of sim for the slot index.
+ * Set a field in the subscription database. Note not all fields are supported.
*
- * @param slotIndex
+ * @param subscriptionId Subscription Id of Subscription.
+ * @param columnName Column name in the database. Note not all fields are supported.
+ * @param value Value to store in the database.
*
- * {@See TelephonyManager#SIM_STATE_UNKNOWN}
- * {@See TelephonyManager#SIM_STATE_ABSENT}
- * {@See TelephonyManager#SIM_STATE_PIN_REQUIRED}
- * {@See TelephonyManager#SIM_STATE_PUK_REQUIRED}
- * {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED}
- * {@See TelephonyManager#SIM_STATE_READY}
- * {@See TelephonyManager#SIM_STATE_NOT_READY}
- * {@See TelephonyManager#SIM_STATE_PERM_DISABLED}
- * {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR}
+ * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+ * exposed.
+ * @throws SecurityException if callers do not hold the required permission.
+ *
+ * @see android.provider.Telephony.SimInfo for all the columns.
*
- * {@hide}
- */
- public static int getSimStateForSlotIndex(int slotIndex) {
- int simState = TelephonyManager.SIM_STATE_UNKNOWN;
-
- try {
- ISub iSub = TelephonyManager.getSubscriptionService();
- if (iSub != null) {
- simState = iSub.getSimStateForSlotIndex(slotIndex);
- }
- } catch (RemoteException ex) {
- }
-
- return simState;
- }
-
- /**
- * Store properties associated with SubscriptionInfo in database
- * @param subId Subscription Id of Subscription
- * @param propKey Column name in database associated with SubscriptionInfo
- * @param propValue Value to store in DB for particular subId & column name
* @hide
*/
- public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public static void setSubscriptionProperty(int subscriptionId, @NonNull String columnName,
+ @NonNull String value) {
try {
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
- iSub.setSubscriptionProperty(subId, propKey, propValue);
+ iSub.setSubscriptionProperty(subscriptionId, columnName, value);
}
} catch (RemoteException ex) {
// ignore it
@@ -2659,118 +2635,149 @@ public class SubscriptionManager {
}
/**
- * Return list of contacts uri corresponding to query result.
- * @param subId Subscription Id of Subscription
- * @param propKey Column name in SubscriptionInfo database
- * @return list of contacts uri to be returned
- * @hide
- */
- private static List<Uri> getContactsFromSubscriptionProperty(int subId, String propKey,
- Context context) {
- String result = getSubscriptionProperty(subId, propKey, context);
- if (result != null) {
- try {
- byte[] b = Base64.decode(result, Base64.DEFAULT);
- ByteArrayInputStream bis = new ByteArrayInputStream(b);
- ObjectInputStream ois = new ObjectInputStream(bis);
- List<String> contacts = ArrayList.class.cast(ois.readObject());
- List<Uri> uris = new ArrayList<>();
- for (String contact : contacts) {
- uris.add(Uri.parse(contact));
- }
- return uris;
- } catch (IOException e) {
- logd("getContactsFromSubscriptionProperty IO exception");
- } catch (ClassNotFoundException e) {
- logd("getContactsFromSubscriptionProperty ClassNotFound exception");
- }
- }
- return new ArrayList<>();
- }
-
- /**
- * Store properties associated with SubscriptionInfo in database
- * @param subId Subscription Id of Subscription
- * @param propKey Column name in SubscriptionInfo database
- * @return Value associated with subId and propKey column in database
+ * Get specific field in string format from the subscription info database.
+ *
+ * @param context The calling context.
+ * @param subscriptionId Subscription id of the subscription.
+ * @param columnName Column name in subscription database.
+ *
+ * @return Value in string format associated with {@code subscriptionId} and {@code columnName}
+ * from the database.
+ *
+ * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+ * exposed.
+ *
+ * @see android.provider.Telephony.SimInfo for all the columns.
+ *
* @hide
*/
- private static String getSubscriptionProperty(int subId, String propKey,
- Context context) {
+ @NonNull
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ private static String getStringSubscriptionProperty(@NonNull Context context,
+ int subscriptionId, @NonNull String columnName) {
String resultValue = null;
try {
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
- resultValue = iSub.getSubscriptionProperty(subId, propKey,
+ resultValue = iSub.getSubscriptionProperty(subscriptionId, columnName,
context.getOpPackageName(), context.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
}
- return resultValue;
+ return TextUtils.emptyIfNull(resultValue);
}
/**
- * Returns boolean value corresponding to query result.
- * @param subId Subscription Id of Subscription
- * @param propKey Column name in SubscriptionInfo database
- * @param defValue Default boolean value to be returned
- * @return boolean result value to be returned
+ * Get specific field in {@code boolean} format from the subscription info database.
+ *
+ * @param subscriptionId Subscription id of the subscription.
+ * @param columnName Column name in subscription database.
+ * @param defaultValue Default value in case not found or error.
+ * @param context The calling context.
+ *
+ * @return Value in {@code boolean} format associated with {@code subscriptionId} and
+ * {@code columnName} from the database, or {@code defaultValue} if not found or error.
+ *
+ * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+ * exposed.
+ *
+ * @see android.provider.Telephony.SimInfo for all the columns.
+ *
* @hide
*/
- public static boolean getBooleanSubscriptionProperty(int subId, String propKey,
- boolean defValue, Context context) {
- String result = getSubscriptionProperty(subId, propKey, context);
- if (result != null) {
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public static boolean getBooleanSubscriptionProperty(int subscriptionId,
+ @NonNull String columnName, boolean defaultValue, @NonNull Context context) {
+ String result = getStringSubscriptionProperty(context, subscriptionId, columnName);
+ if (!result.isEmpty()) {
try {
return Integer.parseInt(result) == 1;
} catch (NumberFormatException err) {
logd("getBooleanSubscriptionProperty NumberFormat exception");
}
}
- return defValue;
+ return defaultValue;
}
/**
- * Returns integer value corresponding to query result.
- * @param subId Subscription Id of Subscription
- * @param propKey Column name in SubscriptionInfo database
- * @param defValue Default integer value to be returned
- * @return integer result value to be returned
+ * Get specific field in {@code integer} format from the subscription info database.
+ *
+ * @param subscriptionId Subscription id of the subscription.
+ * @param columnName Column name in subscription database.
+ * @param defaultValue Default value in case not found or error.
+ * @param context The calling context.
+ *
+ * @return Value in {@code integer} format associated with {@code subscriptionId} and
+ * {@code columnName} from the database, or {@code defaultValue} if not found or error.
+ *
+ * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+ * exposed.
+ *
+ * @see android.provider.Telephony.SimInfo for all the columns.
+ *
* @hide
*/
- public static int getIntegerSubscriptionProperty(int subId, String propKey, int defValue,
- Context context) {
- String result = getSubscriptionProperty(subId, propKey, context);
- if (result != null) {
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public static int getIntegerSubscriptionProperty(int subscriptionId, @NonNull String columnName,
+ int defaultValue, @NonNull Context context) {
+ String result = getStringSubscriptionProperty(context, subscriptionId, columnName);
+ if (!result.isEmpty()) {
try {
return Integer.parseInt(result);
} catch (NumberFormatException err) {
logd("getIntegerSubscriptionProperty NumberFormat exception");
}
}
- return defValue;
+ return defaultValue;
}
/**
- * Returns long value corresponding to query result.
- * @param subId Subscription Id of Subscription
- * @param propKey Column name in SubscriptionInfo database
- * @param defValue Default long value to be returned
- * @return long result value to be returned
+ * Get specific field in {@code long} format from the subscription info database.
+ *
+ * @param subscriptionId Subscription id of the subscription.
+ * @param columnName Column name in subscription database.
+ * @param defaultValue Default value in case not found or error.
+ * @param context The calling context.
+ *
+ * @return Value in {@code long} format associated with {@code subscriptionId} and
+ * {@code columnName} from the database, or {@code defaultValue} if not found or error.
+ *
+ * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+ * exposed.
+ *
+ * @see android.provider.Telephony.SimInfo for all the columns.
+ *
* @hide
*/
- public static long getLongSubscriptionProperty(int subId, String propKey, long defValue,
- Context context) {
- String result = getSubscriptionProperty(subId, propKey, context);
- if (result != null) {
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges",
+ })
+ public static long getLongSubscriptionProperty(int subscriptionId, @NonNull String columnName,
+ long defaultValue, @NonNull Context context) {
+ String result = getStringSubscriptionProperty(context, subscriptionId, columnName);
+ if (!result.isEmpty()) {
try {
return Long.parseLong(result);
} catch (NumberFormatException err) {
logd("getLongSubscriptionProperty NumberFormat exception");
}
}
- return defValue;
+ return defaultValue;
}
/**
@@ -3008,7 +3015,6 @@ public class SubscriptionManager {
* considered unmetered.
* @param networkTypes the network types this override applies to. If no
* network types are specified, override values will be ignored.
- * {@see TelephonyManager#getAllNetworkTypes()}
* @param expirationDurationMillis the duration after which the requested override
* will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
@@ -3069,17 +3075,14 @@ public class SubscriptionManager {
* </ul>
*
* @param subId the subscriber this override applies to.
- * @param overrideCongested set if the subscription should be considered
- * congested.
- * @param networkTypes the network types this override applies to. If no
- * network types are specified, override values will be ignored.
- * {@see TelephonyManager#getAllNetworkTypes()}
+ * @param overrideCongested set if the subscription should be considered congested.
+ * @param networkTypes the network types this override applies to. If no network types are
+ * specified, override values will be ignored.
* @param expirationDurationMillis the duration after which the requested override
- * will be automatically cleared, or {@code 0} to leave in the
- * requested state until explicitly cleared, or the next reboot,
- * whichever happens first.
- * @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
+ * will be automatically cleared, or {@code 0} to leave in the requested state until explicitly
+ * cleared, or the next reboot, whichever happens first.
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements outlined above.
*/
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
@NonNull @Annotation.NetworkType int[] networkTypes,
@@ -3095,10 +3098,11 @@ public class SubscriptionManager {
*
* Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns
* true). To check for permissions for non-embedded subscription as well,
- * {@see android.telephony.TelephonyManager#hasCarrierPrivileges}.
*
* @param info The subscription to check.
* @return whether the app is authorized to manage this subscription per its metadata.
+ *
+ * @see android.telephony.TelephonyManager#hasCarrierPrivileges
*/
public boolean canManageSubscription(SubscriptionInfo info) {
return canManageSubscription(info, mContext.getPackageName());
@@ -3111,11 +3115,13 @@ public class SubscriptionManager {
*
* Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns
* true). To check for permissions for non-embedded subscription as well,
- * {@see android.telephony.TelephonyManager#hasCarrierPrivileges}.
*
* @param info The subscription to check.
* @param packageName Package name of the app to check.
+ *
* @return whether the app is authorized to manage this subscription per its access rules.
+ *
+ * @see android.telephony.TelephonyManager#hasCarrierPrivileges
* @hide
*/
@SystemApi
@@ -3428,21 +3434,20 @@ public class SubscriptionManager {
/**
* Remove a list of subscriptions from their subscription group.
- * See {@link #createSubscriptionGroup(List)} for more details.
*
* Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
- * permission or had carrier privilege permission on the subscriptions:
- * {@link TelephonyManager#hasCarrierPrivileges()} or
- * {@link #canManageSubscription(SubscriptionInfo)}
- *
- * @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
- * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong
- * the specified group.
- * @throws IllegalStateException if Telephony service is in bad state.
+ * permission or has carrier privilege permission on all of the subscriptions provided in
+ * {@code subIdList}.
*
* @param subIdList list of subId that need removing from their groups.
+ * @param groupUuid The UUID of the subscription group.
+ *
+ * @throws SecurityException if the caller doesn't meet the requirements outlined above.
+ * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong the
+ * specified group.
+ * @throws IllegalStateException if Telephony service is in bad state.
*
+ * @see #createSubscriptionGroup(List)
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -3450,7 +3455,7 @@ public class SubscriptionManager {
@NonNull ParcelUuid groupUuid) {
Preconditions.checkNotNull(subIdList, "subIdList can't be null.");
Preconditions.checkNotNull(groupUuid, "groupUuid can't be null.");
- String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String callingPackage = mContext != null ? mContext.getOpPackageName() : "<unknown>";
if (VDBG) {
logd("[removeSubscriptionsFromGroup]");
}
@@ -3460,7 +3465,7 @@ public class SubscriptionManager {
try {
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
- iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, pkgForDebug);
+ iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, callingPackage);
} else {
if (!isSystemProcess()) {
throw new IllegalStateException("telephony service is null.");
@@ -3498,7 +3503,6 @@ public class SubscriptionManager {
* @param groupUuid of which list of subInfo will be returned.
* @return list of subscriptionInfo that belong to the same group, including the given
* subscription itself. It will return an empty list if no subscription belongs to the group.
- *
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@@ -3538,7 +3542,8 @@ public class SubscriptionManager {
* want to see their own hidden subscriptions.
*
* @param info the subscriptionInfo to check against.
- * @return true if this subscription should be visible to the API caller.
+ *
+ * @return {@code true} if this subscription should be visible to the API caller.
*
* @hide
*/
@@ -3611,9 +3616,9 @@ public class SubscriptionManager {
* <p>
* Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
*
+ * @param subscriptionId Subscription to be enabled or disabled. It could be a eSIM or pSIM
+ * subscription.
* @param enable whether user is turning it on or off.
- * @param subscriptionId Subscription to be enabled or disabled.
- * It could be a eSIM or pSIM subscription.
*
* @return whether the operation is successful.
*
@@ -3646,8 +3651,6 @@ public class SubscriptionManager {
* available from SubscriptionInfo.areUiccApplicationsEnabled() will be updated
* immediately.)
*
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
- *
* @param subscriptionId which subscription to operate on.
* @param enabled whether uicc applications are enabled or disabled.
* @hide
@@ -3680,8 +3683,6 @@ public class SubscriptionManager {
* It provides whether a physical SIM card can be disabled without taking it out, which is done
* via {@link #setSubscriptionEnabled(int, boolean)} API.
*
- * Requires Permission: READ_PRIVILEGED_PHONE_STATE.
- *
* @return whether can disable subscriptions on physical SIMs.
*
* @hide
@@ -3709,10 +3710,11 @@ public class SubscriptionManager {
}
/**
- * DO NOT USE.
- * This API is designed for features that are not finished at this point. Do not call this API.
+ * Check if the subscription is currently active in any slot.
+ *
+ * @param subscriptionId The subscription id.
+ *
* @hide
- * TODO b/135547512: further clean up
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3730,11 +3732,14 @@ public class SubscriptionManager {
}
/**
- * Set the device to device status sharing user preference for a subscription ID. The setting
+ * Set the device to device status sharing user preference for a subscription id. The setting
* app uses this method to indicate with whom they wish to share device to device status
* information.
- * @param sharing the status sharing preference
- * @param subscriptionId the unique Subscription ID in database
+ *
+ * @param subscriptionId The subscription id.
+ * @param sharing The status sharing preference.
+ *
+ * @throws SecurityException if the caller doesn't have permissions required.
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setDeviceToDeviceStatusSharingPreference(int subscriptionId,
@@ -3751,6 +3756,8 @@ public class SubscriptionManager {
* Returns the user-chosen device to device status sharing preference
* @param subscriptionId Subscription id of subscription
* @return The device to device status sharing preference
+ *
+ * @throws SecurityException if the caller doesn't have permissions required.
*/
public @DeviceToDeviceStatusSharingPreference int getDeviceToDeviceStatusSharingPreference(
int subscriptionId) {
@@ -3762,11 +3769,14 @@ public class SubscriptionManager {
}
/**
- * Set the list of contacts that allow device to device status sharing for a subscription ID.
+ * Set the list of contacts that allow device to device status sharing for a subscription id.
* The setting app uses this method to indicate with whom they wish to share device to device
* status information.
- * @param contacts The list of contacts that allow device to device status sharing
- * @param subscriptionId The unique Subscription ID in database
+ *
+ * @param subscriptionId The subscription id.
+ * @param contacts The list of contacts that allow device to device status sharing.
+ *
+ * @throws SecurityException if the caller doesn't have permissions required.
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setDeviceToDeviceStatusSharingContacts(int subscriptionId,
@@ -3782,17 +3792,33 @@ public class SubscriptionManager {
}
/**
- * Returns the list of contacts that allow device to device status sharing.
- * @param subscriptionId Subscription id of subscription
- * @return The list of contacts that allow device to device status sharing
+ * Get the list of contacts that allow device to device status sharing.
+ *
+ * @param subscriptionId Subscription id.
+ *
+ * @return The list of contacts that allow device to device status sharing.
*/
- public @NonNull List<Uri> getDeviceToDeviceStatusSharingContacts(
- int subscriptionId) {
- if (VDBG) {
- logd("[getDeviceToDeviceStatusSharingContacts] + subId: " + subscriptionId);
+ public @NonNull List<Uri> getDeviceToDeviceStatusSharingContacts(int subscriptionId) {
+ String result = getStringSubscriptionProperty(mContext, subscriptionId,
+ D2D_STATUS_SHARING_SELECTED_CONTACTS);
+ if (result != null) {
+ try {
+ byte[] b = Base64.decode(result, Base64.DEFAULT);
+ ByteArrayInputStream bis = new ByteArrayInputStream(b);
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ List<String> contacts = ArrayList.class.cast(ois.readObject());
+ List<Uri> uris = new ArrayList<>();
+ for (String contact : contacts) {
+ uris.add(Uri.parse(contact));
+ }
+ return uris;
+ } catch (IOException e) {
+ logd("getDeviceToDeviceStatusSharingContacts IO exception");
+ } catch (ClassNotFoundException e) {
+ logd("getDeviceToDeviceStatusSharingContacts ClassNotFound exception");
+ }
}
- return getContactsFromSubscriptionProperty(subscriptionId,
- D2D_STATUS_SHARING_SELECTED_CONTACTS, mContext);
+ return new ArrayList<>();
}
/**
@@ -3847,12 +3873,12 @@ public class SubscriptionManager {
/**
* Get active data subscription id. Active data subscription refers to the subscription
* currently chosen to provide cellular internet connection to the user. This may be
- * different from getDefaultDataSubscriptionId(). Eg. Opportunistics data
+ * different from {@link #getDefaultDataSubscriptionId()}.
*
- * See {@link PhoneStateListener#onActiveDataSubscriptionIdChanged(int)} for the details.
+ * @return Active data subscription id if any is chosen, or {@link #INVALID_SUBSCRIPTION_ID} if
+ * not.
*
- * @return Active data subscription id if any is chosen, or
- * SubscriptionManager.INVALID_SUBSCRIPTION_ID if not.
+ * @see TelephonyCallback.ActiveDataSubscriptionIdListener
*/
public static int getActiveDataSubscriptionId() {
if (isSubscriptionManagerServiceEnabled()) {
@@ -4045,12 +4071,15 @@ public class SubscriptionManager {
* security-related or other sensitive scenarios.
*
* @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
- * for the default one.
+ * for the default one.
* @param source the source of the phone number, one of the PHONE_NUMBER_SOURCE_* constants.
+ *
* @return the phone number, or an empty string if not available.
+ *
* @throws IllegalArgumentException if {@code source} is invalid.
* @throws IllegalStateException if the telephony process is not currently available.
* @throws SecurityException if the caller doesn't have permissions required.
+ *
* @see #PHONE_NUMBER_SOURCE_UICC
* @see #PHONE_NUMBER_SOURCE_CARRIER
* @see #PHONE_NUMBER_SOURCE_IMS
@@ -4103,8 +4132,10 @@ public class SubscriptionManager {
* @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
* for the default one.
* @return the phone number, or an empty string if not available.
+ *
* @throws IllegalStateException if the telephony process is not currently available.
* @throws SecurityException if the caller doesn't have permissions required.
+ *
* @see #getPhoneNumber(int, int)
*/
@RequiresPermission(anyOf = {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7a19d36ba743..9d418e144f4c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3504,7 +3504,7 @@ public class TelephonyManager {
"state as absent");
return SIM_STATE_ABSENT;
}
- return SubscriptionManager.getSimStateForSlotIndex(slotIndex);
+ return getSimStateForSlotIndex(slotIndex);
}
/**
@@ -3651,9 +3651,7 @@ public class TelephonyManager {
@Deprecated
public @SimState int getSimApplicationState(int physicalSlotIndex) {
int activePort = getFirstActivePortIndex(physicalSlotIndex);
- int simState =
- SubscriptionManager.getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex,
- activePort));
+ int simState = getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex, activePort));
return getSimApplicationStateFromSimState(simState);
}
@@ -3679,9 +3677,7 @@ public class TelephonyManager {
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimApplicationState(int physicalSlotIndex, int portIndex) {
- int simState =
- SubscriptionManager.getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex,
- portIndex));
+ int simState = getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex, portIndex));
return getSimApplicationStateFromSimState(simState);
}
@@ -3750,7 +3746,7 @@ public class TelephonyManager {
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimState(int slotIndex) {
- int simState = SubscriptionManager.getSimStateForSlotIndex(slotIndex);
+ int simState = getSimStateForSlotIndex(slotIndex);
if (simState == SIM_STATE_LOADED) {
simState = SIM_STATE_READY;
}
@@ -17006,4 +17002,30 @@ public class TelephonyManager {
}
return false;
}
+
+ /**
+ * Returns a constant indicating the state of sim for the slot index.
+ *
+ * @param slotIndex Logical SIM slot index.
+ *
+ * @see TelephonyManager.SimState
+ *
+ * @hide
+ */
+ @SimState
+ public static int getSimStateForSlotIndex(int slotIndex) {
+ try {
+ ITelephony telephony = ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
+ if (telephony != null) {
+ return telephony.getSimStateForSlotIndex(slotIndex);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in getSimStateForSlotIndex: " + e);
+ }
+ return TelephonyManager.SIM_STATE_UNKNOWN;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 5173405ac17d..c5f6902062ff 100644
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -135,11 +135,11 @@ interface ISub {
/**
* Set SIM icon tint color by simInfo index
- * @param tint the icon tint color of the SIM
* @param subId the unique SubscriptionInfo index in database
+ * @param tint the icon tint color of the SIM
* @return the number of records updated
*/
- int setIconTint(int tint, int subId);
+ int setIconTint(int subId, int tint);
/**
* Set display name by simInfo index with name source
@@ -242,8 +242,6 @@ interface ISub {
int getDefaultSubId();
- int clearSubInfo();
-
int getPhoneId(int subId);
/**
@@ -274,11 +272,6 @@ interface ISub {
boolean isSubscriptionEnabled(int subId);
int getEnabledSubscriptionId(int slotIndex);
- /**
- * Get the SIM state for the slot index
- * @return SIM state as the ordinal of IccCardConstants.State
- */
- int getSimStateForSlotIndex(int slotIndex);
boolean isActiveSubId(int subId, String callingPackage, String callingFeatureId);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index da1ffcdea812..ecafe702ea4e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2564,4 +2564,11 @@ interface ITelephony {
* @hide
*/
boolean isRemovableEsimDefaultEuicc(String callingPackage);
+
+ /**
+ * Get the SIM state for the logical SIM slot index.
+ *
+ * @param slotIndex Logical SIM slot index.
+ */
+ int getSimStateForSlotIndex(int slotIndex);
}