diff options
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); } |