diff options
153 files changed, 3402 insertions, 1096 deletions
diff --git a/Android.bp b/Android.bp index f6a9328d2501..b139b7e50676 100644 --- a/Android.bp +++ b/Android.bp @@ -132,6 +132,7 @@ filegroup { ":libcamera_client_aidl", ":libcamera_client_framework_aidl", ":libupdate_engine_aidl", + ":libupdate_engine_stable-V2-java-source", ":logd_aidl", ":resourcemanager_aidl", ":storaged_aidl", diff --git a/core/api/current.txt b/core/api/current.txt index 5abb92b1c2e4..c7b921c8f6d5 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -22102,7 +22102,7 @@ package android.media { } @FlaggedApi("android.media.audio.loudness_configurator_api") public class LoudnessCodecConfigurator { - method @FlaggedApi("android.media.audio.loudness_configurator_api") public void addMediaCodec(@NonNull android.media.MediaCodec); + method @FlaggedApi("android.media.audio.loudness_configurator_api") public boolean addMediaCodec(@NonNull android.media.MediaCodec); method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create(); method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create(@NonNull java.util.concurrent.Executor, @NonNull android.media.LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener); method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public android.os.Bundle getLoudnessCodecParams(@NonNull android.media.AudioTrack, @NonNull android.media.MediaCodec); @@ -28825,6 +28825,8 @@ package android.nfc { method public boolean isSecureNfcEnabled(); method public boolean isSecureNfcSupported(); method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled(); + method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity); + method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int); field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED"; field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED"; @@ -28840,6 +28842,13 @@ package android.nfc { field public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence"; field public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME"; field public static final String EXTRA_TAG = "android.nfc.extra.TAG"; + field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_DISABLE = 0; // 0x0 + field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_KEEP = -1; // 0xffffffff + field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_A = 1; // 0x1 + field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_B = 2; // 0x2 + field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_F = 4; // 0x4 + field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_DISABLE = 0; // 0x0 + field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_KEEP = -1; // 0xffffffff field public static final int FLAG_READER_NFC_A = 1; // 0x1 field public static final int FLAG_READER_NFC_B = 2; // 0x2 field public static final int FLAG_READER_NFC_BARCODE = 16; // 0x10 diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 014ddd41f8d4..705b2ee214f7 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -3481,24 +3481,26 @@ class ContextImpl extends Context { } mResources = r; - // only do this if the user already has more than one preferred locale - if (android.content.res.Flags.defaultLocale() - && r.getConfiguration().getLocales().size() > 1) { - LocaleConfig lc; - if (getUserId() < 0) { - lc = LocaleConfig.fromContextIgnoringOverride(this); - } else { - // This is needed because the app might have locale config overrides that need to - // be read from disk in order for resources to correctly choose which values to - // load. - StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads(); - try { - lc = new LocaleConfig(this); - } finally { - StrictMode.setThreadPolicy(policy); + if (r != null) { + // only do this if the user already has more than one preferred locale + if (android.content.res.Flags.defaultLocale() + && r.getConfiguration().getLocales().size() > 1) { + LocaleConfig lc; + if (getUserId() < 0) { + lc = LocaleConfig.fromContextIgnoringOverride(this); + } else { + // This is needed because the app might have locale config overrides that need + // to be read from disk in order for resources to correctly choose which values + // to load. + StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads(); + try { + lc = new LocaleConfig(this); + } finally { + StrictMode.setThreadPolicy(policy); + } } + mResourcesManager.setLocaleConfig(lc); } - mResourcesManager.setLocaleConfig(lc); } } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7c52238b477a..ee1d117bf71c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4207,7 +4207,9 @@ public class Intent implements Parcelable, Cloneable { * new state of quiet mode. This is only sent to registered receivers, not manifest receivers. * * <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_AVAILABLE} but functions as a - * generic broadcast for all profile users. + * generic broadcast for all users of type {@link android.os.UserManager#isProfile()}}. In + * case of a managed profile, both {@link #ACTION_MANAGED_PROFILE_AVAILABLE} and + * {@link #ACTION_PROFILE_AVAILABLE} broadcasts are sent. */ @FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE) public static final String ACTION_PROFILE_AVAILABLE = @@ -4221,7 +4223,9 @@ public class Intent implements Parcelable, Cloneable { * new state of quiet mode. This is only sent to registered receivers, not manifest receivers. * * <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_UNAVAILABLE} but functions as - * a generic broadcast for all profile users. + * a generic broadcast for all users of type {@link android.os.UserManager#isProfile()}}. In + * case of a managed profile, both {@link #ACTION_MANAGED_PROFILE_UNAVAILABLE} and + * {@link #ACTION_PROFILE_UNAVAILABLE} broadcasts are sent. */ @FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE) public static final String ACTION_PROFILE_UNAVAILABLE = diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java index 1c6de0464245..518f902acdcb 100644 --- a/core/java/android/hardware/face/FaceAuthenticateOptions.java +++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java @@ -261,7 +261,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable * The reason for this operation when requested by the system (sysui), * otherwise AUTHENTICATE_REASON_UNKNOWN. * - * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt + * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt * for more details about each reason. */ @DataClass.Generated.Member @@ -524,7 +524,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable * The reason for this operation when requested by the system (sysui), * otherwise AUTHENTICATE_REASON_UNKNOWN. * - * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt + * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt * for more details about each reason. */ @DataClass.Generated.Member diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 967a0cc92ef5..286cf2890eea 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -95,4 +95,6 @@ interface INfcAdapter void registerWlcStateListener(in INfcWlcStateListener listener); void unregisterWlcStateListener(in INfcWlcStateListener listener); WlcLDeviceInfo getWlcLDeviceInfo(); + + void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags); } diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl index 191385a3c13d..f4b46046bc3e 100644 --- a/core/java/android/nfc/INfcCardEmulation.aidl +++ b/core/java/android/nfc/INfcCardEmulation.aidl @@ -43,4 +43,7 @@ interface INfcCardEmulation ApduServiceInfo getPreferredPaymentService(int userHandle); boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status); boolean isDefaultPaymentRegistered(); + + boolean overrideRoutingTable(int userHandle, String protocol, String technology); + boolean recoverRoutingTable(int userHandle); } diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java index 8d75cac531fb..f03fc0af86b3 100644 --- a/core/java/android/nfc/NfcActivityManager.java +++ b/core/java/android/nfc/NfcActivityManager.java @@ -112,6 +112,9 @@ public final class NfcActivityManager extends IAppCallback.Stub Bundle readerModeExtras = null; Binder token; + int mPollTech = NfcAdapter.FLAG_USE_ALL_TECH; + int mListenTech = NfcAdapter.FLAG_USE_ALL_TECH; + public NfcActivityState(Activity activity) { if (activity.isDestroyed()) { throw new IllegalStateException("activity is already destroyed"); @@ -132,6 +135,9 @@ public final class NfcActivityManager extends IAppCallback.Stub readerModeFlags = 0; readerModeExtras = null; token = null; + + mPollTech = NfcAdapter.FLAG_USE_ALL_TECH; + mListenTech = NfcAdapter.FLAG_USE_ALL_TECH; } @Override public String toString() { @@ -278,6 +284,9 @@ public final class NfcActivityManager extends IAppCallback.Stub int readerModeFlags = 0; Bundle readerModeExtras = null; Binder token; + int pollTech; + int listenTech; + synchronized (NfcActivityManager.this) { NfcActivityState state = findActivityState(activity); if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state); @@ -286,9 +295,15 @@ public final class NfcActivityManager extends IAppCallback.Stub token = state.token; readerModeFlags = state.readerModeFlags; readerModeExtras = state.readerModeExtras; + + pollTech = state.mPollTech; + listenTech = state.mListenTech; } if (readerModeFlags != 0) { setReaderMode(token, readerModeFlags, readerModeExtras); + } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH + || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) { + changeDiscoveryTech(token, pollTech, listenTech); } requestNfcServiceCallback(); } @@ -298,6 +313,9 @@ public final class NfcActivityManager extends IAppCallback.Stub public void onActivityPaused(Activity activity) { boolean readerModeFlagsSet; Binder token; + int pollTech; + int listenTech; + synchronized (NfcActivityManager.this) { NfcActivityState state = findActivityState(activity); if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state); @@ -305,10 +323,17 @@ public final class NfcActivityManager extends IAppCallback.Stub state.resumed = false; token = state.token; readerModeFlagsSet = state.readerModeFlags != 0; + + pollTech = state.mPollTech; + listenTech = state.mListenTech; } if (readerModeFlagsSet) { // Restore default p2p modes setReaderMode(token, 0, null); + } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH + || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) { + changeDiscoveryTech(token, + NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH); } } @@ -333,4 +358,53 @@ public final class NfcActivityManager extends IAppCallback.Stub } } + /** setDiscoveryTechnology() implementation */ + public void setDiscoveryTech(Activity activity, int pollTech, int listenTech) { + boolean isResumed; + Binder token; + boolean readerModeFlagsSet; + synchronized (NfcActivityManager.this) { + NfcActivityState state = getActivityState(activity); + readerModeFlagsSet = state.readerModeFlags != 0; + state.mListenTech = listenTech; + state.mPollTech = pollTech; + token = state.token; + isResumed = state.resumed; + } + if (!readerModeFlagsSet && isResumed) { + changeDiscoveryTech(token, pollTech, listenTech); + } else if (readerModeFlagsSet) { + throw new IllegalStateException("Cannot be used when the Reader Mode is enabled"); + } + } + + /** resetDiscoveryTechnology() implementation */ + public void resetDiscoveryTech(Activity activity) { + boolean isResumed; + Binder token; + boolean readerModeFlagsSet; + synchronized (NfcActivityManager.this) { + NfcActivityState state = getActivityState(activity); + readerModeFlagsSet = state.readerModeFlags != 0; + state.mListenTech = NfcAdapter.FLAG_USE_ALL_TECH; + state.mPollTech = NfcAdapter.FLAG_USE_ALL_TECH; + token = state.token; + isResumed = state.resumed; + } + if (readerModeFlagsSet) { + disableReaderMode(activity); + } else if (isResumed) { + changeDiscoveryTech(token, NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH); + } + + } + + private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) { + try { + NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech); + } catch (RemoteException e) { + mAdapter.attemptDeadServiceRecovery(e); + } + } + } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 21e23ae53979..5a40e424ea91 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -333,6 +333,19 @@ public final class NfcAdapter { */ public static final int FLAG_READER_NFC_BARCODE = 0x10; + /** @hide */ + @IntDef(flag = true, prefix = {"FLAG_READER_"}, value = { + FLAG_READER_KEEP, + FLAG_READER_DISABLE, + FLAG_READER_NFC_A, + FLAG_READER_NFC_B, + FLAG_READER_NFC_F, + FLAG_READER_NFC_V, + FLAG_READER_NFC_BARCODE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PollTechnology {} + /** * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. * <p> @@ -360,6 +373,76 @@ public final class NfcAdapter { public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence"; /** + * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}. + * <p> + * Setting this flag enables listening for Nfc-A technology. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) + public static final int FLAG_LISTEN_NFC_PASSIVE_A = 0x1; + + /** + * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}. + * <p> + * Setting this flag enables listening for Nfc-B technology. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) + public static final int FLAG_LISTEN_NFC_PASSIVE_B = 1 << 1; + + /** + * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}. + * <p> + * Setting this flag enables listening for Nfc-F technology. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) + public static final int FLAG_LISTEN_NFC_PASSIVE_F = 1 << 2; + + /** + * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}. + * <p> + * Setting this flag disables listening. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) + public static final int FLAG_LISTEN_DISABLE = 0x0; + + /** + * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}. + * <p> + * Setting this flag disables polling. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) + public static final int FLAG_READER_DISABLE = 0x0; + + /** + * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}. + * <p> + * Setting this flag makes listening to use current flags. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) + public static final int FLAG_LISTEN_KEEP = -1; + + /** + * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}. + * <p> + * Setting this flag makes polling to use current flags. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) + public static final int FLAG_READER_KEEP = -1; + + /** @hide */ + public static final int FLAG_USE_ALL_TECH = 0xff; + + /** @hide */ + @IntDef(flag = true, prefix = {"FLAG_LISTEN_"}, value = { + FLAG_LISTEN_KEEP, + FLAG_LISTEN_DISABLE, + FLAG_LISTEN_NFC_PASSIVE_A, + FLAG_LISTEN_NFC_PASSIVE_B, + FLAG_LISTEN_NFC_PASSIVE_F + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ListenTechnology {} + + /** * @hide * @removed */ @@ -437,12 +520,14 @@ public final class NfcAdapter { @Retention(RetentionPolicy.SOURCE) public @interface TagIntentAppPreferenceResult {} - // Guarded by NfcAdapter.class + // Guarded by sLock static boolean sIsInitialized = false; static boolean sHasNfcFeature; static boolean sHasCeFeature; static boolean sHasNfcWlcFeature; + static Object sLock = new Object(); + // Final after first constructor, except for // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort // recovery @@ -1235,7 +1320,7 @@ public final class NfcAdapter { @java.lang.Deprecated @UnsupportedAppUsage public void setBeamPushUris(Uri[] uris, Activity activity) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1305,7 +1390,7 @@ public final class NfcAdapter { @java.lang.Deprecated @UnsupportedAppUsage public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1390,7 +1475,7 @@ public final class NfcAdapter { @UnsupportedAppUsage public void setNdefPushMessage(NdefMessage message, Activity activity, Activity ... activities) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1404,7 +1489,7 @@ public final class NfcAdapter { @SystemApi @UnsupportedAppUsage public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1483,7 +1568,7 @@ public final class NfcAdapter { @UnsupportedAppUsage public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity, Activity ... activities) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1534,7 +1619,7 @@ public final class NfcAdapter { @UnsupportedAppUsage public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback, Activity activity, Activity ... activities) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1577,7 +1662,7 @@ public final class NfcAdapter { */ public void enableForegroundDispatch(Activity activity, PendingIntent intent, IntentFilter[] filters, String[][] techLists) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1612,7 +1697,7 @@ public final class NfcAdapter { * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void disableForegroundDispatch(Activity activity) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1648,7 +1733,7 @@ public final class NfcAdapter { */ public void enableReaderMode(Activity activity, ReaderCallback callback, int flags, Bundle extras) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1665,7 +1750,7 @@ public final class NfcAdapter { * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void disableReaderMode(Activity activity) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1693,7 +1778,7 @@ public final class NfcAdapter { @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @SuppressLint("VisiblySynchronized") public void setReaderMode(boolean enablePolling) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1708,6 +1793,80 @@ public final class NfcAdapter { } /** + * Set the NFC controller to enable specific poll/listen technologies, + * as specified in parameters, while this Activity is in the foreground. + * + * Use {@link #FLAG_READER_KEEP} to keep current polling technology. + * Use {@link #FLAG_LISTEN_KEEP} to keep current listenig technology. + * Use {@link #FLAG_READER_DISABLE} to disable polling. + * Use {@link #FLAG_LISTEN_DISABLE} to disable listening. + * Also refer to {@link #resetDiscoveryTechnology(Activity)} to restore these changes. + * </p> + * The pollTech, listenTech parameters can be one or several of below list. + * <pre> + * Poll Listen + * Passive A 0x01 (NFC_A) 0x01 (NFC_PASSIVE_A) + * Passive B 0x02 (NFC_B) 0x02 (NFC_PASSIVE_B) + * Passive F 0x04 (NFC_F) 0x04 (NFC_PASSIVE_F) + * ISO 15693 0x08 (NFC_V) - + * Kovio 0x10 (NFC_BARCODE) - + * </pre> + * <p>Example usage in an Activity that requires to disable poll, + * keep current listen technologies: + * <pre> + * protected void onResume() { + * mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext()); + * mNfcAdapter.setDiscoveryTechnology(this, + * NfcAdapter.FLAG_READER_DISABLE, NfcAdapter.FLAG_LISTEN_KEEP); + * }</pre></p> + * @param activity The Activity that requests NFC controller to enable specific technologies. + * @param pollTech Flags indicating poll technologies. + * @param listenTech Flags indicating listen technologies. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF are unavailable. + */ + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) + public void setDiscoveryTechnology(@NonNull Activity activity, + @PollTechnology int pollTech, @ListenTechnology int listenTech) { + if (listenTech == FLAG_LISTEN_DISABLE) { + synchronized (sLock) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } + mNfcActivityManager.enableReaderMode(activity, null, pollTech, null); + return; + } + if (pollTech == FLAG_READER_DISABLE) { + synchronized (sLock) { + if (!sHasCeFeature) { + throw new UnsupportedOperationException(); + } + } + } else { + synchronized (sLock) { + if (!sHasNfcFeature || !sHasCeFeature) { + throw new UnsupportedOperationException(); + } + } + } + mNfcActivityManager.setDiscoveryTech(activity, pollTech, listenTech); + } + + /** + * Restore the poll/listen technologies of NFC controller, + * which were changed by {@link #setDiscoveryTechnology(Activity , int , int)} + * + * @param activity The Activity that requests to changed technologies. + */ + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) + public void resetDiscoveryTechnology(@NonNull Activity activity) { + mNfcActivityManager.resetDiscoveryTech(activity); + } + + /** * Manually invoke Android Beam to share data. * * <p>The Android Beam animation is normally only shown when two NFC-capable @@ -1737,7 +1896,7 @@ public final class NfcAdapter { @java.lang.Deprecated @UnsupportedAppUsage public boolean invokeBeam(Activity activity) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1775,7 +1934,7 @@ public final class NfcAdapter { @Deprecated @UnsupportedAppUsage public void enableForegroundNdefPush(Activity activity, NdefMessage message) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1805,7 +1964,7 @@ public final class NfcAdapter { @Deprecated @UnsupportedAppUsage public void disableForegroundNdefPush(Activity activity) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -2085,7 +2244,7 @@ public final class NfcAdapter { @java.lang.Deprecated @UnsupportedAppUsage public boolean isNdefPushEnabled() { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -2199,7 +2358,7 @@ public final class NfcAdapter { @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler, String[] tagTechnologies) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -2248,7 +2407,7 @@ public final class NfcAdapter { @SystemApi @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(NfcUnlockHandler unlockHandler) { - synchronized (NfcAdapter.class) { + synchronized (sLock) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index 58b6179691e9..ad86d70db967 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -998,6 +998,87 @@ public final class CardEmulation { } } + /** + * Setting NFC controller routing table, which includes Protocol Route and Technology Route, + * while this Activity is in the foreground. + * + * The parameter set to null can be used to keep current values for that entry. + * <p> + * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route: + * <pre> + * protected void onResume() { + * mNfcAdapter.overrideRoutingTable(this , "ESE" , null); + * }</pre> + * </p> + * Also activities must call this method when it goes to the background, + * with all parameters set to null. + * @param activity The Activity that requests NFC controller routing table to be changed. + * @param protocol ISO-DEP route destination, which can be "DH" or "UICC" or "ESE". + * @param technology Tech-A, Tech-B route destination, which can be "DH" or "UICC" or "ESE". + * @return true if operation is successful and false otherwise + * + * This is a high risk API and only included to support mainline effort + * @hide + */ + public boolean overrideRoutingTable(Activity activity, String protocol, String technology) { + if (activity == null) { + throw new NullPointerException("activity or service or category is null"); + } + if (!activity.isResumed()) { + throw new IllegalArgumentException("Activity must be resumed."); + } + try { + return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + + /** + * Restore the NFC controller routing table, + * which was changed by {@link #overrideRoutingTable(Activity, String, String)} + * + * @param activity The Activity that requested NFC controller routing table to be changed. + * @return true if operation is successful and false otherwise + * + * @hide + */ + public boolean recoverRoutingTable(Activity activity) { + if (activity == null) { + throw new NullPointerException("activity is null"); + } + if (!activity.isResumed()) { + throw new IllegalArgumentException("Activity must be resumed."); + } + try { + return sService.recoverRoutingTable(UserHandle.myUserId()); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.recoverRoutingTable(UserHandle.myUserId()); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + void recoverService() { NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext); sService = adapter.getCardEmulationService(); diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig index ce4f77725ef1..01a45708fddf 100644 --- a/core/java/android/nfc/flags.aconfig +++ b/core/java/android/nfc/flags.aconfig @@ -62,3 +62,10 @@ flag { description: "Flag for NFC charging changes" bug: "292143899" } + +flag { + name: "enable_nfc_set_discovery_tech" + namespace: "nfc" + description: "Flag for NFC set discovery tech API" + bug: "300351519" +} diff --git a/core/java/android/os/UpdateEngineStable.java b/core/java/android/os/UpdateEngineStable.java new file mode 100644 index 000000000000..9e2593e39e0e --- /dev/null +++ b/core/java/android/os/UpdateEngineStable.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2024 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; + +/** + * UpdateEngineStable handles calls to the update engine stalbe which takes care of A/B OTA updates. + * This interface has lesser functionalities than UpdateEngine and doesn't allow cancel. + * + * <p>The minimal flow is: + * + * <ol> + * <li>Create a new UpdateEngineStable instance. + * <li>Call {@link #bind}, provide callback function. + * <li>Call {@link #applyPayloadFd}. + * </ol> + * + * The APIs defined in this class and UpdateEngineStableCallback class must be in sync with the ones + * in {@code system/update_engine/stable/android/os/IUpdateEngineStable.aidl} and {@code + * ssystem/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl}. + * + * @hide + */ +public class UpdateEngineStable { + private static final String TAG = "UpdateEngineStable"; + + private static final String UPDATE_ENGINE_STABLE_SERVICE = + "android.os.UpdateEngineStableService"; + + /** + * Error codes from update engine upon finishing a call to {@link applyPayloadFd}. Values will + * be passed via the callback function {@link + * UpdateEngineStableCallback#onPayloadApplicationComplete}. Values must agree with the ones in + * {@code system/update_engine/common/error_code.h}. + */ + /** @hide */ + @IntDef( + value = { + UpdateEngine.ErrorCodeConstants.SUCCESS, + UpdateEngine.ErrorCodeConstants.ERROR, + UpdateEngine.ErrorCodeConstants.FILESYSTEM_COPIER_ERROR, + UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR, + UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR, + UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR, + UpdateEngine.ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR, + UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR, + UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, + UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR, + UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR, + UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR, + UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE, + UpdateEngine.ErrorCodeConstants.NOT_ENOUGH_SPACE, + UpdateEngine.ErrorCodeConstants.DEVICE_CORRUPTED, + }) + public @interface ErrorCode {} + + private final IUpdateEngineStable mUpdateEngineStable; + private IUpdateEngineStableCallback mUpdateEngineStableCallback = null; + private final Object mUpdateEngineStableCallbackLock = new Object(); + + /** + * Creates a new instance. + * + * @hide + */ + public UpdateEngineStable() { + mUpdateEngineStable = + IUpdateEngineStable.Stub.asInterface( + ServiceManager.getService(UPDATE_ENGINE_STABLE_SERVICE)); + if (mUpdateEngineStable == null) { + throw new IllegalStateException("Failed to find " + UPDATE_ENGINE_STABLE_SERVICE); + } + } + + /** + * Prepares this instance for use. The callback will be notified on any status change, and when + * the update completes. A handler can be supplied to control which thread runs the callback, or + * null. + * + * @hide + */ + public boolean bind(final UpdateEngineStableCallback callback, final Handler handler) { + synchronized (mUpdateEngineStableCallbackLock) { + mUpdateEngineStableCallback = + new IUpdateEngineStableCallback.Stub() { + @Override + public void onStatusUpdate(final int status, final float percent) { + if (handler != null) { + handler.post( + new Runnable() { + @Override + public void run() { + callback.onStatusUpdate(status, percent); + } + }); + } else { + callback.onStatusUpdate(status, percent); + } + } + + @Override + public void onPayloadApplicationComplete(final int errorCode) { + if (handler != null) { + handler.post( + new Runnable() { + @Override + public void run() { + callback.onPayloadApplicationComplete(errorCode); + } + }); + } else { + callback.onPayloadApplicationComplete(errorCode); + } + } + + @Override + public int getInterfaceVersion() { + return super.VERSION; + } + + @Override + public String getInterfaceHash() { + return super.HASH; + } + }; + + try { + return mUpdateEngineStable.bind(mUpdateEngineStableCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Equivalent to {@code bind(callback, null)}. + * + * @hide + */ + public boolean bind(final UpdateEngineStableCallback callback) { + return bind(callback, null); + } + + /** + * Applies payload from given ParcelFileDescriptor. Usage is same as UpdateEngine#applyPayload + * + * @hide + */ + public void applyPayloadFd( + ParcelFileDescriptor fd, long offset, long size, String[] headerKeyValuePairs) { + try { + mUpdateEngineStable.applyPayloadFd(fd, offset, size, headerKeyValuePairs); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unbinds the last bound callback function. + * + * @hide + */ + public boolean unbind() { + synchronized (mUpdateEngineStableCallbackLock) { + if (mUpdateEngineStableCallback == null) { + return true; + } + try { + boolean result = mUpdateEngineStable.unbind(mUpdateEngineStableCallback); + mUpdateEngineStableCallback = null; + return result; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } +} diff --git a/core/java/android/os/UpdateEngineStableCallback.java b/core/java/android/os/UpdateEngineStableCallback.java new file mode 100644 index 000000000000..4bcfb4baae95 --- /dev/null +++ b/core/java/android/os/UpdateEngineStableCallback.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 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; + +/** + * Callback function for UpdateEngineStable. Used to keep the caller up to date with progress, so + * the UI (if any) can be updated. + * + * <p>The APIs defined in this class and UpdateEngineStable class must be in sync with the ones in + * system/update_engine/stable/android/os/IUpdateEngineStable.aidl and + * system/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl. + * + * <p>{@hide} + */ +public abstract class UpdateEngineStableCallback { + + /** + * Invoked when anything changes. The value of {@code status} will be one of the values from + * {@link UpdateEngine.UpdateStatusConstants}, and {@code percent} will be valid + * + * @hide + */ + public abstract void onStatusUpdate(int status, float percent); + + /** + * Invoked when the payload has been applied, whether successfully or unsuccessfully. The value + * of {@code errorCode} will be one of the values from {@link UpdateEngine.ErrorCodeConstants}. + * + * @hide + */ + public abstract void onPayloadApplicationComplete(@UpdateEngineStable.ErrorCode int errorCode); +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 54cc5f471c33..84fddcb2c22a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7428,6 +7428,20 @@ public final class Settings { public static final String DEFAULT_INPUT_METHOD = "default_input_method"; /** + * Used only by {@link com.android.server.inputmethod.InputMethodManagerService} as a + * temporary data store of {@link #DEFAULT_INPUT_METHOD} while a virtual-device-specific + * input method is set as default.</p> + * + * <p>This should be considered to be an implementation detail of + * {@link com.android.server.inputmethod.InputMethodManagerService}. Other system + * components should never rely on this value.</p> + * + * @see #DEFAULT_INPUT_METHOD + * @hide + */ + public static final String DEFAULT_DEVICE_INPUT_METHOD = "default_device_input_method"; + + /** * Setting to record the input method subtype used by default, holding the ID * of the desired method. */ @@ -14347,6 +14361,19 @@ public final class Settings { "mute_alarm_stream_with_ringer_mode"; /** + * The user's choice for whether or not Alarm stream should always be muted with Ringer. + * + * <p>Note that this is different from {@link #MUTE_ALARM_STREAM_WITH_RINGER_MODE}, which + * controls the real state of whether or not the Alarm stream and Ringer association occurs. + * The two Settings are not necessarily equal, if the final decision for the association + * depends on factors beyond the user's preference. + * + * @hide + */ + public static final String MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE = + "mute_alarm_stream_with_ringer_mode_user_preference"; + + /** * Overlay display devices setting. * The associated value is a specially formatted string that describes the * size and density of simulated secondary display devices. diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index e4af2da57f40..d47ff2e2cd99 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -368,11 +368,13 @@ public final class Telephony { * <p> * As of Android 11 apps will need specific permission to query other packages. To use * this method an app must include in their AndroidManifest: + * <pre>{@code * <queries> * <intent> * <action android:name="android.provider.Telephony.SMS_DELIVER"/> * </intent> * </queries> + * }</pre> * Which will allow them to query packages which declare intent filters that include * the {@link android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION} intent. * </p> diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 692dad49ec89..1a2be15b2e8d 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -1439,12 +1439,12 @@ public abstract class WallpaperService extends Service { // to make sure the wallpaper is stopped even after the events // onSurfaceCreated() and onSurfaceChanged(). if (noConsecutiveVisibilityEvents()) { - if (DEBUG) Log.v(TAG, "toggling doVisibilityChanged"); - Trace.beginSection("WPMS.Engine.doVisibilityChanged-true"); - doVisibilityChanged(true); + if (DEBUG) Log.v(TAG, "toggling onVisibilityChanged"); + Trace.beginSection("WPMS.Engine.onVisibilityChanged-true"); + onVisibilityChanged(true); Trace.endSection(); - Trace.beginSection("WPMS.Engine.doVisibilityChanged-false"); - doVisibilityChanged(false); + Trace.beginSection("WPMS.Engine.onVisibilityChanged-false"); + onVisibilityChanged(false); Trace.endSection(); } else { if (DEBUG) { diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 216acdce38fb..3366a7ee23c0 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -38,6 +38,14 @@ flag { } flag { + name: "draw_magnifier_border_outside_wmlock" + namespace: "windowing_frontend" + description: "Avoid holding WM locks for a long time when executing lockCanvas" + bug: "316075123" + is_fixed_read_only: true +} + +flag { name: "introduce_smoother_dimmer" namespace: "windowing_frontend" description: "Refactor dim to fix flickers" diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 65597de44255..066f38b61eec 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -819,14 +819,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId); return; } + // Checks if container should be updated before apply new parentInfo. + final boolean shouldUpdateContainer = taskContainer.shouldUpdateContainer(parentInfo); taskContainer.updateTaskFragmentParentInfo(parentInfo); if (!taskContainer.isVisible()) { // Don't update containers if the task is not visible. We only update containers when // parentInfo#isVisibleRequested is true. return; } - if (isInPictureInPicture(parentInfo.getConfiguration())) { - // No need to update presentation in PIP until the Task exit PIP. + + // If the last direct activity of the host task is dismissed and the overlay container is + // the only taskFragment, the overlay container should also be dismissed. + dismissOverlayContainerIfNeeded(wct, taskContainer); + + if (!shouldUpdateContainer) { return; } updateContainersInTask(wct, taskContainer); @@ -1947,11 +1953,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen void updateOverlayContainer(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container) { final TaskContainer taskContainer = container.getTaskContainer(); - // Dismiss the overlay container if it's the only container in the task and there's no - // direct activity in the parent task. - if (taskContainer.getTaskFragmentContainers().size() == 1 - && !taskContainer.hasDirectActivity()) { - container.finish(false /* shouldFinishDependent */, mPresenter, wct, this); + + if (dismissOverlayContainerIfNeeded(wct, taskContainer)) { return; } @@ -1968,6 +1971,24 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } + /** Dismisses the overlay container in the {@code taskContainer} if needed. */ + @GuardedBy("mLock") + private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct, + @NonNull TaskContainer taskContainer) { + final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer(); + if (overlayContainer == null) { + return false; + } + // Dismiss the overlay container if it's the only container in the task and there's no + // direct activity in the parent task. + if (taskContainer.getTaskFragmentContainers().size() == 1 + && !taskContainer.hasDirectActivity()) { + mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); + return true; + } + return false; + } + /** * Updates {@link SplitContainer} with the given {@link SplitAttributes} if the * {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 64ad4faa421d..71195b6df97e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -138,6 +138,21 @@ class TaskContainer { } /** + * Returns {@code true} if the container should be updated with {@code info}. + */ + boolean shouldUpdateContainer(@NonNull TaskFragmentParentInfo info) { + final Configuration configuration = info.getConfiguration(); + + return info.isVisible() + // No need to update presentation in PIP until the Task exit PIP. + && !isInPictureInPicture(configuration) + // If the task properties equals regardless of starting position, don't need to + // update the container. + && (mConfiguration.diffPublicOnly(configuration) != 0 + || mDisplayId != info.getDisplayId()); + } + + /** * Returns the windowing mode for the TaskFragments below this Task, which should be split with * other TaskFragments. * @@ -161,7 +176,11 @@ class TaskContainer { } boolean isInPictureInPicture() { - return getWindowingMode() == WINDOWING_MODE_PINNED; + return isInPictureInPicture(mConfiguration); + } + + private static boolean isInPictureInPicture(@NonNull Configuration configuration) { + return configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED; } boolean isInMultiWindow() { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index b52971a15a3c..6fe8e50f105f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -616,6 +616,9 @@ class TaskFragmentContainer { * Removes all activities that belong to this process and finishes other containers/activities * configured to finish together. */ + // Suppress GuardedBy warning because lint ask to mark this method as + // @GuardedBy(container.mController.mLock), which is mLock itself + @SuppressWarnings("GuardedBy") @GuardedBy("mController.mLock") void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter, @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java index 396956e56bb5..6624c703f027 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java @@ -77,9 +77,11 @@ class TransactionManager { @NonNull TransactionRecord startNewTransaction(@Nullable IBinder taskFragmentTransactionToken) { if (mCurrentTransaction != null) { + final TransactionRecord lastTransaction = mCurrentTransaction; mCurrentTransaction = null; throw new IllegalStateException( - "The previous transaction has not been applied or aborted,"); + "The previous transaction:" + lastTransaction + " has not been applied or " + + "aborted."); } mCurrentTransaction = new TransactionRecord(taskFragmentTransactionToken); return mCurrentTransaction; @@ -199,5 +201,15 @@ class TransactionManager { ? mOriginType : TASK_FRAGMENT_TRANSIT_CHANGE; } + + @Override + @NonNull + public String toString() { + return TransactionRecord.class.getSimpleName() + "{" + + "token=" + mTaskFragmentTransactionToken + + ", type=" + getTransactionTransitionType() + + ", transaction=" + mTransaction + + "}"; + } } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index 5ef6a5263f96..bc921010b469 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -472,6 +472,29 @@ public class OverlayPresentationTest { verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs)); } + @Test + public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() { + final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test"); + + final TaskContainer taskContainer = overlayContainer.getTaskContainer(); + spyOn(taskContainer); + final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties(); + final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo( + new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(), + true /* visible */, false /* hasDirectActivity */, null /* decorSurface */); + parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10); + + mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo); + + // The parent info must be applied to the task container + verify(taskContainer).updateTaskFragmentParentInfo(parentInfo); + verify(mSplitController, never()).updateContainer(any(), any()); + + assertWithMessage("The overlay container must still be dismissed even if " + + "#updateContainer is not called") + .that(taskContainer.getOverlayContainer()).isNull(); + } + /** * A simplified version of {@link SplitController.ActivityStartMonitor * #createOrUpdateOverlayTaskFragmentIfNeeded} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java index 6213f628dfd3..8f04f126960c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java @@ -63,6 +63,10 @@ class TouchTracker { if ((touchX < mStartThresholdX && mSwipeEdge == BackEvent.EDGE_LEFT) || (touchX > mStartThresholdX && mSwipeEdge == BackEvent.EDGE_RIGHT)) { mStartThresholdX = touchX; + if ((mSwipeEdge == BackEvent.EDGE_LEFT && mStartThresholdX < mInitTouchX) + || (mSwipeEdge == BackEvent.EDGE_RIGHT && mStartThresholdX > mInitTouchX)) { + mInitTouchX = mStartThresholdX; + } } mLatestTouchX = touchX; mLatestTouchY = touchY; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt index bf07dccd0658..6dbb1e2b8d92 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt @@ -170,6 +170,71 @@ class TouchTrackerTest { nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / nonLinearTarget) } + @Test + fun restartingGesture_resetsInitialTouchX_leftEdge() { + val linearTracker = linearTouchTracker() + linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT) + var touchX = 100f + val velocityX = 0f + val velocityY = 0f + + // assert that progress is increased when increasing touchX + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) + + // assert that progress is reset to 0 when start location is updated + linearTracker.updateStartLocation() + linearTracker.assertProgress(0f) + + // assert that progress remains 0 when touchX is decreased + touchX -= 50 + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress(0f) + + // assert that progress uses new minimal touchX for progress calculation + val newInitialTouchX = touchX + touchX += 100 + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE) + + // assert the same for triggerBack==true + linearTracker.triggerBack = true + linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE) + } + + @Test + fun restartingGesture_resetsInitialTouchX_rightEdge() { + val linearTracker = linearTouchTracker() + linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT) + + var touchX = INITIAL_X_RIGHT_EDGE - 100f + val velocityX = 0f + val velocityY = 0f + + // assert that progress is increased when decreasing touchX + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / MAX_DISTANCE) + + // assert that progress is reset to 0 when start location is updated + linearTracker.updateStartLocation() + linearTracker.assertProgress(0f) + + // assert that progress remains 0 when touchX is increased + touchX += 50 + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress(0f) + + // assert that progress uses new maximal touchX for progress calculation + val newInitialTouchX = touchX + touchX -= 100 + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE) + + // assert the same for triggerBack==true + linearTracker.triggerBack = true + linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE) + } + companion object { private const val MAX_DISTANCE = 500f private const val LINEAR_DISTANCE = 400f diff --git a/media/java/android/media/LoudnessCodecConfigurator.java b/media/java/android/media/LoudnessCodecConfigurator.java index de9d87c0b28c..aadd78328d68 100644 --- a/media/java/android/media/LoudnessCodecConfigurator.java +++ b/media/java/android/media/LoudnessCodecConfigurator.java @@ -234,19 +234,21 @@ public class LoudnessCodecConfigurator { * @param mediaCodec the codec to start receiving asynchronous loudness * updates. The codec has to be in a configured or started * state in order to add it for loudness updates. - * @throws IllegalArgumentException if the {@code mediaCodec} was not configured, - * does not contain loudness metadata or if it - * was already added before + * @throws IllegalArgumentException if the same {@code mediaCodec} was already + * added before. + * @return {@code false} if the {@code mediaCodec} was not configured or does + * not contain loudness metadata, {@code true} otherwise. */ @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API) - public void addMediaCodec(@NonNull MediaCodec mediaCodec) { + public boolean addMediaCodec(@NonNull MediaCodec mediaCodec) { final MediaCodec mc = Objects.requireNonNull(mediaCodec, "MediaCodec for addMediaCodec cannot be null"); int piid = PLAYER_PIID_INVALID; final LoudnessCodecInfo mcInfo = getCodecInfo(mc); if (mcInfo == null) { - throw new IllegalArgumentException("Could not extract codec loudness information"); + Log.v(TAG, "Could not extract codec loudness information"); + return false; } synchronized (mConfiguratorLock) { final AtomicBoolean containsCodec = new AtomicBoolean(false); @@ -271,6 +273,8 @@ public class LoudnessCodecConfigurator { if (piid != PLAYER_PIID_INVALID) { mLcDispatcher.addLoudnessCodecInfo(piid, mediaCodec.hashCode(), mcInfo); } + + return true; } /** diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java index ce1004c4c58c..74e5612c0486 100644 --- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java +++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java @@ -19,6 +19,7 @@ package com.android.loudnesscodecapitest; import static android.media.audio.Flags.FLAG_LOUDNESS_CONFIGURATOR_API; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -191,6 +192,18 @@ public class LoudnessCodecConfiguratorTest { @Test @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API) + public void addUnconfiguredMediaCodec_returnsFalse() throws Exception { + final MediaCodec mediaCodec = MediaCodec.createDecoderByType("audio/mpeg"); + + try { + assertFalse(mLcc.addMediaCodec(mediaCodec)); + } finally { + mediaCodec.release(); + } + } + + @Test + @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API) public void setClearTrack_removeAllAudioServicePiidCodecs() throws Exception { final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class); final AudioTrack track = createAudioTrack(); diff --git a/packages/CredentialManager/res/drawable/more_horiz_24px.xml b/packages/CredentialManager/res/drawable/more_horiz_24px.xml new file mode 100644 index 000000000000..7b235f84f0fa --- /dev/null +++ b/packages/CredentialManager/res/drawable/more_horiz_24px.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M240,560Q207,560 183.5,536.5Q160,513 160,480Q160,447 183.5,423.5Q207,400 240,400Q273,400 296.5,423.5Q320,447 320,480Q320,513 296.5,536.5Q273,560 240,560ZM480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM720,560Q687,560 663.5,536.5Q640,513 640,480Q640,447 663.5,423.5Q687,400 720,400Q753,400 776.5,423.5Q800,447 800,480Q800,513 776.5,536.5Q753,560 720,560Z"/> +</vector> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index dfa5735fcaec..34f2509b2e1e 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -398,7 +398,7 @@ class CredentialAutofillService : AutofillService() { val sliceBuilder = InlineSuggestionUi .newContentBuilder(bottomSheetPendingIntent) .setStartIcon(Icon.createWithResource(this, - com.android.credentialmanager.R.drawable.ic_other_sign_in_24)) + com.android.credentialmanager.R.drawable.more_horiz_24px)) val presentationBuilder = Presentations.Builder() .setInlinePresentation(InlinePresentation( sliceBuilder.build().slice, spec, /* pinned= */ true)) diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java index 754437e64e78..b5af845ea0ac 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java @@ -76,7 +76,7 @@ public class UnarchiveActivity extends Activity { boolean hasRequestInstallPermission = Arrays.asList(getRequestedPermissions(callingPackage)) .contains(permission.REQUEST_INSTALL_PACKAGES); boolean hasInstallPermission = getBaseContext().checkPermission(permission.INSTALL_PACKAGES, - 0 /* random value for pid */, callingUid) != PackageManager.PERMISSION_GRANTED; + 0 /* random value for pid */, callingUid) == PackageManager.PERMISSION_GRANTED; if (!hasRequestInstallPermission && !hasInstallPermission) { Log.e(TAG, "Uid " + callingUid + " does not have " + permission.REQUEST_INSTALL_PACKAGES + " or " diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java index 6ccbc4cb5e6b..42dd382b98bc 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java @@ -28,12 +28,13 @@ public class UnarchiveFragment extends DialogFragment implements @Override public Dialog onCreateDialog(Bundle savedInstanceState) { String appTitle = getArguments().getString(UnarchiveActivity.APP_TITLE); + String installerTitle = getArguments().getString(UnarchiveActivity.INSTALLER_TITLE); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); dialogBuilder.setTitle( String.format(getContext().getString(R.string.unarchive_application_title), - appTitle)); + appTitle, installerTitle)); dialogBuilder.setMessage(R.string.unarchive_body_text); dialogBuilder.setPositiveButton(R.string.restore, this); diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt index f4edb36b5e76..460a6f7261ae 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt @@ -24,7 +24,8 @@ import com.android.settingslib.spa.framework.common.createSettingsPage import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider import com.android.settingslib.spa.gallery.card.CardPageProvider import com.android.settingslib.spa.gallery.chart.ChartPageProvider -import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider +import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider +import com.android.settingslib.spa.gallery.dialog.NavDialogProvider import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuCheckBoxProvider @@ -91,7 +92,8 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) { ProgressBarPageProvider, LoadingBarPageProvider, ChartPageProvider, - AlertDialogPageProvider, + DialogMainPageProvider, + NavDialogProvider, ItemListPageProvider, ItemOperatePageProvider, OperateListPageProvider, diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt index 1545a3e886b4..4e3fcee5383e 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt @@ -28,10 +28,10 @@ import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel -private const val TITLE = "AlertDialogPage" +private const val TITLE = "Category: Dialog" -object AlertDialogPageProvider : SettingsPageProvider { - override val name = "AlertDialogPage" +object DialogMainPageProvider : SettingsPageProvider { + override val name = "DialogMain" private val owner = createSettingsPage() override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf( @@ -47,6 +47,12 @@ object AlertDialogPageProvider : SettingsPageProvider { override val onClick = alertDialogPresenter::open }) }.build(), + SettingsEntryBuilder.create("NavDialog", owner).setUiLayoutFn { + Preference(object : PreferenceModel { + override val title = "Navigate to Dialog" + override val onClick = navigator(route = NavDialogProvider.name) + }) + }.build(), ) fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner) diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt new file mode 100644 index 000000000000..6f79911225b7 --- /dev/null +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 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.settingslib.spa.gallery.dialog + +import android.os.Bundle +import androidx.compose.runtime.Composable +import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.widget.dialog.SettingsDialogCard + +object NavDialogProvider : SettingsPageProvider { + override val name = "NavDialog" + override val navType = SettingsPageProvider.NavType.Dialog + + @Composable + override fun Page(arguments: Bundle?) { + SettingsDialogCard("Example Nav Dialog") {} + } +} diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt index 6a2e5985a735..1f028d5e7bc9 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt @@ -30,7 +30,7 @@ import com.android.settingslib.spa.gallery.SettingsPageProviderEnum import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider import com.android.settingslib.spa.gallery.card.CardPageProvider import com.android.settingslib.spa.gallery.chart.ChartPageProvider -import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider +import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider import com.android.settingslib.spa.gallery.page.ArgumentPageModel @@ -71,7 +71,7 @@ object HomePageProvider : SettingsPageProvider { ProgressBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), LoadingBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), - AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), + DialogMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), CardPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), CopyablePageProvider.buildInjectEntry().setLink(fromPage = owner).build(), diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt index 9f8c868f4aa4..5605485c4b84 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt @@ -22,7 +22,6 @@ import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.annotation.VisibleForTesting -import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -30,15 +29,19 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.core.view.WindowCompat +import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraph.Companion.findStartDestination +import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import androidx.navigation.compose.dialog import androidx.navigation.compose.rememberNavController import com.android.settingslib.spa.R import com.android.settingslib.spa.framework.common.LogCategory import com.android.settingslib.spa.framework.common.NullPageProvider import com.android.settingslib.spa.framework.common.SettingsPage import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.framework.common.SettingsPageProvider.NavType import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory import com.android.settingslib.spa.framework.common.createSettingsPage @@ -127,27 +130,31 @@ private fun NavControllerWrapperImpl.NavContent( allProvider: Collection<SettingsPageProvider>, content: @Composable (SettingsPage) -> Unit, ) { - // TODO(b/298520326): Remove Box after the issue is fixed. - // Wrap the top level node into a Box to workaround an issue of Compose 1.6.0-alpha03. - Box { - NavHost( - navController = navController, - startDestination = NullPageProvider.name, - ) { - composable(NullPageProvider.name) {} - for (spp in allProvider) { - animatedComposable( - route = spp.name + spp.parameter.navRoute(), - arguments = spp.parameter, - ) { navBackStackEntry -> - val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) } - content(page) - } + NavHost( + navController = navController, + startDestination = NullPageProvider.name, + ) { + composable(NullPageProvider.name) {} + for (spp in allProvider) { + destination(spp) { navBackStackEntry -> + val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) } + content(page) } } } } +private fun NavGraphBuilder.destination( + spp: SettingsPageProvider, + content: @Composable (NavBackStackEntry) -> Unit, +) { + val route = spp.name + spp.parameter.navRoute() + when (spp.navType) { + NavType.Page -> animatedComposable(route, spp.parameter) { content(it) } + NavType.Dialog -> dialog(route, spp.parameter) { content(it) } + } +} + @Composable private fun NavControllerWrapperImpl.InitialDestination( initialIntent: Intent?, diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt index 18f964ed7e6d..81bee5ec0b94 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt @@ -34,6 +34,14 @@ interface SettingsPageProvider { /** The page provider name, needs to be *unique* and *stable*. */ val name: String + enum class NavType { + Page, + Dialog, + } + + val navType: NavType + get() = NavType.Page + /** The display name of this page provider, for better readability. */ val displayName: String get() = name diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt index 8b172da08dd2..f08e7400d734 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt @@ -19,10 +19,13 @@ package com.android.settingslib.spa.widget.dialog import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding +import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.window.Dialog +import androidx.navigation.compose.NavHost import com.android.settingslib.spa.framework.theme.SettingsDimension import com.android.settingslib.spa.framework.theme.SettingsShape import com.android.settingslib.spa.widget.ui.SettingsTitle @@ -34,13 +37,27 @@ fun SettingsDialog( content: @Composable () -> Unit, ) { Dialog(onDismissRequest = onDismissRequest) { - Card(shape = SettingsShape.CornerExtraLarge) { - Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) { - Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) { - SettingsTitle(title = title, useMediumWeight = true) - } - content() + SettingsDialogCard(title, content) + } +} + +/** + * Card for dialog, suitable for independent dialog in the [NavHost]. + */ +@Composable +fun SettingsDialogCard( + title: String, + content: @Composable () -> Unit, +) { + Card( + shape = SettingsShape.CornerExtraLarge, + colors = CardDefaults.cardColors(containerColor = AlertDialogDefaults.containerColor), + ) { + Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) { + Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) { + SettingsTitle(title = title, useMediumWeight = true) } + content() } } } diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt index 92d3411b72f3..8cbd964bbd67 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt @@ -18,6 +18,7 @@ package com.android.settingslib.spa.framework import android.content.Context import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithText @@ -29,12 +30,14 @@ import com.android.settingslib.spa.framework.common.LogEvent import com.android.settingslib.spa.framework.common.SettingsPage import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory import com.android.settingslib.spa.framework.common.createSettingsPage +import com.android.settingslib.spa.tests.testutils.SppDialog import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest import com.android.settingslib.spa.tests.testutils.SpaLoggerForTest import com.android.settingslib.spa.tests.testutils.SppDisabled import com.android.settingslib.spa.tests.testutils.SppHome import com.android.settingslib.spa.testutils.waitUntil -import com.google.common.truth.Truth +import com.android.settingslib.spa.testutils.waitUntilExists +import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -106,11 +109,31 @@ class BrowseActivityTest { composeTestRule.onNodeWithText(sppDisabled.getTitle(null)).assertDoesNotExist() spaLogger.verifyPageEvent(pageDisabled.id, 0, 0) } + + @Test + fun browseContent_dialog() { + val spaEnvironment = SpaEnvironmentForTest( + context = context, + rootPages = listOf(SppHome.createSettingsPage()), + logger = spaLogger, + ) + SpaEnvironmentFactory.reset(spaEnvironment) + val sppRepository by spaEnvironment.pageProviderRepository + + composeTestRule.setContent { + BrowseContent( + sppRepository = sppRepository, + isPageEnabled = SettingsPage::isEnabled, + initialIntent = null, + ) + } + composeTestRule.onNodeWithText(SppDialog.name).performClick() + + composeTestRule.waitUntilExists(hasText(SppDialog.CONTENT)) + } } private fun SpaLoggerForTest.verifyPageEvent(id: String, entryCount: Int, leaveCount: Int) { - Truth.assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK)) - .isEqualTo(entryCount) - Truth.assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK)) - .isEqualTo(leaveCount) + assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK)).isEqualTo(entryCount) + assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK)).isEqualTo(leaveCount) } diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt index b139f2874a36..0a1c05f38f2d 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt @@ -22,6 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.framework.util.genEntryId import com.android.settingslib.spa.framework.util.genPageId import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest +import com.android.settingslib.spa.tests.testutils.SppDialog import com.android.settingslib.spa.tests.testutils.SppHome import com.android.settingslib.spa.tests.testutils.SppLayer1 import com.android.settingslib.spa.tests.testutils.SppLayer2 @@ -39,26 +40,21 @@ class SettingsEntryRepositoryTest { @Test fun testGetPageWithEntry() { val pageWithEntry = entryRepository.getAllPageWithEntry() - assertThat(pageWithEntry.size).isEqualTo(3) - assertThat( - entryRepository.getPageWithEntry(genPageId("SppHome")) - ?.entries?.size - ).isEqualTo(1) - assertThat( - entryRepository.getPageWithEntry(genPageId("SppLayer1")) - ?.entries?.size - ).isEqualTo(3) - assertThat( - entryRepository.getPageWithEntry(genPageId("SppLayer2")) - ?.entries?.size - ).isEqualTo(2) + + assertThat(pageWithEntry).hasSize(4) + assertThat(entryRepository.getPageWithEntry(genPageId("SppHome"))?.entries) + .hasSize(2) + assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer1"))?.entries) + .hasSize(3) + assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer2"))?.entries) + .hasSize(2) assertThat(entryRepository.getPageWithEntry(genPageId("SppWithParam"))).isNull() } @Test fun testGetEntry() { val entry = entryRepository.getAllEntries() - assertThat(entry.size).isEqualTo(7) + assertThat(entry).hasSize(8) assertThat( entryRepository.getEntry( genEntryId( @@ -91,6 +87,16 @@ class SettingsEntryRepositoryTest { ).isNotNull() assertThat( entryRepository.getEntry( + genEntryId( + "INJECT", + SppDialog.createSettingsPage(), + SppHome.createSettingsPage(), + SppDialog.createSettingsPage(), + ) + ) + ).isNotNull() + assertThat( + entryRepository.getEntry( genEntryId("Layer1Entry1", SppLayer1.createSettingsPage()) ) ).isNotNull() diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt index 857657331126..169c541e8b5f 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt @@ -25,39 +25,55 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SettingsPageProviderRepositoryTest { @Test - fun getStartPageTest() { - val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList()) + fun rootPages_empty() { + val sppRepoEmpty = SettingsPageProviderRepository(emptyList()) + assertThat(sppRepoEmpty.getDefaultStartPage()).isEqualTo("") assertThat(sppRepoEmpty.getAllRootPages()).isEmpty() + } + @Test + fun rootPages_single() { val nullPage = NullPageProvider.createSettingsPage() - val sppRepoNull = - SettingsPageProviderRepository(emptyList(), listOf(nullPage)) + + val sppRepoNull = SettingsPageProviderRepository( + allPageProviders = emptyList(), + rootPages = listOf(nullPage), + ) + assertThat(sppRepoNull.getDefaultStartPage()).isEqualTo("NULL") - assertThat(sppRepoNull.getAllRootPages()).contains(nullPage) + assertThat(sppRepoNull.getAllRootPages()).containsExactly(nullPage) + } + @Test + fun rootPages_twoPages() { val rootPage1 = createSettingsPage(sppName = "Spp1", displayName = "Spp1") val rootPage2 = createSettingsPage(sppName = "Spp2", displayName = "Spp2") - val sppRepo = SettingsPageProviderRepository(emptyList(), listOf(rootPage1, rootPage2)) - val allRoots = sppRepo.getAllRootPages() + + val sppRepo = SettingsPageProviderRepository( + allPageProviders = emptyList(), + rootPages = listOf(rootPage1, rootPage2), + ) + assertThat(sppRepo.getDefaultStartPage()).isEqualTo("Spp1") - assertThat(allRoots.size).isEqualTo(2) - assertThat(allRoots).contains(rootPage1) - assertThat(allRoots).contains(rootPage2) + assertThat(sppRepo.getAllRootPages()).containsExactly(rootPage1, rootPage2) } @Test - fun getProviderTest() { - val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList()) + fun getProviderOrNull_empty() { + val sppRepoEmpty = SettingsPageProviderRepository(emptyList()) assertThat(sppRepoEmpty.getAllProviders()).isEmpty() assertThat(sppRepoEmpty.getProviderOrNull("Spp")).isNull() + } + @Test + fun getProviderOrNull_single() { val sppRepo = SettingsPageProviderRepository(listOf( object : SettingsPageProvider { override val name = "Spp" } - ), emptyList()) - assertThat(sppRepo.getAllProviders().size).isEqualTo(1) + )) + assertThat(sppRepo.getAllProviders()).hasSize(1) assertThat(sppRepo.getProviderOrNull("Spp")).isNotNull() assertThat(sppRepo.getProviderOrNull("SppUnknown")).isNull() } diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt index 2755b4e18154..22a5ca328755 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt @@ -21,6 +21,8 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable import androidx.navigation.NavType import androidx.navigation.navArgument import com.android.settingslib.spa.framework.BrowseActivity @@ -88,6 +90,7 @@ object SppHome : SettingsPageProvider { val owner = this.createSettingsPage() return listOf( SppLayer1.buildInject().setLink(fromPage = owner).build(), + SppDialog.buildInject().setLink(fromPage = owner).build(), ) } } @@ -160,6 +163,21 @@ object SppLayer2 : SettingsPageProvider { } } +object SppDialog : SettingsPageProvider { + override val name = "SppDialog" + override val navType = SettingsPageProvider.NavType.Dialog + + const val CONTENT = "SppDialog Content" + + @Composable + override fun Page(arguments: Bundle?) { + Text(CONTENT) + } + + fun buildInject() = SettingsEntryBuilder.createInject(this.createSettingsPage()) + .setMacro { SimplePreferenceMacro(title = name, clickRoute = name) } +} + object SppForSearch : SettingsPageProvider { override val name = "SppForSearch" @@ -223,6 +241,7 @@ class SpaEnvironmentForTest( navArgument("rt_param") { type = NavType.StringType }, ) }, + SppDialog, ), rootPages ) diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java index 9560b8dc802e..cdb87404b016 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java @@ -19,6 +19,7 @@ import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECT import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHearingAid; import android.content.Context; import android.graphics.drawable.Drawable; import android.media.AudioManager; @@ -42,18 +43,16 @@ public class BluetoothMediaDevice extends MediaDevice { BluetoothMediaDevice( Context context, CachedBluetoothDevice device, - MediaRoute2Info info, - String packageName) { - this(context, device, info, packageName, null); + MediaRoute2Info info) { + this(context, device, info, null); } BluetoothMediaDevice( Context context, CachedBluetoothDevice device, MediaRoute2Info info, - String packageName, RouteListingPreference.Item item) { - super(context, info, packageName, item); + super(context, info, item); mCachedDevice = device; mAudioManager = context.getSystemService(AudioManager.class); initDeviceRecord(); @@ -100,7 +99,12 @@ public class BluetoothMediaDevice extends MediaDevice { @Override public String getId() { - return MediaDeviceUtils.getId(mCachedDevice); + if (mCachedDevice.isHearingAidDevice()) { + if (mCachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) { + return Long.toString(mCachedDevice.getHiSyncId()); + } + } + return mCachedDevice.getAddress(); } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java index 4e0ebd1bdc1a..338fb872650c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java @@ -34,9 +34,8 @@ public class ComplexMediaDevice extends MediaDevice { ComplexMediaDevice( Context context, MediaRoute2Info info, - String packageName, RouteListingPreference.Item item) { - super(context, info, packageName, item); + super(context, info, item); } // MediaRoute2Info.getName was made public on API 34, but exists since API 30. @@ -63,7 +62,7 @@ public class ComplexMediaDevice extends MediaDevice { @Override public String getId() { - return MediaDeviceUtils.getId(mRouteInfo); + return mRouteInfo.getId(); } public boolean isConnected() { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java index 012cbc05b5b2..1347dd131f69 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java @@ -45,14 +45,13 @@ public class InfoMediaDevice extends MediaDevice { InfoMediaDevice( Context context, MediaRoute2Info info, - String packageName, RouteListingPreference.Item item) { - super(context, info, packageName, item); + super(context, info, item); initDeviceRecord(); } - InfoMediaDevice(Context context, MediaRoute2Info info, String packageName) { - this(context, info, packageName, null); + InfoMediaDevice(Context context, MediaRoute2Info info) { + this(context, info, null); } @Override @@ -118,7 +117,7 @@ public class InfoMediaDevice extends MediaDevice { @Override public String getId() { - return MediaDeviceUtils.getId(mRouteInfo); + return mRouteInfo.getId(); } public boolean isConnected() { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index e5fce5bfe02a..581c7dea002c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -383,7 +383,7 @@ public abstract class InfoMediaManager extends MediaManager { for (MediaRoute2Info route : getSelectableRoutes(info)) { deviceList.add( new InfoMediaDevice( - mContext, route, mPackageName, mPreferenceItemMap.get(route.getId()))); + mContext, route, mPreferenceItemMap.get(route.getId()))); } return deviceList; } @@ -410,7 +410,7 @@ public abstract class InfoMediaManager extends MediaManager { for (MediaRoute2Info route : getDeselectableRoutes(info)) { deviceList.add( new InfoMediaDevice( - mContext, route, mPackageName, mPreferenceItemMap.get(route.getId()))); + mContext, route, mPreferenceItemMap.get(route.getId()))); Log.d(TAG, route.getName() + " is deselectable for " + mPackageName); } return deviceList; @@ -434,7 +434,7 @@ public abstract class InfoMediaManager extends MediaManager { for (MediaRoute2Info route : getSelectedRoutes(info)) { deviceList.add( new InfoMediaDevice( - mContext, route, mPackageName, mPreferenceItemMap.get(route.getId()))); + mContext, route, mPreferenceItemMap.get(route.getId()))); } return deviceList; } @@ -633,7 +633,6 @@ public abstract class InfoMediaManager extends MediaManager { new InfoMediaDevice( mContext, route, - mPackageName, mPreferenceItemMap.get(route.getId())); break; case TYPE_BUILTIN_SPEAKER: @@ -650,7 +649,6 @@ public abstract class InfoMediaManager extends MediaManager { new PhoneMediaDevice( mContext, route, - mPackageName, mPreferenceItemMap.getOrDefault(route.getId(), null)); break; case TYPE_HEARING_AID: @@ -666,7 +664,6 @@ public abstract class InfoMediaManager extends MediaManager { mContext, cachedDevice, route, - mPackageName, mPreferenceItemMap.getOrDefault(route.getId(), null)); } break; @@ -675,7 +672,6 @@ public abstract class InfoMediaManager extends MediaManager { new ComplexMediaDevice( mContext, route, - mPackageName, mPreferenceItemMap.get(route.getId())); break; default: diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index dbc3bf7b793e..ebcca42bb588 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -567,7 +567,7 @@ public class LocalMediaManager implements BluetoothCallback { final CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(device); if (isBondedMediaDevice(cachedDevice) && isMutingExpectedDevice(cachedDevice)) { - return new BluetoothMediaDevice(mContext, cachedDevice, null, mPackageName); + return new BluetoothMediaDevice(mContext, cachedDevice, null); } } return null; @@ -614,7 +614,7 @@ public class LocalMediaManager implements BluetoothCallback { mDisconnectedMediaDevices.clear(); for (CachedBluetoothDevice cachedDevice : cachedBluetoothDeviceList) { final MediaDevice mediaDevice = - new BluetoothMediaDevice(mContext, cachedDevice, null, mPackageName); + new BluetoothMediaDevice(mContext, cachedDevice, null); if (!mMediaDevices.contains(mediaDevice)) { cachedDevice.registerCallback(mDeviceAttributeChangeCallback); mDisconnectedMediaDevices.add(mediaDevice); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java index c8e4c0c93789..f2d9d1493c74 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java @@ -121,16 +121,13 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { protected final Context mContext; protected final MediaRoute2Info mRouteInfo; protected final RouteListingPreference.Item mItem; - protected final String mPackageName; MediaDevice( Context context, MediaRoute2Info info, - String packageName, RouteListingPreference.Item item) { mContext = context; mRouteInfo = info; - mPackageName = packageName; mItem = item; setType(info); } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java deleted file mode 100644 index b3a52b9ec788..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settingslib.media; - -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHearingAid; -import android.media.MediaRoute2Info; - -import com.android.settingslib.bluetooth.CachedBluetoothDevice; - -/** - * MediaDeviceUtils provides utility function for MediaDevice - */ -public class MediaDeviceUtils { - /** - * Use CachedBluetoothDevice address to represent unique id - * - * @param cachedDevice the CachedBluetoothDevice - * @return CachedBluetoothDevice address - */ - public static String getId(CachedBluetoothDevice cachedDevice) { - if (cachedDevice.isHearingAidDevice()) { - if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) { - return Long.toString(cachedDevice.getHiSyncId()); - } - } - return cachedDevice.getAddress(); - } - - /** - * Use BluetoothDevice address to represent unique id - * - * @param bluetoothDevice the BluetoothDevice - * @return BluetoothDevice address - */ - public static String getId(BluetoothDevice bluetoothDevice) { - return bluetoothDevice.getAddress(); - } - - /** - * Use MediaRoute2Info id to represent unique id - * - * @param route the MediaRoute2Info - * @return MediaRoute2Info id - */ - public static String getId(MediaRoute2Info route) { - return route.getId(); - } -} diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java index 0676ce5a65f1..d6f1eab442fa 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java @@ -117,16 +117,15 @@ public class PhoneMediaDevice extends MediaDevice { return name.toString(); } - PhoneMediaDevice(Context context, MediaRoute2Info info, String packageName) { - this(context, info, packageName, null); + PhoneMediaDevice(Context context, MediaRoute2Info info) { + this(context, info, null); } PhoneMediaDevice( Context context, MediaRoute2Info info, - String packageName, RouteListingPreference.Item item) { - super(context, info, packageName, item); + super(context, info, item); mDeviceIconUtil = new DeviceIconUtil(mContext); initDeviceRecord(); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java index f50802a1702e..7061742a56d2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java @@ -39,6 +39,8 @@ import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class BluetoothMediaDeviceTest { + private static final String TEST_ADDRESS = "11:22:33:44:55:66"; + @Mock private CachedBluetoothDevice mDevice; @@ -54,7 +56,7 @@ public class BluetoothMediaDeviceTest { when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true); when(mDevice.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true); - mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null, null); + mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null); } @Test @@ -111,4 +113,10 @@ public class BluetoothMediaDeviceTest { assertThat(mBluetoothMediaDevice.getIcon() instanceof BitmapDrawable).isFalse(); } + + @Test + public void getId_returnsCachedBluetoothDeviceAddress() { + when(mDevice.getAddress()).thenReturn(TEST_ADDRESS); + assertThat(mBluetoothMediaDevice.getId()).isEqualTo(TEST_ADDRESS); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java index a072c1722c10..0665308fdbfb 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java @@ -65,7 +65,7 @@ public class InfoMediaDeviceTest { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo, TEST_PACKAGE_NAME); + mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index 2252b69d61d6..f0330c46315c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -547,7 +547,7 @@ public class InfoMediaManagerTest { @Test public void connectDeviceWithoutPackageName_noSession_returnFalse() { final MediaRoute2Info info = mock(MediaRoute2Info.class); - final MediaDevice device = new InfoMediaDevice(mContext, info, TEST_PACKAGE_NAME); + final MediaDevice device = new InfoMediaDevice(mContext, info); final List<RoutingSessionInfo> infos = new ArrayList<>(); @@ -623,7 +623,7 @@ public class InfoMediaManagerTest { routingSessionInfos.add(info); final MediaRoute2Info route2Info = mock(MediaRoute2Info.class); - final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME); + final MediaDevice device = new InfoMediaDevice(mContext, route2Info); final List<String> list = new ArrayList<>(); list.add(TEST_ID); @@ -644,7 +644,7 @@ public class InfoMediaManagerTest { routingSessionInfos.add(info); final MediaRoute2Info route2Info = mock(MediaRoute2Info.class); - final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME); + final MediaDevice device = new InfoMediaDevice(mContext, route2Info); final List<String> list = new ArrayList<>(); list.add("fake_id"); @@ -674,7 +674,7 @@ public class InfoMediaManagerTest { routingSessionInfos.add(info); final MediaRoute2Info route2Info = mock(MediaRoute2Info.class); - final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME); + final MediaDevice device = new InfoMediaDevice(mContext, route2Info); final List<String> list = new ArrayList<>(); list.add(TEST_ID); @@ -695,7 +695,7 @@ public class InfoMediaManagerTest { routingSessionInfos.add(info); final MediaRoute2Info route2Info = mock(MediaRoute2Info.class); - final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME); + final MediaDevice device = new InfoMediaDevice(mContext, route2Info); final List<String> list = new ArrayList<>(); list.add("fake_id"); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 926b41a0017f..999e8d508e19 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -116,8 +116,8 @@ public class LocalMediaManagerTest { when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile); - mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1, TEST_PACKAGE_NAME)); - mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, TEST_PACKAGE_NAME); + mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1)); + mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2); mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, mInfoMediaManager, "com.test.packagename"); mLocalMediaManager.mAudioManager = mAudioManager; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java index 18055d97d835..098ab162c225 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java @@ -171,17 +171,17 @@ public class MediaDeviceTest { mBluetoothMediaDevice1 = new BluetoothMediaDevice( - mContext, mCachedDevice1, mBluetoothRouteInfo1, TEST_PACKAGE_NAME); + mContext, mCachedDevice1, mBluetoothRouteInfo1); mBluetoothMediaDevice2 = new BluetoothMediaDevice( - mContext, mCachedDevice2, mBluetoothRouteInfo2, TEST_PACKAGE_NAME); + mContext, mCachedDevice2, mBluetoothRouteInfo2); mBluetoothMediaDevice3 = new BluetoothMediaDevice( - mContext, mCachedDevice3, mBluetoothRouteInfo3, TEST_PACKAGE_NAME); - mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1, TEST_PACKAGE_NAME); - mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, TEST_PACKAGE_NAME); - mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3, TEST_PACKAGE_NAME); - mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo, TEST_PACKAGE_NAME); + mContext, mCachedDevice3, mBluetoothRouteInfo3); + mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1); + mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2); + mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3); + mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo); } @Test @@ -316,7 +316,7 @@ public class MediaDeviceTest { when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES); final PhoneMediaDevice phoneMediaDevice = - new PhoneMediaDevice(mContext, phoneRouteInfo, TEST_PACKAGE_NAME); + new PhoneMediaDevice(mContext, phoneRouteInfo); mMediaDevices.add(mBluetoothMediaDevice1); mMediaDevices.add(phoneMediaDevice); @@ -332,7 +332,7 @@ public class MediaDeviceTest { when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES); final PhoneMediaDevice phoneMediaDevice = - new PhoneMediaDevice(mContext, phoneRouteInfo, TEST_PACKAGE_NAME); + new PhoneMediaDevice(mContext, phoneRouteInfo); mMediaDevices.add(mInfoMediaDevice1); mMediaDevices.add(phoneMediaDevice); @@ -483,7 +483,7 @@ public class MediaDeviceTest { public void getFeatures_noRouteInfo_returnEmptyList() { mBluetoothMediaDevice1 = new BluetoothMediaDevice( - mContext, mCachedDevice1, null /* MediaRoute2Info */, TEST_PACKAGE_NAME); + mContext, mCachedDevice1, /* MediaRoute2Info */ null); assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0); } @@ -498,10 +498,9 @@ public class MediaDeviceTest { mContext, mCachedDevice1, null /* MediaRoute2Info */, - TEST_PACKAGE_NAME, mItem); mPhoneMediaDevice = - new PhoneMediaDevice(mContext, mPhoneRouteInfo, TEST_PACKAGE_NAME, mItem); + new PhoneMediaDevice(mContext, mPhoneRouteInfo, mItem); assertThat(mBluetoothMediaDevice1.getSelectionBehavior()).isEqualTo( SELECTION_BEHAVIOR_TRANSFER); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java deleted file mode 100644 index 30a6ad2576db..000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.media; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.when; - -import android.bluetooth.BluetoothDevice; -import android.media.MediaRoute2Info; - -import com.android.settingslib.bluetooth.CachedBluetoothDevice; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; - -@RunWith(RobolectricTestRunner.class) -public class MediaDeviceUtilsTest { - - private static final String TEST_ADDRESS = "11:22:33:44:55:66"; - private static final String TEST_ROUTE_ID = "test_route_id"; - - @Mock - private CachedBluetoothDevice mCachedDevice; - @Mock - private BluetoothDevice mBluetoothDevice; - @Mock - private MediaRoute2Info mRouteInfo; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void getId_returnCachedBluetoothDeviceAddress() { - when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS); - - final String id = MediaDeviceUtils.getId(mCachedDevice); - - assertThat(id).isEqualTo(TEST_ADDRESS); - } - - @Test - public void getId_returnBluetoothDeviceAddress() { - when(mBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS); - - final String id = MediaDeviceUtils.getId(mBluetoothDevice); - - assertThat(id).isEqualTo(TEST_ADDRESS); - } - - @Test - public void getId_returnRouteInfoId() { - when(mRouteInfo.getId()).thenReturn(TEST_ROUTE_ID); - - final String id = MediaDeviceUtils.getId(mRouteInfo); - - assertThat(id).isEqualTo(TEST_ROUTE_ID); - } -} diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java index d6e8d26af1a0..2e39adc8f79e 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java @@ -69,6 +69,7 @@ public class GlobalSettings { Settings.Global.PRIVATE_DNS_SPECIFIER, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, Settings.Global.ZEN_DURATION, + Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE, Settings.Global.REVERSE_CHARGING_AUTO_ON, Settings.Global.CHARGING_VIBRATION_ENABLED, Settings.Global.AWARE_ALLOWED, diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 59c3cd38a97d..e7d7bb01e180 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -19,93 +19,105 @@ package android.provider.settings.backup; import android.compat.annotation.UnsupportedAppUsage; import android.provider.Settings; +import com.android.server.display.feature.flags.Flags; + +import java.util.ArrayList; +import java.util.List; + /** Information about the system settings to back up */ public class SystemSettings { /** - * Settings to backup. + * Settings to back up. * * NOTE: Settings are backed up and restored in the order they appear * in this array. If you have one setting depending on another, * make sure that they are ordered appropriately. */ @UnsupportedAppUsage - public static final String[] SETTINGS_TO_BACKUP = { - Settings.System.STAY_ON_WHILE_PLUGGED_IN, // moved to global - Settings.System.WIFI_USE_STATIC_IP, - Settings.System.WIFI_STATIC_IP, - Settings.System.WIFI_STATIC_GATEWAY, - Settings.System.WIFI_STATIC_NETMASK, - Settings.System.WIFI_STATIC_DNS1, - Settings.System.WIFI_STATIC_DNS2, - Settings.System.BLUETOOTH_DISCOVERABILITY, - Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT, - Settings.System.FONT_SCALE, - Settings.System.DIM_SCREEN, - Settings.System.SCREEN_OFF_TIMEOUT, - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.ADAPTIVE_SLEEP, // moved to secure - Settings.System.APPLY_RAMPING_RINGER, - Settings.System.VIBRATE_INPUT_DEVICES, - Settings.System.MODE_RINGER_STREAMS_AFFECTED, - Settings.System.TEXT_AUTO_REPLACE, - Settings.System.TEXT_AUTO_CAPS, - Settings.System.TEXT_AUTO_PUNCTUATE, - Settings.System.TEXT_SHOW_PASSWORD, - Settings.System.AUTO_TIME, // moved to global - Settings.System.AUTO_TIME_ZONE, // moved to global - Settings.System.TIME_12_24, - Settings.System.DTMF_TONE_WHEN_DIALING, - Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, - Settings.System.HEARING_AID, - Settings.System.TTY_MODE, - Settings.System.MASTER_MONO, - Settings.System.MASTER_BALANCE, - Settings.System.FOLD_LOCK_BEHAVIOR, - Settings.System.SOUND_EFFECTS_ENABLED, - Settings.System.HAPTIC_FEEDBACK_ENABLED, - Settings.System.POWER_SOUNDS_ENABLED, // moved to global - Settings.System.DOCK_SOUNDS_ENABLED, // moved to global - Settings.System.LOCKSCREEN_SOUNDS_ENABLED, - Settings.System.SHOW_WEB_SUGGESTIONS, - Settings.System.SIP_CALL_OPTIONS, - Settings.System.SIP_RECEIVE_CALLS, - Settings.System.POINTER_SPEED, - Settings.System.VIBRATE_ON, - Settings.System.VIBRATE_WHEN_RINGING, - Settings.System.RINGTONE, - Settings.System.LOCK_TO_APP_ENABLED, - Settings.System.NOTIFICATION_SOUND, - Settings.System.ACCELEROMETER_ROTATION, - Settings.System.SHOW_BATTERY_PERCENT, - Settings.System.ALARM_VIBRATION_INTENSITY, - Settings.System.MEDIA_VIBRATION_INTENSITY, - Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Settings.System.RING_VIBRATION_INTENSITY, - Settings.System.HAPTIC_FEEDBACK_INTENSITY, - Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, - Settings.System.KEYBOARD_VIBRATION_ENABLED, - Settings.System.HAPTIC_FEEDBACK_ENABLED, - Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE - Settings.System.DISPLAY_COLOR_MODE, - Settings.System.ALARM_ALERT, - Settings.System.NOTIFICATION_LIGHT_PULSE, - Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, - Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF, - Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR, - Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS, - Settings.System.LOCALE_PREFERENCES, - Settings.System.TOUCHPAD_POINTER_SPEED, - Settings.System.TOUCHPAD_NATURAL_SCROLLING, - Settings.System.TOUCHPAD_TAP_TO_CLICK, - Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, - Settings.System.CAMERA_FLASH_NOTIFICATION, - Settings.System.SCREEN_FLASH_NOTIFICATION, - Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, - Settings.System.PEAK_REFRESH_RATE, - Settings.System.MIN_REFRESH_RATE, - Settings.System.NOTIFICATION_COOLDOWN_ENABLED, - Settings.System.NOTIFICATION_COOLDOWN_ALL, - Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, - }; + public static final String[] SETTINGS_TO_BACKUP = getSettingsToBackUp(); + + private static String[] getSettingsToBackUp() { + List<String> settings = new ArrayList<>(List.of( + Settings.System.STAY_ON_WHILE_PLUGGED_IN, // moved to global + Settings.System.WIFI_USE_STATIC_IP, + Settings.System.WIFI_STATIC_IP, + Settings.System.WIFI_STATIC_GATEWAY, + Settings.System.WIFI_STATIC_NETMASK, + Settings.System.WIFI_STATIC_DNS1, + Settings.System.WIFI_STATIC_DNS2, + Settings.System.BLUETOOTH_DISCOVERABILITY, + Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT, + Settings.System.FONT_SCALE, + Settings.System.DIM_SCREEN, + Settings.System.SCREEN_OFF_TIMEOUT, + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.ADAPTIVE_SLEEP, // moved to secure + Settings.System.APPLY_RAMPING_RINGER, + Settings.System.VIBRATE_INPUT_DEVICES, + Settings.System.MODE_RINGER_STREAMS_AFFECTED, + Settings.System.TEXT_AUTO_REPLACE, + Settings.System.TEXT_AUTO_CAPS, + Settings.System.TEXT_AUTO_PUNCTUATE, + Settings.System.TEXT_SHOW_PASSWORD, + Settings.System.AUTO_TIME, // moved to global + Settings.System.AUTO_TIME_ZONE, // moved to global + Settings.System.TIME_12_24, + Settings.System.DTMF_TONE_WHEN_DIALING, + Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, + Settings.System.HEARING_AID, + Settings.System.TTY_MODE, + Settings.System.MASTER_MONO, + Settings.System.MASTER_BALANCE, + Settings.System.FOLD_LOCK_BEHAVIOR, + Settings.System.SOUND_EFFECTS_ENABLED, + Settings.System.HAPTIC_FEEDBACK_ENABLED, + Settings.System.POWER_SOUNDS_ENABLED, // moved to global + Settings.System.DOCK_SOUNDS_ENABLED, // moved to global + Settings.System.LOCKSCREEN_SOUNDS_ENABLED, + Settings.System.SHOW_WEB_SUGGESTIONS, + Settings.System.SIP_CALL_OPTIONS, + Settings.System.SIP_RECEIVE_CALLS, + Settings.System.POINTER_SPEED, + Settings.System.VIBRATE_ON, + Settings.System.VIBRATE_WHEN_RINGING, + Settings.System.RINGTONE, + Settings.System.LOCK_TO_APP_ENABLED, + Settings.System.NOTIFICATION_SOUND, + Settings.System.ACCELEROMETER_ROTATION, + Settings.System.SHOW_BATTERY_PERCENT, + Settings.System.ALARM_VIBRATION_INTENSITY, + Settings.System.MEDIA_VIBRATION_INTENSITY, + Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Settings.System.RING_VIBRATION_INTENSITY, + Settings.System.HAPTIC_FEEDBACK_INTENSITY, + Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, + Settings.System.KEYBOARD_VIBRATION_ENABLED, + Settings.System.HAPTIC_FEEDBACK_ENABLED, + Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE + Settings.System.DISPLAY_COLOR_MODE, + Settings.System.ALARM_ALERT, + Settings.System.NOTIFICATION_LIGHT_PULSE, + Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, + Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF, + Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR, + Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS, + Settings.System.LOCALE_PREFERENCES, + Settings.System.TOUCHPAD_POINTER_SPEED, + Settings.System.TOUCHPAD_NATURAL_SCROLLING, + Settings.System.TOUCHPAD_TAP_TO_CLICK, + Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, + Settings.System.CAMERA_FLASH_NOTIFICATION, + Settings.System.SCREEN_FLASH_NOTIFICATION, + Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, + Settings.System.NOTIFICATION_COOLDOWN_ENABLED, + Settings.System.NOTIFICATION_COOLDOWN_ALL, + Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED + )); + if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) { + settings.add(Settings.System.PEAK_REFRESH_RATE); + settings.add(Settings.System.MIN_REFRESH_RATE); + } + return settings.toArray(new String[0]); + } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index f8bdcf65c45d..502239513002 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -210,6 +210,8 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR); VALIDATORS.put(Global.STYLUS_EVER_USED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE, BOOLEAN_VALIDATOR); + VALIDATORS.put( + Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 6ad10cc8cc5b..1481d97a04fa 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -33,6 +33,8 @@ import android.provider.settings.backup.SystemSettings; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.display.feature.flags.Flags; + import org.junit.Test; import org.junit.runner.RunWith; @@ -53,59 +55,6 @@ public class SettingsBackupTest { public static final String HYBRID_SYSUI_BATTERY_WARNING_FLAGS = "hybrid_sysui_battery_warning_flags"; - /** - * The following denylists contain settings that should *not* be backed up and restored to - * another device. As a general rule, anything that is not user configurable should be - * denied (and conversely, things that *are* user configurable *should* be backed up) - */ - private static final Set<String> BACKUP_DENY_LIST_SYSTEM_SETTINGS = - newHashSet( - Settings.System.ADVANCED_SETTINGS, // candidate for backup? - Settings.System.ALARM_ALERT_CACHE, // internal cache - Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2 - Settings.System.EGG_MODE, // I am the lolrus - Settings.System.END_BUTTON_BEHAVIOR, // bug? - Settings.System - .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, // candidate for backup? - Settings.System.LOCKSCREEN_DISABLED, // ? - Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup? - Settings.System.MUTE_STREAMS_AFFECTED, // candidate for backup? - Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache - Settings.System.POINTER_LOCATION, // backup candidate? - Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, // used for testing only - Settings.System.RINGTONE_CACHE, // internal cache - Settings.System.SCREEN_BRIGHTNESS, // removed in P - Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW - Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup? - Settings.System.SHOW_TOUCHES, - Settings.System.SHOW_KEY_PRESSES, - Settings.System.SHOW_ROTARY_INPUT, - Settings.System.SIP_ADDRESS_ONLY, // value, not a setting - Settings.System.SIP_ALWAYS, // value, not a setting - Settings.System.SYSTEM_LOCALES, // bug? - Settings.System.USER_ROTATION, // backup candidate? - Settings.System.VIBRATE_IN_SILENT, // deprecated? - Settings.System.VOLUME_ACCESSIBILITY, // used internally, changing value will - // not change volume - Settings.System.VOLUME_ALARM, // deprecated since API 2? - Settings.System.VOLUME_ASSISTANT, // candidate for backup? - Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2? - Settings.System.VOLUME_MASTER, // candidate for backup? - Settings.System.VOLUME_MUSIC, // deprecated since API 2? - Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2? - Settings.System.VOLUME_RING, // deprecated since API 2? - Settings.System.VOLUME_SYSTEM, // deprecated since API 2? - Settings.System.VOLUME_VOICE, // deprecated since API 2? - Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug? - Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only - Settings.System.SCREEN_BRIGHTNESS_FLOAT, - Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, - Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, - Settings.System.WEAR_TTS_PREWARM_ENABLED, - Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, - Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific - ); - private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS = newHashSet( Settings.Global.ACTIVITY_MANAGER_CONSTANTS, @@ -737,6 +686,7 @@ public class SettingsBackupTest { Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, Settings.Secure.CONTENT_CAPTURE_ENABLED, Settings.Secure.DEFAULT_INPUT_METHOD, + Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, Settings.Secure.DEVICE_PAIRED, Settings.Secure.DIALER_DEFAULT_APPLICATION, Settings.Secure.DISABLED_PRINT_SERVICES, @@ -862,7 +812,7 @@ public class SettingsBackupTest { checkSettingsBackedUpOrDenied( getCandidateSettings(Settings.System.class), newHashSet(SystemSettings.SETTINGS_TO_BACKUP), - BACKUP_DENY_LIST_SYSTEM_SETTINGS); + getBackUpDenyListSystemSettings()); } @Test @@ -937,6 +887,69 @@ public class SettingsBackupTest { checkSettingsBackedUpOrDenied(allSettings, keys, BACKUP_DENY_LIST_SECURE_SETTINGS); } + /** + * The following denylists contain settings that should *not* be backed up and restored to + * another device. As a general rule, anything that is not user configurable should be + * denied (and conversely, things that *are* user configurable *should* be backed up) + */ + private static Set<String> getBackUpDenyListSystemSettings() { + Set<String> settings = + newHashSet( + Settings.System.ADVANCED_SETTINGS, // candidate for backup? + Settings.System.ALARM_ALERT_CACHE, // internal cache + Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2 + Settings.System.EGG_MODE, // I am the lolrus + Settings.System.END_BUTTON_BEHAVIOR, // bug? + Settings.System + .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, + // candidate for backup? + Settings.System.LOCKSCREEN_DISABLED, // ? + Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup? + Settings.System.MUTE_STREAMS_AFFECTED, // candidate for backup? + Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache + Settings.System.POINTER_LOCATION, // backup candidate? + Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, + // used for testing only + Settings.System.RINGTONE_CACHE, // internal cache + Settings.System.SCREEN_BRIGHTNESS, // removed in P + Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW + Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup? + Settings.System.SHOW_TOUCHES, + Settings.System.SHOW_KEY_PRESSES, + Settings.System.SHOW_ROTARY_INPUT, + Settings.System.SIP_ADDRESS_ONLY, // value, not a setting + Settings.System.SIP_ALWAYS, // value, not a setting + Settings.System.SYSTEM_LOCALES, // bug? + Settings.System.USER_ROTATION, // backup candidate? + Settings.System.VIBRATE_IN_SILENT, // deprecated? + Settings.System.VOLUME_ACCESSIBILITY, + // used internally, changing value will + // not change volume + Settings.System.VOLUME_ALARM, // deprecated since API 2? + Settings.System.VOLUME_ASSISTANT, // candidate for backup? + Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2? + Settings.System.VOLUME_MASTER, // candidate for backup? + Settings.System.VOLUME_MUSIC, // deprecated since API 2? + Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2? + Settings.System.VOLUME_RING, // deprecated since API 2? + Settings.System.VOLUME_SYSTEM, // deprecated since API 2? + Settings.System.VOLUME_VOICE, // deprecated since API 2? + Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug? + Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only + Settings.System.SCREEN_BRIGHTNESS_FLOAT, + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, + Settings.System.WEAR_TTS_PREWARM_ENABLED, + Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, + Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific + ); + if (!Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) { + settings.add(Settings.System.MIN_REFRESH_RATE); + settings.add(Settings.System.PEAK_REFRESH_RATE); + } + return settings; + } + private static void checkSettingsBackedUpOrDenied( Set<String> settings, Set<String> settingsToBackup, Set<String> denylist) { Set<String> settingsNotBackedUp = difference(settings, settingsToBackup); diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig new file mode 100644 index 000000000000..1ad16667f317 --- /dev/null +++ b/packages/SystemUI/aconfig/predictive_back.aconfig @@ -0,0 +1,22 @@ +package: "com.android.systemui" + +flag { + name: "predictive_back_sysui" + namespace: "systemui" + description: "Predictive Back Dispatching for SysUI" + bug: "309545085" +} + +flag { + name: "predictive_back_animate_shade" + namespace: "systemui" + description: "Enable Shade Animations" + bug: "309545085" +} + +flag { + name: "predictive_back_animate_bouncer" + namespace: "systemui" + description: "Enable Predictive Back Animation in Bouncer" + bug: "309545085" +}
\ No newline at end of file diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index aa0903cab7aa..5b218549de43 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -165,7 +165,7 @@ flag { name: "qs_new_tiles" namespace: "systemui" description: "Use the new tiles in the Quick Settings. Should have no behavior changes." - bug: "241772429" + bug: "311147395" } flag { @@ -308,6 +308,13 @@ flag { } flag { + name: "run_fingerprint_detect_on_dismissible_keyguard" + namespace: "systemui" + description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible." + bug: "311145851" +} + +flag { name: "bluetooth_qs_tile_dialog_auto_on_toggle" namespace: "systemui" description: "Displays the auto on toggle in the bluetooth QS tile dialog" diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt index 23fcb691ddb4..867bbb7d74eb 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt @@ -65,12 +65,33 @@ object ShaderUtilLibrary { return dest; } - // Return range [-1, 1]. + // Integer mod. GLSL es 1.0 doesn't have integer mod :( + int imod(int a, int b) { + return a - (b * (a / b)); + } + + ivec3 imod(ivec3 a, int b) { + return ivec3(imod(a.x, b), imod(a.y, b), imod(a.z, b)); + } + + // Integer based hash function with the return range of [-1, 1]. vec3 hash(vec3 p) { - p = fract(p * vec3(.3456, .1234, .9876)); - p += dot(p, p.yxz + 43.21); - p = (p.xxy + p.yxx) * p.zyx; - return (fract(sin(p) * 4567.1234567) - .5) * 2.; + ivec3 v = ivec3(p); + v = v * 1671731 + 10139267; + + v.x += v.y * v.z; + v.y += v.z * v.x; + v.z += v.x * v.y; + + ivec3 v2 = v / 65536; // v >> 16 + v = imod((10 - imod((v + v2), 10)), 10); // v ^ v2 + + v.x += v.y * v.z; + v.y += v.z * v.x; + v.z += v.x * v.y; + + // Use sin and cos to map the range to [-1, 1]. + return vec3(sin(float(v.x)), cos(float(v.y)), sin(float(v.z))); } // Skew factors (non-uniform). diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 1fc2843b1cb8..c8461d2c5415 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -45,10 +45,10 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants import com.android.systemui.classifier.FalsingA11yDelegate import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.log.SessionTracker @@ -140,7 +140,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @Mock private lateinit var viewMediatorCallback: ViewMediatorCallback @Mock private lateinit var audioManager: AudioManager @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor - @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor @Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController @Mock private lateinit var postureController: DevicePostureController diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index e5da1f86a841..36aa4416f292 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -85,10 +85,10 @@ import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayView import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.FalsingManager; @@ -336,7 +336,7 @@ public class UdfpsControllerTest extends SysuiTestCase { mSessionTracker, mAlternateBouncerInteractor, mInputManager, - mock(KeyguardFaceAuthInteractor.class), + mock(DeviceEntryFaceAuthInteractor.class), mUdfpsKeyguardAccessibilityDelegate, mSelectedUserInteractor, mFpsUnlockTracker, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt index 1f8854ffc1c8..335ac9d42e77 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt @@ -32,11 +32,11 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState @@ -111,7 +111,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : FakeTrustRepository(), testScope.backgroundScope, mSelectedUserInteractor, - mock(KeyguardFaceAuthInteractor::class.java), + mock(DeviceEntryFaceAuthInteractor::class.java), ) mAlternateBouncerInteractor = AlternateBouncerInteractor( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index 99c18744f9b4..93ba6a48f3dd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -25,7 +25,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat @@ -46,7 +46,7 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) class BouncerInteractorTest : SysuiTestCase() { - @Mock private lateinit var keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var mDeviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor private val utils = SceneTestUtils(this) private val testScope = utils.testScope @@ -67,7 +67,7 @@ class BouncerInteractorTest : SysuiTestCase() { underTest = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, - keyguardFaceAuthInteractor = keyguardFaceAuthInteractor, + deviceEntryFaceAuthInteractor = mDeviceEntryFaceAuthInteractor, ) } @@ -306,7 +306,7 @@ class BouncerInteractorTest : SysuiTestCase() { fun intentionalUserInputEvent_notifiesFaceAuthInteractor() = testScope.runTest { underTest.onIntentionalUserInput() - verify(keyguardFaceAuthInteractor).onPrimaryBouncerUserInput() + verify(mDeviceEntryFaceAuthInteractor).onPrimaryBouncerUserInput() } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt index bdf5041f8a38..c8560c31cdf1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt @@ -26,9 +26,9 @@ import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepositor import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.TrustRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.utils.os.FakeHandler @@ -54,7 +54,7 @@ class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() { @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor - @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor private val mainHandler = FakeHandler(Looper.getMainLooper()) private lateinit var underTest: PrimaryBouncerInteractor diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt index a3bf3f492e6e..a0c2acc31589 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt @@ -30,9 +30,9 @@ import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.TrustRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.domain.interactor.SelectedUserInteractor @@ -63,7 +63,7 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() { @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor lateinit var bouncerInteractor: PrimaryBouncerInteractor private val mainHandler = FakeHandler(Looper.getMainLooper()) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 941d67f0a34e..6a14220e6a42 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.data.repository +package com.android.systemui.deviceentry.data.repository import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP import android.app.StatusBarManager.SESSION_KEYGUARD @@ -37,10 +37,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId.fakeInstanceId import com.android.internal.logging.UiEventLogger -import com.android.keyguard.FaceAuthUiEvent -import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN -import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED -import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository @@ -53,21 +49,32 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER +import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus +import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus import com.android.systemui.display.data.repository.FakeDisplayRepository import com.android.systemui.display.data.repository.display import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags.KEYGUARD_WM_STATE_REFACTOR +import com.android.systemui.keyguard.data.repository.BiometricType +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.FakeCommandQueue +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory -import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FaceDetectionStatus -import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.log.FaceAuthenticationLogger diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 2b4117866254..3a26ebff6c6a 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -66,6 +66,7 @@ android_library { "kotlinx_coroutines", "dagger2", "jsr330", + "com_android_systemui_shared_flags_lib", ], resource_dirs: [ "res", diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 131eb6b63df3..c08b0837da8e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -20,10 +20,11 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; +import static com.android.systemui.shared.Flags.shadeAllowBackGesture; + import android.annotation.IntDef; import android.content.Context; import android.content.res.Resources; -import android.os.SystemProperties; import android.view.ViewConfiguration; import android.view.WindowManagerPolicyConstants; @@ -132,8 +133,7 @@ public class QuickStepContract { SYSUI_STATE_WAKEFULNESS_TRANSITION | SYSUI_STATE_AWAKE; // Whether the back gesture is allowed (or ignored) by the Shade - public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean( - "persist.wm.debug.shade_allow_back_gesture", false); + public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = shadeAllowBackGesture(); @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 5e35e7764dd8..b6b8c2afc463 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -78,10 +78,10 @@ import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; @@ -135,7 +135,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final SessionTracker mSessionTracker; private final Optional<SideFpsController> mSideFpsController; private final FalsingA11yDelegate mFalsingA11yDelegate; - private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; + private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor; private final BouncerMessageInteractor mBouncerMessageInteractor; private int mTranslationY; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; @@ -216,7 +216,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard @Override public void onUserInput() { mBouncerMessageInteractor.onPrimaryBouncerUserInput(); - mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput(); + mDeviceEntryFaceAuthInteractor.onPrimaryBouncerUserInput(); } @Override @@ -347,11 +347,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final SwipeListener mSwipeListener = new SwipeListener() { @Override public void onSwipeUp() { - if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) { + if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) { mKeyguardSecurityCallback.userActivity(); } - mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer(); - if (mKeyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) { + mDeviceEntryFaceAuthInteractor.onSwipeUpOnBouncer(); + if (mDeviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) { mUpdateMonitor.requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT, "swipeUpOnBouncer"); @@ -456,7 +456,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard TelephonyManager telephonyManager, ViewMediatorCallback viewMediatorCallback, AudioManager audioManager, - KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, + DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor, BouncerMessageInteractor bouncerMessageInteractor, Provider<JavaAdapter> javaAdapter, SelectedUserInteractor selectedUserInteractor, @@ -495,7 +495,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mTelephonyManager = telephonyManager; mViewMediatorCallback = viewMediatorCallback; mAudioManager = audioManager; - mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; + mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor; mBouncerMessageInteractor = bouncerMessageInteractor; mSelectedUserInteractor = selectedUserInteractor; mDeviceEntryInteractor = deviceEntryInteractor; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 4d84d0b3b666..e59868e14d26 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -113,24 +113,26 @@ import com.android.settingslib.WirelessUtils; import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.CoreStartable; import com.android.systemui.Dumpable; +import com.android.systemui.Flags; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; +import com.android.systemui.deviceentry.domain.interactor.FaceAuthenticationListener; +import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus; +import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus; +import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus; +import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus; +import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus; +import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus; +import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; -import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent; -import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus; -import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus; -import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus; -import com.android.systemui.keyguard.shared.model.FaceDetectionStatus; -import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus; -import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus; -import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.clocks.WeatherData; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -359,7 +361,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Nullable private final BiometricManager mBiometricManager; @Nullable - private KeyguardFaceAuthInteractor mFaceAuthInteractor; + private DeviceEntryFaceAuthInteractor mFaceAuthInteractor; private final DevicePostureController mDevicePostureController; private final TaskStackChangeListeners mTaskStackChangeListeners; private final IActivityTaskManager mActivityTaskManager; @@ -373,6 +375,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private List<SubscriptionInfo> mSubscriptionInfo; @VisibleForTesting protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; + private boolean mFingerprintDetectRunning; private boolean mIsDreaming; private boolean mLogoutEnabled; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -986,6 +989,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean wasCancellingRestarting = mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING; mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; + mFingerprintDetectRunning = false; if (wasCancellingRestarting) { KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } else { @@ -1094,6 +1098,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING; boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING; mFingerprintRunningState = fingerprintRunningState; + if (mFingerprintRunningState == BIOMETRIC_STATE_STOPPED) { + mFingerprintDetectRunning = false; + } mLogger.logFingerprintRunningState(mFingerprintRunningState); // Clients of KeyguardUpdateMonitor don't care about the internal state about the // asynchronousness of the cancel cycle. So only notify them if the actually running state @@ -1266,14 +1273,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return getFaceAuthInteractor() != null && getFaceAuthInteractor().isRunning(); } - private @Nullable KeyguardFaceAuthInteractor getFaceAuthInteractor() { + private @Nullable DeviceEntryFaceAuthInteractor getFaceAuthInteractor() { return mFaceAuthInteractor; } /** * Set the face auth interactor that should be used for initiating face authentication. */ - public void setFaceAuthInteractor(KeyguardFaceAuthInteractor faceAuthInteractor) { + public void setFaceAuthInteractor(DeviceEntryFaceAuthInteractor faceAuthInteractor) { if (mFaceAuthInteractor != null) { mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener); } @@ -1356,7 +1363,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * @return whether the current user has been authenticated with face. This may be true * on the lockscreen if the user doesn't have bypass enabled. * - * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()} + * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isAuthenticated()} */ @Deprecated public boolean getIsFaceAuthenticated() { @@ -1387,7 +1394,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab /** * Returns whether the user is unlocked with face. - * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()} instead + * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isAuthenticated()} instead */ @Deprecated public boolean isCurrentUserUnlockedWithFace() { @@ -2066,6 +2073,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting void resetBiometricListeningState() { mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; + mFingerprintDetectRunning = false; } @VisibleForTesting @@ -2457,7 +2465,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab /** * @return true if there's at least one face enrolled - * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()} + * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()} */ @Deprecated public boolean isFaceEnabledAndEnrolled() { @@ -2504,8 +2512,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return; } final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported()); - final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING + final boolean running = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING; + final boolean runningOrRestarting = running || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING; + final boolean runDetect = shouldRunFingerprintDetect(); if (runningOrRestarting && !shouldListenForFingerprint) { if (action == BIOMETRIC_ACTION_START) { mLogger.v("Ignoring stopListeningForFingerprint()"); @@ -2517,10 +2527,24 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLogger.v("Ignoring startListeningForFingerprint()"); return; } - startListeningForFingerprint(); + startListeningForFingerprint(runDetect); + } else if (running && runDetect && !mFingerprintDetectRunning) { + if (action == BIOMETRIC_ACTION_STOP) { + mLogger.v("Ignoring startListeningForFingerprint(detect)"); + return; + } + // stop running authentication and start running fingerprint detection + stopListeningForFingerprint(); + startListeningForFingerprint(true); } } + private boolean shouldRunFingerprintDetect() { + return !isUnlockingWithFingerprintAllowed() + || (Flags.runFingerprintDetectOnDismissibleKeyguard() + && getUserCanSkipBouncer(mSelectedUserInteractor.getSelectedUserId())); + } + /** * If a user is encrypted or not. * This is NOT related to the lock screen being visible or not. @@ -2776,7 +2800,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && biometricEnabledForUser && !isUserInLockdown(user); final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed(); - final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled(); final boolean shouldListenBouncerState = !strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing; @@ -2822,7 +2845,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab /** * If face auth is allows to scan on this exact moment. * - * @deprecated Use {@link KeyguardFaceAuthInteractor#canFaceAuthRun()} + * @deprecated Use {@link DeviceEntryFaceAuthInteractor#canFaceAuthRun()} */ @Deprecated public boolean shouldListenForFace() { @@ -2839,7 +2862,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private void startListeningForFingerprint() { + private void startListeningForFingerprint(boolean runDetect) { final int userId = mSelectedUserInteractor.getSelectedUserId(); final boolean unlockPossible = isUnlockWithFingerprintPossible(userId); if (mFingerprintCancelSignal != null) { @@ -2869,18 +2892,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mFingerprintInteractiveToAuthProvider.getVendorExtension(userId)); } - if (!isUnlockingWithFingerprintAllowed()) { + if (runDetect) { mLogger.v("startListeningForFingerprint - detect"); mFpm.detectFingerprint( mFingerprintCancelSignal, mFingerprintDetectionCallback, fingerprintAuthenticateOptions); + mFingerprintDetectRunning = true; } else { mLogger.v("startListeningForFingerprint"); mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal, mFingerprintAuthenticationCallback, null /* handler */, fingerprintAuthenticateOptions); + mFingerprintDetectRunning = false; } setFingerprintRunningState(BIOMETRIC_STATE_RUNNING); } @@ -2891,7 +2916,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** - * @deprecated Use {@link KeyguardFaceAuthInteractor#isLockedOut()} + * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isLockedOut()} */ @Deprecated public boolean isFaceLockedOut() { @@ -2932,7 +2957,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** - * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()} + * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()} */ @VisibleForTesting @Deprecated @@ -3876,6 +3901,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mSelectedUserInteractor.getSelectedUserId())); pw.println(" getUserUnlockedWithBiometric()=" + getUserUnlockedWithBiometric(mSelectedUserInteractor.getSelectedUserId())); + pw.println(" mFingerprintDetectRunning=" + mFingerprintDetectRunning); pw.println(" SIM States:"); for (SimData data : mSimDatas.values()) { pw.println(" " + data.toString()); diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt index 066cba230b76..6076f32486b9 100644 --- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt @@ -22,10 +22,9 @@ import android.window.OnBackInvokedCallback import android.window.OnBackInvokedDispatcher import android.window.WindowOnBackInvokedDispatcher import com.android.systemui.CoreStartable +import com.android.systemui.Flags.predictiveBackAnimateShade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor import com.android.systemui.shade.QuickSettingsController @@ -48,14 +47,13 @@ constructor( private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, private val shadeController: ShadeController, private val notificationShadeWindowController: NotificationShadeWindowController, - private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor, - featureFlags: FeatureFlags, + private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor ) : CoreStartable { private var isCallbackRegistered = false private val callback = - if (featureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE)) { + if (predictiveBackAnimateShade()) { /** * New callback that handles back gesture invoked, cancel, progress and provides * feedback via Shade animation. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 634531215002..45967c600a3c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -32,13 +32,13 @@ import com.android.keyguard.logging.KeyguardLogger import com.android.settingslib.Utils import com.android.systemui.CoreStartable import com.android.systemui.Flags.lightRevealMigration -import com.android.systemui.res.R import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlags +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.log.core.LogLevel import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LiftReveal import com.android.systemui.statusbar.LightRevealEffect @@ -50,6 +50,7 @@ import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.ViewController +import kotlinx.coroutines.ExperimentalCoroutinesApi import java.io.PrintWriter import javax.inject.Inject import javax.inject.Provider @@ -62,6 +63,7 @@ import javax.inject.Provider * * The ripple uses the accent color of the current theme. */ +@ExperimentalCoroutinesApi @SysUISingleton class AuthRippleController @Inject constructor( private val sysuiContext: Context, @@ -75,7 +77,6 @@ class AuthRippleController @Inject constructor( private val udfpsControllerProvider: Provider<UdfpsController>, private val statusBarStateController: StatusBarStateController, private val displayMetrics: DisplayMetrics, - private val featureFlags: FeatureFlags, private val logger: KeyguardLogger, private val biometricUnlockController: BiometricUnlockController, private val lightRevealScrim: LightRevealScrim, @@ -313,6 +314,18 @@ class AuthRippleController @Inject constructor( mView.fadeDwellRipple() } } + + override fun onBiometricDetected( + userId: Int, + biometricSourceType: BiometricSourceType, + isStrongBiometric: Boolean + ) { + // TODO (b/309804148): add support detect auth ripple for deviceEntryUdfpsRefactor + if (!DeviceEntryUdfpsRefactor.isEnabled && + keyguardUpdateMonitor.getUserCanSkipBouncer(userId)) { + showUnlockRipple(biometricSourceType) + } + } } private val configurationChangedListener = diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt index cb750493ba26..3ea1632b20d7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt @@ -22,7 +22,7 @@ import android.view.View import android.view.accessibility.AccessibilityNodeInfo import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.res.R import javax.inject.Inject @@ -35,7 +35,7 @@ class FaceAuthAccessibilityDelegate @Inject constructor( @Main private val resources: Resources, - private val faceAuthInteractor: KeyguardFaceAuthInteractor, + private val faceAuthInteractor: DeviceEntryFaceAuthInteractor, ) : View.AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) { super.onInitializeAccessibilityNodeInfo(host, info) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 2fd13b349438..d6646378681a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -80,11 +80,11 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.FalsingManager; @@ -149,7 +149,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull private final DumpManager mDumpManager; @NonNull private final SystemUIDialogManager mDialogManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; + @NonNull private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor; @NonNull private final VibratorHelper mVibrator; @NonNull private final FalsingManager mFalsingManager; @NonNull private final PowerManager mPowerManager; @@ -664,7 +664,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull SessionTracker sessionTracker, @NonNull AlternateBouncerInteractor alternateBouncerInteractor, @NonNull InputManager inputManager, - @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, + @NonNull DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor, @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate, @NonNull SelectedUserInteractor selectedUserInteractor, @NonNull FpsUnlockTracker fpsUnlockTracker, @@ -736,7 +736,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } return Unit.INSTANCE; }); - mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; + mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor; final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController(); mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController); @@ -1027,7 +1027,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mOnFingerDown) { playStartHaptic(); - mKeyguardFaceAuthInteractor.onUdfpsSensorTouched(); + mDeviceEntryFaceAuthInteractor.onUdfpsSensorTouched(); } mOnFingerDown = true; mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y, diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 81d822fa7b03..c8ce245e48bf 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -29,7 +29,7 @@ import com.android.systemui.classifier.FalsingClassifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.res.R import javax.inject.Inject @@ -52,7 +52,7 @@ constructor( @Application private val applicationContext: Context, private val repository: BouncerRepository, private val authenticationInteractor: AuthenticationInteractor, - private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor, + private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, private val falsingInteractor: FalsingInteractor, private val powerInteractor: PowerInteractor, private val simBouncerInteractor: SimBouncerInteractor, @@ -100,7 +100,7 @@ constructor( * user's pocket or by the user's face while holding their device up to their ear. */ fun onIntentionalUserInput() { - keyguardFaceAuthInteractor.onPrimaryBouncerUserInput() + deviceEntryFaceAuthInteractor.onPrimaryBouncerUserInput() powerInteractor.onUserTouch() falsingInteractor.updateFalseConfidence(FalsingClassifier.Result.passed(0.6)) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt index b587ed846a38..ef4554cfdd7c 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt @@ -30,9 +30,9 @@ import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.bouncer.shared.model.Message import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.flags.SystemPropertiesHelper import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository -import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.res.R.string.bouncer_face_not_recognized diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt index 621ca5dc6f5a..654fa220d491 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt @@ -38,9 +38,9 @@ import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.TrustRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.shared.system.SysUiStatsLog @@ -77,7 +77,7 @@ constructor( private val trustRepository: TrustRepository, @Application private val applicationScope: CoroutineScope, private val selectedUserInteractor: SelectedUserInteractor, - private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor, + private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, ) { private val passiveAuthBouncerDelay = context.resources.getInteger(R.integer.primary_bouncer_passive_auth_delay).toLong() @@ -429,7 +429,7 @@ constructor( keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState() return !needsFullscreenBouncer() && - (keyguardFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock) + (deviceEntryFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt index b91541813d62..71b5ab2d61ed 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt @@ -1,6 +1,7 @@ package com.android.systemui.deviceentry import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule +import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import dagger.Module import dagger.multibindings.Multibinds @@ -9,6 +10,7 @@ import dagger.multibindings.Multibinds includes = [ DeviceEntryRepositoryModule::class, + FaceWakeUpTriggersConfigModule::class, ], ) abstract class DeviceEntryModule { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt index 17d78365417b..7a70c4ac9fab 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.data.repository +package com.android.systemui.deviceentry.data.repository import android.app.StatusBarManager import android.content.Context @@ -22,7 +22,6 @@ import android.hardware.face.FaceManager import android.os.CancellationSignal import com.android.internal.logging.InstanceId import com.android.internal.logging.UiEventLogger -import com.android.keyguard.FaceAuthUiEvent import com.android.systemui.Dumpable import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor @@ -32,19 +31,27 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent +import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus +import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.BiometricType +import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.FaceAuthTableLog +import com.android.systemui.keyguard.data.repository.FaceDetectTableLog +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FaceDetectionStatus -import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.log.FaceAuthenticationLogger diff --git a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt index 84a6b098f239..9d6718be96f6 100644 --- a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,28 +14,35 @@ * limitations under the License. */ -package com.android.keyguard +package com.android.systemui.deviceentry.data.repository import android.content.res.Resources import android.os.Build import android.os.PowerManager import com.android.systemui.Dumpable -import com.android.systemui.res.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.res.R import com.android.systemui.util.settings.GlobalSettings +import dagger.Binds +import dagger.Module import java.io.PrintWriter import java.util.stream.Collectors import javax.inject.Inject /** Determines which device wake-ups should trigger passive authentication. */ +interface FaceWakeUpTriggersConfig { + fun shouldTriggerFaceAuthOnWakeUpFrom(@PowerManager.WakeReason pmWakeReason: Int): Boolean + fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean +} + @SysUISingleton -class FaceWakeUpTriggersConfig +class FaceWakeUpTriggersConfigImpl @Inject constructor(@Main resources: Resources, globalSettings: GlobalSettings, dumpManager: DumpManager) : - Dumpable { + Dumpable, FaceWakeUpTriggersConfig { private val defaultTriggerFaceAuthOnWakeUpFrom: Set<Int> = resources.getIntArray(R.array.config_face_auth_wake_up_triggers).toSet() private val triggerFaceAuthOnWakeUpFrom: Set<Int> @@ -65,11 +72,13 @@ constructor(@Main resources: Resources, globalSettings: GlobalSettings, dumpMana dumpManager.registerDumpable(this) } - fun shouldTriggerFaceAuthOnWakeUpFrom(@PowerManager.WakeReason pmWakeReason: Int): Boolean { + override fun shouldTriggerFaceAuthOnWakeUpFrom( + @PowerManager.WakeReason pmWakeReason: Int + ): Boolean { return triggerFaceAuthOnWakeUpFrom.contains(pmWakeReason) } - fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean = + override fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean = wakeSleepReasonsToTriggerFaceAuth.contains(wakeReason) override fun dump(pw: PrintWriter, args: Array<out String>) { @@ -87,3 +96,8 @@ constructor(@Main resources: Resources, globalSettings: GlobalSettings, dumpMana ?: default } } + +@Module +interface FaceWakeUpTriggersConfigModule { + @Binds fun repository(impl: FaceWakeUpTriggersConfigImpl): FaceWakeUpTriggersConfig +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt index f4a74f03696c..6695b182d085 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt @@ -1,25 +1,25 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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 + * 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 + * 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. + * 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.systemui.keyguard.data.repository +package com.android.systemui.deviceentry.data.repository -import com.android.keyguard.FaceAuthUiEvent import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FaceDetectionStatus +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent +import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt index 1a6bd0427db5..569917630e50 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt @@ -18,13 +18,14 @@ package com.android.systemui.deviceentry.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.shared.DeviceEntryBiometricMode +import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository -import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map @@ -62,7 +63,9 @@ constructor( val faceOnlyFaceFailure: Flow<FailedFaceAuthenticationStatus> = faceOnly.flatMapLatest { faceOnly -> if (faceOnly) { - deviceEntryFaceAuthInteractor.faceFailure + deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance< + FailedFaceAuthenticationStatus + >() } else { emptyFlow() } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt index 70716c6c91fe..99bd25bf0e52 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,80 @@ package com.android.systemui.deviceentry.domain.interactor -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository -import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus -import javax.inject.Inject +import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filterIsInstance - -@SysUISingleton -class DeviceEntryFaceAuthInteractor -@Inject -constructor( - repository: DeviceEntryFaceAuthRepository, -) { - val faceFailure: Flow<FailedFaceAuthenticationStatus> = - repository.authenticationStatus.filterIsInstance<FailedFaceAuthenticationStatus>() + +/** + * Interactor that exposes API to get the face authentication status and handle any events that can + * cause face authentication to run for device entry. + */ +interface DeviceEntryFaceAuthInteractor { + + /** Current authentication status */ + val authenticationStatus: Flow<FaceAuthenticationStatus> + + /** Current detection status */ + val detectionStatus: Flow<FaceDetectionStatus> + + /** Can face auth be run right now */ + fun canFaceAuthRun(): Boolean + + /** Whether face auth is currently running or not. */ + fun isRunning(): Boolean + + /** Whether face auth is in lock out state. */ + fun isLockedOut(): Boolean + + /** Whether face auth is enrolled and enabled for the current user */ + fun isFaceAuthEnabledAndEnrolled(): Boolean + + /** Whether the current user is authenticated successfully with face auth */ + fun isAuthenticated(): Boolean + /** + * Register listener for use from code that cannot use [authenticationStatus] or + * [detectionStatus] + */ + fun registerListener(listener: FaceAuthenticationListener) + + /** Unregister previously registered listener */ + fun unregisterListener(listener: FaceAuthenticationListener) + + fun onUdfpsSensorTouched() + fun onAssistantTriggeredOnLockScreen() + fun onDeviceLifted() + fun onQsExpansionStared() + fun onNotificationPanelClicked() + fun onSwipeUpOnBouncer() + fun onPrimaryBouncerUserInput() + fun onAccessibilityAction() + fun onWalletLaunched() + + /** Whether face auth is considered class 3 */ + fun isFaceAuthStrong(): Boolean +} + +/** + * Listener that can be registered with the [DeviceEntryFaceAuthInteractor] to receive updates about + * face authentication & detection updates. + * + * This is present to make it easier for use the new face auth API for code that cannot use + * [DeviceEntryFaceAuthInteractor.authenticationStatus] or + * [DeviceEntryFaceAuthInteractor.detectionStatus] flows. + */ +interface FaceAuthenticationListener { + /** Receive face isAuthenticated updates */ + fun onAuthenticatedChanged(isAuthenticated: Boolean) + + /** Receive face authentication status updates */ + fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus) + + /** Receive status updates whenever face detection runs */ + fun onDetectionStatusChanged(status: FaceDetectionStatus) + + fun onLockoutStateChanged(isLockedOut: Boolean) + + fun onRunningStateChanged(isRunning: Boolean) + + fun onAuthEnrollmentStateChanged(enrolled: Boolean) } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index f6a9570fc94c..2680328d5d7e 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -20,8 +20,8 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository -import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.scene.domain.interactor.SceneInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt index cd6ab31f3908..3b9416690a85 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt @@ -1,24 +1,24 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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 + * 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 + * 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. + * 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.systemui.keyguard.domain.interactor +package com.android.systemui.deviceentry.domain.interactor import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FaceDetectionStatus +import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow @@ -27,10 +27,10 @@ import kotlinx.coroutines.flow.emptyFlow * Implementation of the interactor that noops all face auth operations. * * This is required for SystemUI variants that do not support face authentication but still inject - * other SysUI components that depend on [KeyguardFaceAuthInteractor] + * other SysUI components that depend on [DeviceEntryFaceAuthInteractor] */ @SysUISingleton -class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor { +class NoopDeviceEntryFaceAuthInteractor @Inject constructor() : DeviceEntryFaceAuthInteractor { override val authenticationStatus: Flow<FaceAuthenticationStatus> get() = emptyFlow() override val detectionStatus: Flow<FaceDetectionStatus> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt index e3f47397eca3..98130eb10f3d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt @@ -14,14 +14,12 @@ * limitations under the License. */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.deviceentry.domain.interactor import android.app.trust.TrustManager import android.content.Context import android.hardware.biometrics.BiometricFaceConstants import android.hardware.biometrics.BiometricSourceType -import com.android.keyguard.FaceAuthUiEvent -import com.android.keyguard.FaceWakeUpTriggersConfig import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.CoreStartable import com.android.systemui.biometrics.data.repository.FacePropertyRepository @@ -32,11 +30,14 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent +import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository -import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.log.FaceAuthenticationLogger import com.android.systemui.power.domain.interactor.PowerInteractor @@ -65,7 +66,7 @@ import kotlinx.coroutines.yield * SystemUI Keyguard. */ @SysUISingleton -class SystemUIKeyguardFaceAuthInteractor +class SystemUIDeviceEntryFaceAuthInteractor @Inject constructor( private val context: Context, @@ -84,7 +85,7 @@ constructor( private val powerInteractor: PowerInteractor, private val biometricSettingsRepository: BiometricSettingsRepository, private val trustManager: TrustManager, -) : CoreStartable, KeyguardFaceAuthInteractor { +) : CoreStartable, DeviceEntryFaceAuthInteractor { private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf() @@ -310,7 +311,7 @@ constructor( } companion object { - const val TAG = "KeyguardFaceAuthInteractor" + const val TAG = "DeviceEntryFaceAuthInteractor" } } diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt index 2abb7a41ef08..ee220d5fb713 100644 --- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,58 +14,55 @@ * limitations under the License. */ -package com.android.keyguard +package com.android.systemui.deviceentry.shared import android.annotation.StringDef import android.os.PowerManager import com.android.internal.logging.UiEvent import com.android.internal.logging.UiEventLogger -import com.android.keyguard.FaceAuthApiRequestReason.Companion.ACCESSIBILITY_ACTION -import com.android.keyguard.FaceAuthApiRequestReason.Companion.NOTIFICATION_PANEL_CLICKED -import com.android.keyguard.FaceAuthApiRequestReason.Companion.PICK_UP_GESTURE_TRIGGERED -import com.android.keyguard.FaceAuthApiRequestReason.Companion.QS_EXPANDED -import com.android.keyguard.FaceAuthApiRequestReason.Companion.SWIPE_UP_ON_BOUNCER -import com.android.keyguard.FaceAuthApiRequestReason.Companion.UDFPS_POINTER_DOWN -import com.android.keyguard.InternalFaceAuthReasons.ALL_AUTHENTICATORS_REGISTERED -import com.android.keyguard.InternalFaceAuthReasons.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN -import com.android.keyguard.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED -import com.android.keyguard.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION -import com.android.keyguard.InternalFaceAuthReasons.BIOMETRIC_ENABLED -import com.android.keyguard.InternalFaceAuthReasons.CAMERA_LAUNCHED -import com.android.keyguard.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE -import com.android.keyguard.InternalFaceAuthReasons.DISPLAY_OFF -import com.android.keyguard.InternalFaceAuthReasons.DREAM_STARTED -import com.android.keyguard.InternalFaceAuthReasons.DREAM_STOPPED -import com.android.keyguard.InternalFaceAuthReasons.ENROLLMENTS_CHANGED -import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTHENTICATED -import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTH_STOPPED_ON_USER_INPUT -import com.android.keyguard.InternalFaceAuthReasons.FACE_CANCEL_NOT_RECEIVED -import com.android.keyguard.InternalFaceAuthReasons.FACE_LOCKOUT_RESET -import com.android.keyguard.InternalFaceAuthReasons.FINISHED_GOING_TO_SLEEP -import com.android.keyguard.InternalFaceAuthReasons.FP_AUTHENTICATED -import com.android.keyguard.InternalFaceAuthReasons.FP_LOCKED_OUT -import com.android.keyguard.InternalFaceAuthReasons.GOING_TO_SLEEP -import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_GOING_AWAY -import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_INIT -import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED -import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_RESET -import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED -import com.android.keyguard.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED -import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED -import com.android.keyguard.InternalFaceAuthReasons.POSTURE_CHANGED -import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN -import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN -import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE -import com.android.keyguard.InternalFaceAuthReasons.STARTED_WAKING_UP -import com.android.keyguard.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED -import com.android.keyguard.InternalFaceAuthReasons.TRUST_DISABLED -import com.android.keyguard.InternalFaceAuthReasons.TRUST_ENABLED -import com.android.keyguard.InternalFaceAuthReasons.USER_SWITCHING +import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.ACCESSIBILITY_ACTION +import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.NOTIFICATION_PANEL_CLICKED +import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.PICK_UP_GESTURE_TRIGGERED +import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.QS_EXPANDED +import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.SWIPE_UP_ON_BOUNCER +import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.UDFPS_POINTER_DOWN +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALL_AUTHENTICATORS_REGISTERED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.BIOMETRIC_ENABLED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_LAUNCHED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DISPLAY_OFF +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DREAM_STARTED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DREAM_STOPPED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ENROLLMENTS_CHANGED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_AUTHENTICATED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_AUTH_STOPPED_ON_USER_INPUT +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_CANCEL_NOT_RECEIVED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_LOCKOUT_RESET +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FINISHED_GOING_TO_SLEEP +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FP_AUTHENTICATED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FP_LOCKED_OUT +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.GOING_TO_SLEEP +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_GOING_AWAY +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_INIT +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_RESET +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.POSTURE_CHANGED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.STARTED_WAKING_UP +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.TRUST_DISABLED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.TRUST_ENABLED +import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.USER_SWITCHING -/** - * List of reasons why face auth is requested by clients through - * [KeyguardUpdateMonitor.requestFaceAuth]. - */ +/** List of reasons why face auth is requested by clients. */ @Retention(AnnotationRetention.SOURCE) @StringDef( SWIPE_UP_ON_BOUNCER, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt index 3de3666fdc3c..f006b3484033 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.systemui.keyguard.shared.model +package com.android.systemui.deviceentry.shared.model import android.hardware.face.FaceManager import android.os.SystemClock.elapsedRealtime /** * Authentication status provided by - * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository] + * [com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository] */ sealed class FaceAuthenticationStatus diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index bb0c2733e511..38c7c6ac67cb 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -444,29 +444,10 @@ object Flags { // TODO(b/254512728): Tracking Bug @JvmField val NEW_BACK_AFFORDANCE = releasedFlag("new_back_affordance") - // TODO(b/255854141): Tracking Bug - @JvmField - val WM_ENABLE_PREDICTIVE_BACK_SYSUI = - unreleasedFlag("persist.wm.debug.predictive_back_sysui_enable", teamfood = true) // TODO(b/270987164): Tracking Bug @JvmField val TRACKPAD_GESTURE_FEATURES = releasedFlag("trackpad_gesture_features") - // TODO(b/263826204): Tracking Bug - @JvmField - val WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM = - unreleasedFlag("persist.wm.debug.predictive_back_bouncer_anim", teamfood = true) - - // TODO(b/238475428): Tracking Bug - @JvmField - val WM_SHADE_ALLOW_BACK_GESTURE = - sysPropBooleanFlag("persist.wm.debug.shade_allow_back_gesture", default = false) - - // TODO(b/238475428): Tracking Bug - @JvmField - val WM_SHADE_ANIMATE_BACK_GESTURE = - unreleasedFlag("persist.wm.debug.shade_animate_back_gesture", teamfood = false) - // TODO(b/265639042): Tracking Bug @JvmField val WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt index d8eb81caa76c..f29b6576217c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt @@ -16,10 +16,10 @@ package com.android.systemui.keyguard.dagger -import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository -import com.android.systemui.keyguard.data.repository.NoopDeviceEntryFaceAuthRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor -import com.android.systemui.keyguard.domain.interactor.NoopKeyguardFaceAuthInteractor +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.deviceentry.data.repository.NoopDeviceEntryFaceAuthRepository +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor +import com.android.systemui.deviceentry.domain.interactor.NoopDeviceEntryFaceAuthInteractor import dagger.Binds import dagger.Module @@ -33,7 +33,9 @@ import dagger.Module @Module interface KeyguardFaceAuthNotSupportedModule { @Binds - fun keyguardFaceAuthInteractor(impl: NoopKeyguardFaceAuthInteractor): KeyguardFaceAuthInteractor + fun keyguardFaceAuthInteractor( + impl: NoopDeviceEntryFaceAuthInteractor + ): DeviceEntryFaceAuthInteractor @Binds fun deviceEntryFaceAuthRepository( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt index ee0eb2d90aa7..b373f8520254 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt @@ -19,8 +19,10 @@ package com.android.systemui.keyguard.data.repository import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor -import com.android.systemui.keyguard.domain.interactor.SystemUIKeyguardFaceAuthInteractor +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepositoryImpl +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor +import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.TableLogBufferFactory import dagger.Binds @@ -38,13 +40,13 @@ interface KeyguardFaceAuthModule { @Binds @IntoMap - @ClassKey(SystemUIKeyguardFaceAuthInteractor::class) - fun bind(impl: SystemUIKeyguardFaceAuthInteractor): CoreStartable + @ClassKey(SystemUIDeviceEntryFaceAuthInteractor::class) + fun bind(impl: SystemUIDeviceEntryFaceAuthInteractor): CoreStartable @Binds fun keyguardFaceAuthInteractor( - impl: SystemUIKeyguardFaceAuthInteractor - ): KeyguardFaceAuthInteractor + impl: SystemUIDeviceEntryFaceAuthInteractor + ): DeviceEntryFaceAuthInteractor companion object { @Provides diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt deleted file mode 100644 index 046916aeb277..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2023 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.systemui.keyguard.domain.interactor - -import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FaceDetectionStatus -import kotlinx.coroutines.flow.Flow - -/** - * Interactor that exposes API to get the face authentication status and handle any events that can - * cause face authentication to run. - */ -interface KeyguardFaceAuthInteractor { - - /** Current authentication status */ - val authenticationStatus: Flow<FaceAuthenticationStatus> - - /** Current detection status */ - val detectionStatus: Flow<FaceDetectionStatus> - - /** Can face auth be run right now */ - fun canFaceAuthRun(): Boolean - - /** Whether face auth is currently running or not. */ - fun isRunning(): Boolean - - /** Whether face auth is in lock out state. */ - fun isLockedOut(): Boolean - - /** Whether face auth is enrolled and enabled for the current user */ - fun isFaceAuthEnabledAndEnrolled(): Boolean - - /** Whether the current user is authenticated successfully with face auth */ - fun isAuthenticated(): Boolean - /** - * Register listener for use from code that cannot use [authenticationStatus] or - * [detectionStatus] - */ - fun registerListener(listener: FaceAuthenticationListener) - - /** Unregister previously registered listener */ - fun unregisterListener(listener: FaceAuthenticationListener) - - fun onUdfpsSensorTouched() - fun onAssistantTriggeredOnLockScreen() - fun onDeviceLifted() - fun onQsExpansionStared() - fun onNotificationPanelClicked() - fun onSwipeUpOnBouncer() - fun onPrimaryBouncerUserInput() - fun onAccessibilityAction() - fun onWalletLaunched() - - /** Whether face auth is considered class 3 */ - fun isFaceAuthStrong(): Boolean -} - -/** - * Listener that can be registered with the [KeyguardFaceAuthInteractor] to receive updates about - * face authentication & detection updates. - * - * This is present to make it easier for use the new face auth API for code that cannot use - * [KeyguardFaceAuthInteractor.authenticationStatus] or [KeyguardFaceAuthInteractor.detectionStatus] - * flows. - */ -interface FaceAuthenticationListener { - /** Receive face isAuthenticated updates */ - fun onAuthenticatedChanged(isAuthenticated: Boolean) - - /** Receive face authentication status updates */ - fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus) - - /** Receive status updates whenever face detection runs */ - fun onDetectionStatusChanged(status: FaceDetectionStatus) - - fun onLockoutStateChanged(isLockedOut: Boolean) - - fun onRunningStateChanged(isRunning: Boolean) - - fun onAuthEnrollmentStateChanged(enrolled: Boolean) -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt index 942cd60db479..b3d0f918bb42 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt @@ -32,7 +32,7 @@ import android.os.PowerManager import android.os.PowerManager.WAKE_REASON_UNKNOWN import android.util.Log import com.android.internal.logging.UiEventLogger -import com.android.keyguard.FaceAuthUiEvent +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent /** * Wrapper for [FaceAuthenticateOptions] to convert SystemUI values to their corresponding value in diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt index 3c2facbc967a..9e6c5520d1b7 100644 --- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt @@ -2,9 +2,9 @@ package com.android.systemui.log import android.hardware.face.FaceManager import android.hardware.face.FaceSensorPropertiesInternal -import com.android.keyguard.FaceAuthUiEvent import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent +import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus import com.android.systemui.log.core.LogLevel.DEBUG import com.android.systemui.log.dagger.FaceAuthLog import com.android.systemui.power.shared.model.WakeSleepReason diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 1e677719e92a..24cb8fff9b67 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -20,6 +20,7 @@ import android.os.Build; import com.android.systemui.common.data.repository.PackageChangeRepository; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepositoryImpl; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogBufferFactory; import com.android.systemui.log.LogcatEchoTracker; @@ -462,7 +463,7 @@ public class LogModule { /** * Provides a {@link LogBuffer} for use by - * {@link com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepositoryImpl}. + * {@link DeviceEntryFaceAuthRepositoryImpl}. */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 3e50dd35749f..ac0bd29e22d6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -49,6 +49,7 @@ import kotlin.jvm.functions.Function1; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Objects; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -231,32 +232,39 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr if (!collapsedView && mQsTileRevealController != null) { mQsTileRevealController.updateRevealedTiles(tiles); } - boolean shouldChange = false; + boolean shouldChangeAll = false; + // If the new tiles are a prefix of the old tiles, we delete the extra tiles (from the old). + // If not (even if they share a prefix) we remove all and add all the new ones. if (tiles.size() <= mRecords.size()) { int i = 0; + // Iterate through the requested tiles and check if they are the same as the existing + // tiles. for (QSTile tile : tiles) { if (tile != mRecords.get(i).tile) { - shouldChange = true; + shouldChangeAll = true; break; } i++; } - // If the first tiles are the same as the new ones, remove any extras. - if (!shouldChange) { - while (i < mRecords.size()) { - QSPanelControllerBase.TileRecord record = mRecords.get(i); + // If the first tiles are the same as the new ones, we reuse them and remove any extra + // tiles. + if (!shouldChangeAll && i < mRecords.size()) { + List<TileRecord> extraRecords = mRecords.subList(i, mRecords.size()); + for (QSPanelControllerBase.TileRecord record : extraRecords) { mView.removeTile(record); record.tile.removeCallback(record.callback); - i++; } + extraRecords.clear(); mCachedSpecs = getTilesSpecs(); } } else { - shouldChange = true; + shouldChangeAll = true; } - if (shouldChange) { + // If we detected that the existing tiles are different than the requested tiles, clear them + // and add the new tiles. + if (shouldChangeAll) { for (QSPanelControllerBase.TileRecord record : mRecords) { mView.removeTile(record); record.tile.removeCallback(record.callback); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index fb6bc38c9a0b..a5c1cf1af191 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -26,6 +26,7 @@ import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.Flags.keyguardBottomAreaRefactor; import static com.android.systemui.Flags.migrateClocksToBlueprint; +import static com.android.systemui.Flags.predictiveBackAnimateShade; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -121,6 +122,7 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.doze.DozeLog; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; @@ -131,7 +133,6 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewConfigurator; import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver; @@ -327,7 +328,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final PulseExpansionHandler mPulseExpansionHandler; private final KeyguardBypassController mKeyguardBypassController; private final KeyguardUpdateMonitor mUpdateMonitor; - private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; + private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor; private final ConversationNotificationManager mConversationNotificationManager; private final AuthController mAuthController; private final MediaHierarchyManager mMediaHierarchyManager; @@ -779,7 +780,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ActiveNotificationsInteractor activeNotificationsInteractor, ShadeAnimationInteractor shadeAnimationInteractor, KeyguardViewConfigurator keyguardViewConfigurator, - KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, + DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor, SplitShadeStateController splitShadeStateController, PowerInteractor powerInteractor, KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm, @@ -891,7 +892,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mShadeHeaderController = shadeHeaderController; mLayoutInflater = layoutInflater; mFeatureFlags = featureFlags; - mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE); + mAnimateBack = predictiveBackAnimateShade(); mTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled(Flags.TRACKPAD_GESTURE_FEATURES); mFalsingCollector = falsingCollector; mWakeUpCoordinator = coordinator; @@ -936,7 +937,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mScreenOffAnimationController = screenOffAnimationController; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE); - mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; + mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor; int currentMode = navigationModeController.addListener( mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode)); @@ -2978,9 +2979,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mShadeLog.v("onMiddleClicked on Keyguard, mDozingOnDown: false"); // Try triggering face auth, this "might" run. Check // KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run. - mKeyguardFaceAuthInteractor.onNotificationPanelClicked(); + mDeviceEntryFaceAuthInteractor.onNotificationPanelClicked(); - if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) { + if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) { mUpdateMonitor.requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT, "lockScreenEmptySpaceTap"); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 8397caae438f..1dff99d53078 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -66,9 +66,9 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.classifier.Classifier; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.fragments.FragmentHostManager; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.MediaHierarchyManager; @@ -152,7 +152,7 @@ public class QuickSettingsController implements Dumpable { private final RecordingController mRecordingController; private final LockscreenGestureLogger mLockscreenGestureLogger; private final ShadeLogger mShadeLog; - private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; + private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor; private final CastController mCastController; private final SplitShadeStateController mSplitShadeStateController; private final InteractionJankMonitor mInteractionJankMonitor; @@ -338,7 +338,7 @@ public class QuickSettingsController implements Dumpable { InteractionJankMonitor interactionJankMonitor, ShadeLogger shadeLog, DumpManager dumpManager, - KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, + DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor, ShadeRepository shadeRepository, ShadeInteractor shadeInteractor, ActiveNotificationsInteractor activeNotificationsInteractor, @@ -384,7 +384,7 @@ public class QuickSettingsController implements Dumpable { mLockscreenGestureLogger = lockscreenGestureLogger; mMetricsLogger = metricsLogger; mShadeLog = shadeLog; - mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; + mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor; mCastController = castController; mInteractionJankMonitor = interactionJankMonitor; mShadeRepository = shadeRepository; @@ -972,7 +972,7 @@ public class QuickSettingsController implements Dumpable { // When expanding QS, let's authenticate the user if possible, // this will speed up notification actions. if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) { - mKeyguardFaceAuthInteractor.onQsExpansionStared(); + mDeviceEntryFaceAuthInteractor.onQsExpansionStared(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt index 4b896154c841..9fdd0bcc4ee9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.shelf.domain.interactor import android.os.PowerManager import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.statusbar.LockscreenShadeTransitionController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 57d49b250883..6e3aabf7c754 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -31,6 +31,7 @@ import static com.android.systemui.Flags.lightRevealMigration; import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL; import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF; import static com.android.systemui.statusbar.StatusBarState.SHADE; +import static com.android.systemui.Flags.predictiveBackSysui; import android.annotation.Nullable; import android.app.ActivityOptions; @@ -836,7 +837,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mLightRevealScrim = lightRevealScrim; // Based on teamfood flag, turn predictive back dispatch on at runtime. - if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) { + if (predictiveBackSysui()) { mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index 32b3ac2ad150..9f0863385ca1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -28,7 +28,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.Assert @@ -43,13 +43,13 @@ import javax.inject.Inject */ @SysUISingleton class KeyguardLiftController @Inject constructor( - private val context: Context, - private val statusBarStateController: StatusBarStateController, - private val asyncSensorManager: AsyncSensorManager, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor, - private val dumpManager: DumpManager, - private val selectedUserInteractor: SelectedUserInteractor, + private val context: Context, + private val statusBarStateController: StatusBarStateController, + private val asyncSensorManager: AsyncSensorManager, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, + private val dumpManager: DumpManager, + private val selectedUserInteractor: SelectedUserInteractor, ) : Dumpable, CoreStartable { private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) @@ -75,7 +75,7 @@ class KeyguardLiftController @Inject constructor( // Not listening anymore since trigger events unregister themselves isListening = false updateListeningState() - keyguardFaceAuthInteractor.onDeviceLifted() + deviceEntryFaceAuthInteractor.onDeviceLifted() keyguardUpdateMonitor.requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, "KeyguardLiftController") @@ -113,7 +113,7 @@ class KeyguardLiftController @Inject constructor( val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible && !statusBarStateController.isDozing - val isFaceEnabled = keyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() + val isFaceEnabled = deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled if (shouldListen != isListening) { isListening = shouldListen diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 4999123247a9..88347ab90606 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import static android.view.WindowInsets.Type.navigationBars; +import static com.android.systemui.Flags.predictiveBackAnimateBouncer; import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; @@ -400,8 +401,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mFoldAodAnimationController = sysUIUnfoldComponent .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); mAlternateBouncerInteractor = alternateBouncerInteractor; - mIsBackAnimationEnabled = - featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM); + mIsBackAnimationEnabled = predictiveBackAnimateBouncer(); mUdfpsOverlayInteractor = udfpsOverlayInteractor; mActivityStarter = activityStarter; mKeyguardTransitionInteractor = keyguardTransitionInteractor; diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java index 5558aa72bb57..111492c3e227 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java @@ -43,7 +43,7 @@ import com.android.settingslib.Utils; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.res.R; @@ -69,7 +69,7 @@ public class WalletActivity extends ComponentActivity implements private final Executor mExecutor; private final Handler mHandler; private final FalsingManager mFalsingManager; - private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; + private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor; private FalsingCollector mFalsingCollector; private final UserTracker mUserTracker; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -94,7 +94,7 @@ public class WalletActivity extends ComponentActivity implements KeyguardUpdateMonitor keyguardUpdateMonitor, StatusBarKeyguardViewManager keyguardViewManager, UiEventLogger uiEventLogger, - KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) { + DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor) { mKeyguardStateController = keyguardStateController; mKeyguardDismissUtil = keyguardDismissUtil; mActivityStarter = activityStarter; @@ -106,7 +106,7 @@ public class WalletActivity extends ComponentActivity implements mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardViewManager = keyguardViewManager; mUiEventLogger = uiEventLogger; - mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; + mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor; } @Override @@ -213,7 +213,7 @@ public class WalletActivity extends ComponentActivity implements true, Utils.getColorAttrDefaultColor( this, com.android.internal.R.attr.colorAccentPrimary)); - mKeyguardFaceAuthInteractor.onWalletLaunched(); + mDeviceEntryFaceAuthInteractor.onWalletLaunched(); } @Override diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index c5ce856e9785..bb9859505fe2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -120,16 +120,19 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated; import com.android.keyguard.logging.KeyguardUpdateMonitorLogger; import com.android.settingslib.fuelgauge.BatteryStatus; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig; +import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigImpl; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; +import com.android.systemui.deviceentry.domain.interactor.FaceAuthenticationListener; +import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus; +import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus; +import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus; import com.android.systemui.dump.DumpManager; -import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; -import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus; -import com.android.systemui.keyguard.shared.model.FaceDetectionStatus; -import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; @@ -261,7 +264,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Mock private SelectedUserInteractor mSelectedUserInteractor; @Mock - private KeyguardFaceAuthInteractor mFaceAuthInteractor; + private DeviceEntryFaceAuthInteractor mFaceAuthInteractor; @Captor private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener; @@ -315,7 +318,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mContext.getOrCreateTestableResources().addOverride( com.android.systemui.res.R.integer.config_face_auth_supported_posture, DEVICE_POSTURE_UNKNOWN); - mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfig( + mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfigImpl( mContext.getResources(), mGlobalSettings, mDumpManager @@ -923,8 +926,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void trustAgentHasTrust() { // WHEN user has trust - mKeyguardUpdateMonitor.onTrustChanged(true, true, - mSelectedUserInteractor.getSelectedUserId(), 0, null); + givenSelectedUserCanSkipBouncerFromTrustedState(); // THEN user is considered as "having trust" and bouncer can be skipped Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust( @@ -948,8 +950,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void trustAgentHasTrust_fingerprintLockout() { // GIVEN user has trust - mKeyguardUpdateMonitor.onTrustChanged(true, true, - mSelectedUserInteractor.getSelectedUserId(), 0, null); + givenSelectedUserCanSkipBouncerFromTrustedState(); Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust( mSelectedUserInteractor.getSelectedUserId())); @@ -1992,6 +1993,43 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + public void runFpDetectFlagDisabled_sideFps_keyguardDismissible_fingerprintAuthenticateRuns() { + mSetFlagsRule.disableFlags(Flags.FLAG_RUN_FINGERPRINT_DETECT_ON_DISMISSIBLE_KEYGUARD); + + // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks) + // will trigger updateBiometricListeningState(); + clearInvocations(mFingerprintManager); + mKeyguardUpdateMonitor.resetBiometricListeningState(); + + // GIVEN the user can skip the bouncer + givenSelectedUserCanSkipBouncerFromTrustedState(); + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); + mTestableLooper.processAllMessages(); + + // WHEN verify authenticate runs + verifyFingerprintAuthenticateCall(); + } + + @Test + public void sideFps_keyguardDismissible_fingerprintDetectRuns() { + mSetFlagsRule.enableFlags(Flags.FLAG_RUN_FINGERPRINT_DETECT_ON_DISMISSIBLE_KEYGUARD); + // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks) + // will trigger updateBiometricListeningState(); + clearInvocations(mFingerprintManager); + mKeyguardUpdateMonitor.resetBiometricListeningState(); + + // GIVEN the user can skip the bouncer + givenSelectedUserCanSkipBouncerFromTrustedState(); + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); + mTestableLooper.processAllMessages(); + + // WHEN verify detect runs + verifyFingerprintDetectCall(); + } + + @Test public void testFingerprintSensorProperties() throws RemoteException { mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered( new ArrayList<>()); @@ -2096,6 +2134,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FACE); } + private void givenSelectedUserCanSkipBouncerFromTrustedState() { + mKeyguardUpdateMonitor.onTrustChanged(true, true, + mSelectedUserInteractor.getSelectedUserId(), 0, null); + } + private void verifyFingerprintAuthenticateNeverCalled() { verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any()); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt index 8693d5c87dce..7c626a141a4a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt @@ -16,6 +16,9 @@ package com.android.systemui.back.domain.interactor +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.view.ViewRootImpl import android.window.BackEvent import android.window.BackEvent.EDGE_LEFT @@ -26,9 +29,8 @@ import android.window.WindowOnBackInvokedDispatcher import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.statusbar.IStatusBarService +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest @@ -72,7 +74,6 @@ import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) class BackActionInteractorTest : SysuiTestCase() { private val testScope = TestScope() - private val featureFlags = FakeFeatureFlags() private val executor = FakeExecutor(FakeSystemClock()) @JvmField @Rule var mockitoRule = MockitoJUnit.rule() @@ -107,17 +108,17 @@ class BackActionInteractorTest : SysuiTestCase() { statusBarKeyguardViewManager, shadeController, notificationShadeWindowController, - windowRootViewVisibilityInteractor, - featureFlags, + windowRootViewVisibilityInteractor ) .apply { this.setup(qsController, shadeViewController) } } private val powerInteractor = PowerInteractorFactory.create().powerInteractor + @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @Before fun setUp() { - featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false) whenever(notificationShadeWindowController.windowRootView).thenReturn(windowRootView) whenever(windowRootView.viewRootImpl).thenReturn(viewRootImpl) whenever(viewRootImpl.onBackInvokedDispatcher).thenReturn(onBackInvokedDispatcher) @@ -229,9 +230,9 @@ class BackActionInteractorTest : SysuiTestCase() { } @Test + @RequiresFlagsDisabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE) fun animationFlagOff_onBackInvoked_keyguardNotified() { backActionInteractor.start() - featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false) windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true) powerInteractor.setAwakeForTest() val callback = getBackInvokedCallback() @@ -243,8 +244,8 @@ class BackActionInteractorTest : SysuiTestCase() { } @Test + @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE) fun animationFlagOn_onBackInvoked_keyguardNotified() { - featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true) backActionInteractor.start() windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true) powerInteractor.setAwakeForTest() @@ -257,8 +258,8 @@ class BackActionInteractorTest : SysuiTestCase() { } @Test + @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE) fun animationFlagOn_callbackIsAnimationCallback() { - featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true) backActionInteractor.start() windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true) powerInteractor.setAwakeForTest() @@ -269,8 +270,8 @@ class BackActionInteractorTest : SysuiTestCase() { } @Test + @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE) fun onBackProgressed_shadeCannotBeCollapsed_shadeViewControllerNotNotified() { - featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true) backActionInteractor.start() windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true) powerInteractor.setAwakeForTest() @@ -284,8 +285,8 @@ class BackActionInteractorTest : SysuiTestCase() { } @Test + @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE) fun onBackProgressed_shadeCanBeCollapsed_shadeViewControllerNotified() { - featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true) backActionInteractor.start() windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true) powerInteractor.setAwakeForTest() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index 1f7dd6dd9a9c..c143bc0aa06c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -123,7 +123,6 @@ class AuthRippleControllerTest : SysuiTestCase() { udfpsControllerProvider, statusBarStateController, displayMetrics, - featureFlags, KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)), biometricUnlockController, lightRevealScrim, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt index 86b9b84ab36e..9b0e58d63952 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt @@ -22,7 +22,7 @@ import android.view.accessibility.AccessibilityNodeInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.whenever @@ -42,7 +42,7 @@ import org.mockito.MockitoAnnotations class FaceAuthAccessibilityDelegateTest : SysuiTestCase() { @Mock private lateinit var hostView: View - @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor private lateinit var underTest: FaceAuthAccessibilityDelegate @Before diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt index 755fa021b07c..54dbd04fb367 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt @@ -57,13 +57,13 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInte import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.display.data.repository.FakeDisplayRepository import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel import com.android.systemui.log.SideFpsLogger import com.android.systemui.log.logcatLogBuffer @@ -110,7 +110,7 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() @Mock private lateinit var activityTaskManager: ActivityTaskManager @Mock private lateinit var displayManager: DisplayManager - @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor @Mock private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider @Mock private lateinit var fpsUnlockTracker: FpsUnlockTracker diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt index bdca948da6e6..1fa60fc9044b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt @@ -55,13 +55,13 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.display.data.repository.FakeDisplayRepository import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel import com.android.systemui.log.SideFpsLogger import com.android.systemui.log.logcatLogBuffer @@ -101,7 +101,7 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() { @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() @Mock private lateinit var activityTaskManager: ActivityTaskManager - @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor @Mock private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt index 45a426e1cb84..e7963031411d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt @@ -36,13 +36,13 @@ import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.flags.SystemPropertiesHelper import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.shared.model.AuthenticationFlags import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown import com.android.systemui.res.R.string.kg_trust_agent_disabled @@ -122,7 +122,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() { fakeTrustRepository, testScope.backgroundScope, mSelectedUserInteractor, - mock(KeyguardFaceAuthInteractor::class.java), + mock(DeviceEntryFaceAuthInteractor::class.java), ) underTest = BouncerMessageInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt index dacf23a5a567..ee46f76a94e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -32,9 +32,9 @@ import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.bouncer.ui.BouncerViewDelegate import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeTrustRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @@ -73,7 +73,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor - @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor private lateinit var mainHandler: FakeHandler private lateinit var underTest: PrimaryBouncerInteractor private lateinit var resources: TestableResources diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt index 59bcf01e0eef..d5c3641e75a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt @@ -24,13 +24,13 @@ import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor +import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.powerRepository import com.android.systemui.power.shared.model.WakeSleepReason diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt index 94c34a525547..e9b4bbb5bb1a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.keyguard +package com.android.systemui.deviceentry.data.repository import android.os.PowerManager import android.testing.AndroidTestingRunner @@ -71,6 +71,6 @@ class FaceWakeUpTriggersConfigTest : SysuiTestCase() { wakeUpTriggers ) - return FaceWakeUpTriggersConfig(mContext.getResources(), globalSettings, dumpManager) + return FaceWakeUpTriggersConfigImpl(mContext.resources, globalSettings, dumpManager) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt index 769cf45da746..368d1d983879 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt @@ -1,21 +1,20 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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 + * 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. + * 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.systemui.keyguard.domain.interactor +package com.android.systemui.deviceentry.domain.interactor import android.app.trust.TrustManager import android.content.pm.UserInfo @@ -25,8 +24,6 @@ import android.os.Handler import android.os.PowerManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.keyguard.FaceAuthUiEvent -import com.android.keyguard.FaceWakeUpTriggersConfig import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase @@ -42,6 +39,9 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent +import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository @@ -49,7 +49,8 @@ import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintA import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository -import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -87,9 +88,9 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) -class KeyguardFaceAuthInteractorTest : SysuiTestCase() { +class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() { - private lateinit var underTest: SystemUIKeyguardFaceAuthInteractor + private lateinit var underTest: SystemUIDeviceEntryFaceAuthInteractor private lateinit var testScope: TestScope private lateinit var bouncerRepository: FakeKeyguardBouncerRepository private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository @@ -133,7 +134,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { fakeBiometricSettingsRepository = FakeBiometricSettingsRepository() underTest = - SystemUIKeyguardFaceAuthInteractor( + SystemUIDeviceEntryFaceAuthInteractor( mContext, testScope.backgroundScope, dispatcher, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt index 68d0f41bb284..ef89752dd3d1 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 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. @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.keyguard + +package com.android.systemui.deviceentry.shared import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt index 027dfa15f812..c4df27c2ccb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository @@ -62,7 +63,7 @@ import org.mockito.junit.MockitoRule class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() - @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt index 1f245f1e86f0..7358b9d5d2d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt @@ -147,7 +147,7 @@ class OccludingAppDeviceEntryInteractorTest : SysuiTestCase() { trustRepository, testScope.backgroundScope, mSelectedUserInteractor, - keyguardFaceAuthInteractor = mock(), + deviceEntryFaceAuthInteractor = mock(), ), AlternateBouncerInteractor( statusBarStateController = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index 018fa9e2bc86..a92111e2c270 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -394,6 +394,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { public void setTiles_differentTiles_extraTileRemoved() { when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); mController.setTiles(); + assertEquals(2, mController.mRecords.size()); clearInvocations(mQSPanel); @@ -402,6 +403,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { verify(mQSPanel, times(1)).removeTile(any()); verify(mQSPanel, never()).addTile(any()); + assertEquals(1, mController.mRecords.size()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 22207565a7b7..313276727caf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -86,6 +86,7 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; import com.android.systemui.common.ui.view.LongPressHandlingView; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; import com.android.systemui.doze.DozeLog; import com.android.systemui.dump.DumpManager; @@ -98,7 +99,6 @@ import com.android.systemui.keyguard.KeyguardViewConfigurator; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; @@ -337,7 +337,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener> mEmptySpaceClickListenerCaptor; @Mock protected ActivityStarter mActivityStarter; - @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; + @Mock protected DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor; @Mock private JavaAdapter mJavaAdapter; @Mock private CastController mCastController; @Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor; @@ -382,7 +382,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); - mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false); mFeatureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false); mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false); mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false); @@ -730,7 +729,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mActiveNotificationsInteractor, mShadeAnimationInteractor, mKeyguardViewConfigurator, - mKeyguardFaceAuthInteractor, + mDeviceEntryFaceAuthInteractor, new ResourcesSplitShadeStateController(), mPowerInteractor, mKeyguardClockPositionAlgorithm, @@ -800,7 +799,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mInteractionJankMonitor, mShadeLog, mDumpManager, - mKeyguardFaceAuthInteractor, + mDeviceEntryFaceAuthInteractor, mShadeRepository, mShadeInteractor, mActiveNotificationsInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 3cbb9bb2df97..2e8d46a83e1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -1120,7 +1120,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo mTouchHandler.onTouch(mock(View.class), mDownMotionEvent); mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0); - verify(mKeyguardFaceAuthInteractor).onNotificationPanelClicked(); + verify(mDeviceEntryFaceAuthInteractor).onNotificationPanelClicked(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index c21addc444b4..ee7c6c89e4a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -62,7 +62,7 @@ import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsReposi import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.TransitionStep @@ -257,7 +257,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { FakeTrustRepository(), testScope.backgroundScope, mSelectedUserInteractor, - mock(KeyguardFaceAuthInteractor::class.java) + mock(DeviceEntryFaceAuthInteractor::class.java) ), facePropertyRepository = FakeFacePropertyRepository(), deviceEntryFingerprintAuthRepository = diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index eb5633b70f61..727a6c3d1adc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -42,6 +42,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlagsClassic; @@ -55,7 +56,6 @@ import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnima import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.media.controls.pipeline.MediaDataManager; @@ -378,7 +378,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mInteractionJankMonitor, mShadeLogger, mDumpManager, - mock(KeyguardFaceAuthInteractor.class), + mock(DeviceEntryFaceAuthInteractor.class), mShadeRepository, mShadeInteractor, mActiveNotificationsInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index e3396364a85a..316f2b99b27b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -334,15 +334,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { public void setup() throws Exception { MockitoAnnotations.initMocks(this); - // CentralSurfacesImpl's runtime flag check fails if the flag is absent. - // This value is unused, because test manifest is opted in. - mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false); // Set default value to avoid IllegalStateException. mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false); - // For the Shade to respond to Back gesture, we must enable the event routing - mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true); - // For the Shade to animate during the Back gesture, we must enable the animation flag. - mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true); mSetFlagsRule.enableFlags(FLAG_LIGHT_REVEAL_MIGRATION); // Turn AOD on and toggle feature flag for jank fixes mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 225ddb6110c2..8dde9359bdfc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -37,6 +37,9 @@ import static org.mockito.Mockito.when; import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.service.trust.TrustAgentService; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -98,6 +101,7 @@ import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.google.common.truth.Truth; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -165,6 +169,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Before public void setUp() { @@ -175,7 +181,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate); when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback); mFeatureFlags = new FakeFeatureFlags(); - mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true); mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false); mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false); mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); @@ -584,6 +589,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER) public void testPredictiveBackCallback_registration() { /* verify that a predictive back callback is registered when the bouncer becomes visible */ mBouncerExpansionCallback.onVisibilityChanged(true); @@ -598,6 +604,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER) public void testPredictiveBackCallback_invocationHidesBouncer() { mBouncerExpansionCallback.onVisibilityChanged(true); /* capture the predictive back callback during registration */ @@ -615,6 +622,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER) public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() { when(mKeyguardSecurityModel.getSecurityMode(anyInt())) .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin); @@ -634,6 +642,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER) public void testPredictiveBackCallback_forwardsBackDispatches() { mBouncerExpansionCallback.onVisibilityChanged(true); /* capture the predictive back callback during registration */ diff --git a/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt new file mode 100644 index 000000000000..187935bb26c4 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 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.keyguard + +import android.app.trust.TrustManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.trustManager by Kosmos.Fixture { mock<TrustManager>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt new file mode 100644 index 000000000000..6ef74194fd85 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 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.systemui.biometrics.data.repository + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture + +val Kosmos.facePropertyRepository by Fixture { FakeFacePropertyRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt new file mode 100644 index 000000000000..21cff0dbde2d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 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.systemui.deviceentry.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig by + Kosmos.Fixture { FakeFaceWakeUpTriggersConfig() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt new file mode 100644 index 000000000000..af617b741df3 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 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.systemui.deviceentry.data.repository + +import com.android.systemui.power.shared.model.WakeSleepReason + +class FakeFaceWakeUpTriggersConfig : FaceWakeUpTriggersConfig { + private val triggerFaceAuthOnWakeUpFrom: MutableSet<Int> = mutableSetOf() + private val wakeSleepReasonsToTriggerFaceAuth: MutableSet<WakeSleepReason> = mutableSetOf() + override fun shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason: Int): Boolean { + return triggerFaceAuthOnWakeUpFrom.contains(pmWakeReason) + } + + override fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean { + return wakeSleepReasonsToTriggerFaceAuth.contains(wakeReason) + } + + fun setTriggerFaceAuthOnWakeUpFrom(pmWakeReasons: Set<Int>) { + triggerFaceAuthOnWakeUpFrom.clear() + triggerFaceAuthOnWakeUpFrom.addAll(pmWakeReasons) + + wakeSleepReasonsToTriggerFaceAuth.clear() + wakeSleepReasonsToTriggerFaceAuth.addAll( + pmWakeReasons.map { WakeSleepReason.fromPowerManagerWakeReason(it) } + ) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt index d2dff7834b40..0b1fb4074226 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt @@ -18,13 +18,45 @@ package com.android.systemui.deviceentry.domain.interactor +import android.content.applicationContext +import com.android.keyguard.keyguardUpdateMonitor +import com.android.keyguard.trustManager +import com.android.systemui.biometrics.data.repository.facePropertyRepository +import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor +import com.android.systemui.deviceentry.data.repository.faceWakeUpTriggersConfig +import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.log.FaceAuthenticationLogger +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.user.data.repository.userRepository +import com.android.systemui.util.mockito.mock import kotlinx.coroutines.ExperimentalCoroutinesApi +val Kosmos.faceAuthLogger by Kosmos.Fixture { mock<FaceAuthenticationLogger>() } val Kosmos.deviceEntryFaceAuthInteractor by Kosmos.Fixture { - DeviceEntryFaceAuthInteractor( + SystemUIDeviceEntryFaceAuthInteractor( + context = applicationContext, + applicationScope = applicationCoroutineScope, + mainDispatcher = testDispatcher, repository = deviceEntryFaceAuthRepository, + primaryBouncerInteractor = { primaryBouncerInteractor }, + alternateBouncerInteractor = alternateBouncerInteractor, + keyguardTransitionInteractor = keyguardTransitionInteractor, + faceAuthenticationLogger = faceAuthLogger, + keyguardUpdateMonitor = keyguardUpdateMonitor, + deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository, + userRepository = userRepository, + facePropertyRepository = facePropertyRepository, + faceWakeUpTriggersConfig = faceWakeUpTriggersConfig, + powerInteractor = powerInteractor, + biometricSettingsRepository = biometricSettingsRepository, + trustManager = trustManager, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt index 3d729675f7ab..599b51422cdb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.data.repository +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.kosmos.Kosmos var Kosmos.deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository by diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt index a1b6587be0b8..e96aeada0212 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt @@ -16,10 +16,11 @@ package com.android.systemui.keyguard.data.repository -import com.android.keyguard.FaceAuthUiEvent import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus -import com.android.systemui.keyguard.shared.model.FaceDetectionStatus +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.deviceentry.shared.FaceAuthUiEvent +import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus +import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus import dagger.Binds import dagger.Module import javax.inject.Inject diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt index 3cabf0c07423..5f5d42850619 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt @@ -27,6 +27,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInte import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -73,7 +74,7 @@ object KeyguardDismissInteractorFactory { trustRepository, testScope.backgroundScope, mock(SelectedUserInteractor::class.java), - mock(KeyguardFaceAuthInteractor::class.java), + mock(DeviceEntryFaceAuthInteractor::class.java), ) val alternateBouncerInteractor = AlternateBouncerInteractor( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 9f71161e4452..09ab6557c663 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -52,20 +52,20 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.communal.data.repository.FakeCommunalRepository import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory +import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.doze.DozeLogger import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.TrustRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.kosmos.Kosmos @@ -266,14 +266,14 @@ class SceneTestUtils( fun bouncerInteractor( authenticationInteractor: AuthenticationInteractor, - keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor = mock(), + deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor = mock(), ): BouncerInteractor { return BouncerInteractor( applicationScope = applicationScope(), applicationContext = context, repository = bouncerRepository, authenticationInteractor = authenticationInteractor, - keyguardFaceAuthInteractor = keyguardFaceAuthInteractor, + deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor, falsingInteractor = falsingInteractor(), powerInteractor = powerInteractor(), simBouncerInteractor = simBouncerInteractor, diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 7ac4dd30fe71..a1b6f297f287 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -7879,7 +7879,6 @@ public class AudioService extends IAudioService.Stub DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET); DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET); DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET); - DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_HDMI); } /** only public for mocking/spying, do not call outside of AudioService */ diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 6e503cbd68f3..030585f35983 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -970,9 +970,14 @@ public class DisplayModeDirector { if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) { // The flag had been turned off, we need to restore the original value - Settings.System.putFloatForUser(cr, - Settings.System.MIN_REFRESH_RATE, minRefreshRate, cr.getUserId()); + Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE, + highestRefreshRate, cr.getUserId()); } + } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled + && Math.round(minRefreshRate) == Math.round(highestRefreshRate)) { + // The flag has been turned on, we need to upgrade the setting + Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE, + Float.POSITIVE_INFINITY, cr.getUserId()); } float peakRefreshRate = Settings.System.getFloatForUser(cr, @@ -983,9 +988,14 @@ public class DisplayModeDirector { if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) { // The flag had been turned off, we need to restore the original value - Settings.System.putFloatForUser(cr, - Settings.System.PEAK_REFRESH_RATE, peakRefreshRate, cr.getUserId()); + Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE, + highestRefreshRate, cr.getUserId()); } + } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled + && Math.round(peakRefreshRate) == Math.round(highestRefreshRate)) { + // The flag has been turned on, we need to upgrade the setting + Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE, + Float.POSITIVE_INFINITY, cr.getUserId()); } updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 24bcb4ece7aa..d722242b20b0 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -16,6 +16,7 @@ package com.android.server.inputmethod; +import static android.content.Context.DEVICE_ID_DEFAULT; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; @@ -315,10 +316,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // All known input methods. final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>(); private final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>(); + // Mapping from deviceId to the device-specific imeId for that device. + @GuardedBy("ImfLock.class") private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>(); - private final InputMethodSubtypeSwitchingController mSwitchingController; + // TODO: Instantiate mSwitchingController for each user. + @NonNull + private InputMethodSubtypeSwitchingController mSwitchingController; final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(); @@ -339,6 +344,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; + @GuardedBy("ImfLock.class") + private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; + @Nullable private StatusBarManagerInternal mStatusBarManagerInternal; private boolean mShowOngoingImeSwitcherForPhones; @GuardedBy("ImfLock.class") @@ -1699,7 +1707,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId); mSwitchingController = - InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context); + InputMethodSubtypeSwitchingController.createInstanceLocked(context, mMethodMap, + userId); mHardwareKeyboardShortcutController.reset(mSettings); mMenuController = new InputMethodMenuController(this); mBindingController = @@ -2464,11 +2473,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @StartInputReason int startInputReason, int unverifiedTargetSdkVersion, @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { - // If no method is currently selected, do nothing. - final String selectedMethodId = getSelectedMethodIdLocked(); - if (selectedMethodId == null) { - return InputBindResult.NO_IME; - } + String selectedMethodId = getSelectedMethodIdLocked(); if (!mSystemReady) { // If the system is not yet ready, we shouldn't be running third @@ -2493,8 +2498,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.NOT_IME_TARGET_WINDOW; } final int csDisplayId = cs.mSelfReportedDisplayId; + final int oldDisplayIdToShowIme = mDisplayIdToShowIme; mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId); + // Potentially override the selected input method if the new display belongs to a virtual + // device with a custom IME. + if (oldDisplayIdToShowIme != mDisplayIdToShowIme) { + final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId); + if (deviceMethodId == null) { + mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true); + } else if (!Objects.equals(deviceMethodId, selectedMethodId)) { + setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme); + selectedMethodId = deviceMethodId; + } + } + if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) { hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, null /* resultReceiver */, @@ -2502,6 +2520,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.NO_IME; } + // If no method is currently selected, do nothing. + if (selectedMethodId == null) { + return InputBindResult.NO_IME; + } + if (mCurClient != cs) { prepareClientSwitchLocked(cs); } @@ -2568,6 +2591,62 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return mBindingController.bindCurrentMethod(); } + /** + * Update the current deviceId and return the relevant imeId for this device. + * 1. If the device changes to virtual and its custom IME is not available, then disable IME. + * 2. If the device changes to virtual with valid custom IME, then return the custom IME. If + * the old device was default, then store the current imeId so it can be restored. + * 3. If the device changes to default, restore the default device IME. + * 4. Otherwise keep the current imeId. + */ + @GuardedBy("ImfLock.class") + private String computeCurrentDeviceMethodIdLocked(String currentMethodId) { + if (mVdmInternal == null) { + mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class); + } + if (mVdmInternal == null || !android.companion.virtual.flags.Flags.vdmCustomIme()) { + return currentMethodId; + } + + final int oldDeviceId = mDeviceIdToShowIme; + mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme); + if (mDeviceIdToShowIme == oldDeviceId) { + return currentMethodId; + } + if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { + final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod(); + if (DEBUG) { + Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId); + } + return defaultDeviceMethodId; + } + + final String deviceMethodId = + mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId); + if (Objects.equals(deviceMethodId, currentMethodId)) { + return currentMethodId; + } else if (!mMethodMap.containsKey(deviceMethodId)) { + if (DEBUG) { + Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme + + " because its custom input method is not available: " + deviceMethodId); + } + return null; + } + + if (oldDeviceId == DEVICE_ID_DEFAULT) { + if (DEBUG) { + Slog.v(TAG, "Storing default device input method " + currentMethodId); + } + mSettings.putSelectedDefaultDeviceInputMethod(currentMethodId); + } + if (DEBUG) { + Slog.v(TAG, "Switching current input method from " + currentMethodId + + " to device-specific one " + deviceMethodId + " because the current display " + + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme); + } + return deviceMethodId; + } + @GuardedBy("ImfLock.class") void invalidateAutofillSessionLocked() { mAutofillController.invalidateAutofillSession(); @@ -3218,13 +3297,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // There is no longer an input method set, so stop any current one. resetCurrentMethodAndClientLocked(UnbindReason.NO_IME); } - // Here is not the perfect place to reset the switching controller. Ideally - // mSwitchingController and mSettings should be able to share the same state. - // TODO: Make sure that mSwitchingController and mSettings are sharing the - // the same enabled IMEs list. - mSwitchingController.resetCircularListLocked(mContext); - mHardwareKeyboardShortcutController.reset(mSettings); + // TODO: Instantiate mSwitchingController for each user. + if (mSettings.getCurrentUserId() == mSwitchingController.getUserId()) { + mSwitchingController.resetCircularListLocked(mMethodMap); + } else { + mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( + mContext, mMethodMap, mSettings.getCurrentUserId()); + } + + mHardwareKeyboardShortcutController.reset(mSettings); sendOnNavButtonFlagsChangedLocked(); } @@ -3242,6 +3324,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") void setInputMethodLocked(String id, int subtypeId) { + setInputMethodLocked(id, subtypeId, DEVICE_ID_DEFAULT); + } + + @GuardedBy("ImfLock.class") + void setInputMethodLocked(String id, int subtypeId, int deviceId) { InputMethodInfo info = mMethodMap.get(id); if (info == null) { throw getExceptionForUnknownImeId(id); @@ -3285,6 +3372,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } // Changing to a different IME. + if (mDeviceIdToShowIme != DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_DEFAULT) { + // This change should only be applicable to the default device but the current input + // method is a custom one specific to a virtual device. So only update the settings + // entry used to restore the default device input method once we want to show the IME + // back on the default device. + mSettings.putSelectedDefaultDeviceInputMethod(id); + return; + } IInputMethodInvoker curMethod = getCurMethodLocked(); if (curMethod != null) { curMethod.removeStylusHandwritingWindow(); @@ -4598,6 +4693,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } return; } + if (mSettings.getCurrentUserId() != mSwitchingController.getUserId()) { + return; + } final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked()); if (imi != null) { mSwitchingController.onUserActionLocked(imi, mCurrentSubtype); @@ -4835,9 +4933,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId); - final List<ImeSubtypeListItem> imList = mSwitchingController - .getSortedInputMethodAndSubtypeListForImeMenuLocked( - showAuxSubtypes, isScreenLocked); + final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController + .getSortedInputMethodAndSubtypeList( + showAuxSubtypes, isScreenLocked, false, mContext, + mMethodMap, mSettings.getCurrentUserId()); mMenuController.showInputMethodMenuLocked(showAuxSubtypes, displayId, lastInputMethodId, lastInputMethodSubtypeId, imList); } @@ -5222,11 +5321,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub updateDefaultVoiceImeIfNeededLocked(); - // Here is not the perfect place to reset the switching controller. Ideally - // mSwitchingController and mSettings should be able to share the same state. - // TODO: Make sure that mSwitchingController and mSettings are sharing the - // the same enabled IMEs list. - mSwitchingController.resetCircularListLocked(mContext); + // TODO: Instantiate mSwitchingController for each user. + if (mSettings.getCurrentUserId() == mSwitchingController.getUserId()) { + mSwitchingController.resetCircularListLocked(mMethodMap); + } else { + mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( + mContext, mMethodMap, mSettings.getCurrentUserId()); + } mHardwareKeyboardShortcutController.reset(mSettings); sendOnNavButtonFlagsChangedLocked(); @@ -5308,11 +5409,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub StringBuilder builder = new StringBuilder(); if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked( builder, enabledInputMethodsList, id)) { - // Disabled input method is currently selected, switch to another one. - final String selId = mSettings.getSelectedInputMethod(); - if (id.equals(selId) && !chooseNewDefaultIMELocked()) { - Slog.i(TAG, "Can't find new IME, unsetting the current input method."); - resetSelectedInputMethodAndSubtypeLocked(""); + if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { + // Disabled input method is currently selected, switch to another one. + final String selId = mSettings.getSelectedInputMethod(); + if (id.equals(selId) && !chooseNewDefaultIMELocked()) { + Slog.i(TAG, "Can't find new IME, unsetting the current input method."); + resetSelectedInputMethodAndSubtypeLocked(""); + } + } else if (id.equals(mSettings.getSelectedDefaultDeviceInputMethod())) { + // Disabled default device IME while using a virtual device one, choose a + // new default one but only update the settings. + InputMethodInfo newDefaultIme = + InputMethodInfoUtils.getMostApplicableDefaultIME( + mSettings.getEnabledInputMethodListLocked()); + mSettings.putSelectedDefaultDeviceInputMethod( + newDefaultIme == null ? "" : newDefaultIme.getId()); } // Previous state was enabled. return true; @@ -5652,9 +5763,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void setVirtualDeviceInputMethodForAllUsers(int deviceId, @Nullable String imeId) { - // TODO(b/287269288): validate that id belongs to a valid virtual device instead. - Preconditions.checkArgument(deviceId != Context.DEVICE_ID_DEFAULT, - "DeviceId " + deviceId + " does not belong to a virtual device."); + Preconditions.checkArgument(deviceId != DEVICE_ID_DEFAULT, + TextUtils.formatSimple("DeviceId %d is not a virtual device id.", deviceId)); synchronized (ImfLock.class) { if (imeId == null) { mVirtualDeviceMethodMap.remove(deviceId); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java index 431aabdc3018..4439b0683afa 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java @@ -16,10 +16,14 @@ package com.android.server.inputmethod; +import android.annotation.AnyThread; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.Context; -import android.content.pm.PackageManager; +import android.os.UserHandle; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Printer; import android.util.Slog; @@ -32,7 +36,6 @@ import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.Objects; /** @@ -154,79 +157,71 @@ final class InputMethodSubtypeSwitchingController { } } - private static class InputMethodAndSubtypeList { - private final Context mContext; - // Used to load label - private final PackageManager mPm; - private final String mSystemLocaleStr; - private final InputMethodSettings mSettings; - - InputMethodAndSubtypeList(Context context, InputMethodSettings settings) { - mContext = context; - mSettings = settings; - mPm = context.getPackageManager(); - final Locale locale = context.getResources().getConfiguration().locale; - mSystemLocaleStr = locale != null ? locale.toString() : ""; + static List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList( + boolean includeAuxiliarySubtypes, boolean isScreenLocked, boolean forImeMenu, + @NonNull Context context, @NonNull ArrayMap<String, InputMethodInfo> methodMap, + @UserIdInt int userId) { + final Context userAwareContext = context.getUserId() == userId + ? context + : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); + final String mSystemLocaleStr = SystemLocaleWrapper.get(userId).get(0).toLanguageTag(); + final InputMethodSettings settings = new InputMethodSettings(methodMap, userId); + + final ArrayList<InputMethodInfo> imis = settings.getEnabledInputMethodListLocked(); + if (imis.isEmpty()) { + return Collections.emptyList(); } - - public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList( - boolean includeAuxiliarySubtypes, boolean isScreenLocked, boolean forImeMenu) { - final ArrayList<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked(); - if (imis.isEmpty()) { - return Collections.emptyList(); + if (isScreenLocked && includeAuxiliarySubtypes) { + if (DEBUG) { + Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen."); } - if (isScreenLocked && includeAuxiliarySubtypes) { + includeAuxiliarySubtypes = false; + } + final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>(); + final int numImes = imis.size(); + for (int i = 0; i < numImes; ++i) { + final InputMethodInfo imi = imis.get(i); + if (forImeMenu && !imi.shouldShowInInputMethodPicker()) { + continue; + } + final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = + settings.getEnabledInputMethodSubtypeListLocked(imi, true); + final ArraySet<String> enabledSubtypeSet = new ArraySet<>(); + for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) { + enabledSubtypeSet.add(String.valueOf(subtype.hashCode())); + } + final CharSequence imeLabel = imi.loadLabel(userAwareContext.getPackageManager()); + if (enabledSubtypeSet.size() > 0) { + final int subtypeCount = imi.getSubtypeCount(); if (DEBUG) { - Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen."); - } - includeAuxiliarySubtypes = false; - } - final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>(); - final int numImes = imis.size(); - for (int i = 0; i < numImes; ++i) { - final InputMethodInfo imi = imis.get(i); - if (forImeMenu && !imi.shouldShowInInputMethodPicker()) { - continue; + Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); } - final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = - mSettings.getEnabledInputMethodSubtypeListLocked(imi, true); - final ArraySet<String> enabledSubtypeSet = new ArraySet<>(); - for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) { - enabledSubtypeSet.add(String.valueOf(subtype.hashCode())); - } - final CharSequence imeLabel = imi.loadLabel(mPm); - if (enabledSubtypeSet.size() > 0) { - final int subtypeCount = imi.getSubtypeCount(); - if (DEBUG) { - Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); - } - for (int j = 0; j < subtypeCount; ++j) { - final InputMethodSubtype subtype = imi.getSubtypeAt(j); - final String subtypeHashCode = String.valueOf(subtype.hashCode()); - // We show all enabled IMEs and subtypes when an IME is shown. - if (enabledSubtypeSet.contains(subtypeHashCode) - && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) { - final CharSequence subtypeLabel = - subtype.overridesImplicitlyEnabledSubtype() ? null : subtype - .getDisplayName(mContext, imi.getPackageName(), - imi.getServiceInfo().applicationInfo); - imList.add(new ImeSubtypeListItem(imeLabel, - subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr)); - - // Removing this subtype from enabledSubtypeSet because we no - // longer need to add an entry of this subtype to imList to avoid - // duplicated entries. - enabledSubtypeSet.remove(subtypeHashCode); - } + for (int j = 0; j < subtypeCount; ++j) { + final InputMethodSubtype subtype = imi.getSubtypeAt(j); + final String subtypeHashCode = String.valueOf(subtype.hashCode()); + // We show all enabled IMEs and subtypes when an IME is shown. + if (enabledSubtypeSet.contains(subtypeHashCode) + && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) { + final CharSequence subtypeLabel = + subtype.overridesImplicitlyEnabledSubtype() ? null : subtype + .getDisplayName(userAwareContext, imi.getPackageName(), + imi.getServiceInfo().applicationInfo); + imList.add(new ImeSubtypeListItem(imeLabel, + subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr)); + + // Removing this subtype from enabledSubtypeSet because we no + // longer need to add an entry of this subtype to imList to avoid + // duplicated entries. + enabledSubtypeSet.remove(subtypeHashCode); } - } else { - imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null, - mSystemLocaleStr)); } + } else { + imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null, + mSystemLocaleStr)); } - Collections.sort(imList); - return imList; } + Collections.sort(imList); + return imList; } private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) { @@ -479,18 +474,32 @@ final class InputMethodSubtypeSwitchingController { } } - private final InputMethodSettings mSettings; - private InputMethodAndSubtypeList mSubtypeList; + private final Context mContext; + @UserIdInt + private final int mUserId; private ControllerImpl mController; - private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) { - mSettings = settings; - resetCircularListLocked(context); + private InputMethodSubtypeSwitchingController(@NonNull Context context, + @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) { + mContext = context; + mUserId = userId; + mController = ControllerImpl.createFrom(null, + getSortedInputMethodAndSubtypeList( + false /* includeAuxiliarySubtypes */, false /* isScreenLocked */, + false /* forImeMenu */, context, methodMap, userId)); } + @NonNull public static InputMethodSubtypeSwitchingController createInstanceLocked( - InputMethodSettings settings, Context context) { - return new InputMethodSubtypeSwitchingController(settings, context); + @NonNull Context context, + @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) { + return new InputMethodSubtypeSwitchingController(context, methodMap, userId); + } + + @AnyThread + @UserIdInt + int getUserId() { + return mUserId; } public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) { @@ -503,12 +512,12 @@ final class InputMethodSubtypeSwitchingController { mController.onUserActionLocked(imi, subtype); } - public void resetCircularListLocked(Context context) { - mSubtypeList = new InputMethodAndSubtypeList(context, mSettings); + public void resetCircularListLocked( + @NonNull ArrayMap<String, InputMethodInfo> methodMap) { mController = ControllerImpl.createFrom(mController, - mSubtypeList.getSortedInputMethodAndSubtypeList( + getSortedInputMethodAndSubtypeList( false /* includeAuxiliarySubtypes */, false /* isScreenLocked */, - false /* forImeMenu */)); + false /* forImeMenu */, mContext, methodMap, mUserId)); } public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi, @@ -522,12 +531,6 @@ final class InputMethodSubtypeSwitchingController { return mController.getNextInputMethod(onlyCurrentIme, imi, subtype); } - public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListForImeMenuLocked( - boolean includingAuxiliarySubtypes, boolean isScreenLocked) { - return mSubtypeList.getSortedInputMethodAndSubtypeList( - includingAuxiliarySubtypes, isScreenLocked, true /* forImeMenu */); - } - public void dump(final Printer pw) { if (mController != null) { mController.dump(pw); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index a0b55edddec7..f9b06deb3142 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -51,6 +51,7 @@ import com.android.server.textservices.TextServicesManagerInternal; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.StringJoiner; import java.util.function.Consumer; import java.util.function.Predicate; @@ -241,6 +242,12 @@ final class InputMethodUtils { Slog.d(TAG, "--- Switch the current user from " + mCurrentUserId + " to " + userId); } mCurrentUserId = userId; + String ime = getSelectedInputMethod(); + String defaultDeviceIme = getSelectedDefaultDeviceInputMethod(); + if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) { + putSelectedInputMethod(defaultDeviceIme); + putSelectedDefaultDeviceInputMethod(null); + } } private void putString(@NonNull String key, @Nullable String str) { @@ -636,6 +643,24 @@ final class InputMethodUtils { return imi; } + @Nullable + String getSelectedDefaultDeviceInputMethod() { + final String imi = getString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null); + if (DEBUG) { + Slog.d(TAG, "getSelectedDefaultDeviceInputMethodStr: " + imi + ", " + + mCurrentUserId); + } + return imi; + } + + void putSelectedDefaultDeviceInputMethod(String imeId) { + if (DEBUG) { + Slog.d(TAG, "putSelectedDefaultDeviceInputMethodStr: " + imeId + ", " + + mCurrentUserId); + } + putString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, imeId); + } + void putDefaultVoiceInputMethod(String imeId) { if (DEBUG) { Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId); diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java index 41d01762505d..22951d522b53 100644 --- a/services/core/java/com/android/server/pm/InitAppsHelper.java +++ b/services/core/java/com/android/server/pm/InitAppsHelper.java @@ -180,7 +180,9 @@ final class InitAppsHelper { // priority of system overlays. final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>(); for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) { - for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) { + final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName( + apexInfo.apexModuleName); + for (String packageName : mApexManager.getApksInApex(apexPackageName)) { apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath); } } diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index dcfc855da855..376b06105b8c 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -25,6 +25,7 @@ import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap; import static android.content.pm.ArchivedActivityInfo.drawableToBitmap; import static android.content.pm.PackageInstaller.EXTRA_UNARCHIVE_STATUS; +import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION; import static android.content.pm.PackageInstaller.UNARCHIVAL_OK; import static android.content.pm.PackageManager.DELETE_ARCHIVE; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; @@ -72,10 +73,12 @@ import android.os.Environment; import android.os.IBinder; import android.os.ParcelableException; import android.os.Process; +import android.os.RemoteException; import android.os.SELinux; import android.os.UserHandle; import android.text.TextUtils; import android.util.ExceptionUtils; +import android.util.Pair; import android.util.Slog; import com.android.internal.R; @@ -93,7 +96,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -140,15 +145,23 @@ public class PackageArchiver { private final Context mContext; private final PackageManagerService mPm; + private final AppStateHelper mAppStateHelper; + @Nullable private LauncherApps mLauncherApps; @Nullable private AppOpsManager mAppOpsManager; + /* IntentSender store that maps key: {userId, appPackageName} to respective existing attached + unarchival intent sender. */ + private final Map<Pair<Integer, String>, IntentSender> mLauncherIntentSenders; + PackageArchiver(Context context, PackageManagerService mPm) { this.mContext = context; this.mPm = mPm; + this.mAppStateHelper = new AppStateHelper(mContext); + this.mLauncherIntentSenders = new HashMap<>(); } /** Returns whether a package is archived for a user. */ @@ -235,37 +248,32 @@ public class PackageArchiver { // Return early as the calling UID does not match caller package's UID. return START_CLASS_NOT_FOUND; } + String currentLauncherPackageName = getCurrentLauncherPackageName(userId); if ((currentLauncherPackageName == null || !callerPackageName.equals( currentLauncherPackageName)) && callingUid != Process.SHELL_UID) { // TODO(b/311619990): Remove dependency on SHELL_UID for testing Slog.e(TAG, TextUtils.formatSimple( - "callerPackageName: %s does not qualify for archival of package: " + "%s!", + "callerPackageName: %s does not qualify for unarchival of package: " + "%s!", callerPackageName, packageName)); return START_PERMISSION_DENIED; } - // TODO(b/302114464): Handle edge cases & also divert to a dialog based on - // permissions + compat options + Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName)); - try { - final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { - @Override - public void send(int code, Intent intent, String resolvedType, - IBinder allowlistToken, - IIntentReceiver finishedReceiver, String requiredPermission, - Bundle options) { - // TODO(b/302114464): Handle intent sender status codes - } - }; + try { + // TODO(b/311709794) Make showUnarchivalConfirmation dependent on the compat options. requestUnarchive(packageName, callerPackageName, - new IntentSender((IIntentSender) mLocalSender), UserHandle.of(userId)); + getOrCreateUnarchiveIntentSender(userId, packageName), + UserHandle.of(userId), + false /* showUnarchivalConfirmation= */); } catch (Throwable t) { Slog.e(TAG, TextUtils.formatSimple( "Unexpected error occurred while unarchiving package %s: %s.", packageName, t.getLocalizedMessage())); return START_ABORTED; } + return START_SUCCESS; } @@ -321,6 +329,20 @@ public class PackageArchiver { return true; } + private IntentSender getOrCreateUnarchiveIntentSender(int userId, String packageName) { + Pair<Integer, String> key = Pair.create(userId, packageName); + synchronized (mLauncherIntentSenders) { + IntentSender intentSender = mLauncherIntentSenders.get(key); + if (intentSender != null) { + return intentSender; + } + IntentSender unarchiveIntentSender = new IntentSender( + (IIntentSender) new UnarchiveIntentSender()); + mLauncherIntentSenders.put(key, unarchiveIntentSender); + return unarchiveIntentSender; + } + } + /** Creates archived state for the package and user. */ private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId) throws PackageManager.NameNotFoundException { @@ -553,6 +575,15 @@ public class PackageArchiver { @NonNull String callerPackageName, @NonNull IntentSender statusReceiver, @NonNull UserHandle userHandle) { + requestUnarchive(packageName, callerPackageName, statusReceiver, userHandle, + false /* showUnarchivalConfirmation= */); + } + + private void requestUnarchive( + @NonNull String packageName, + @NonNull String callerPackageName, + @NonNull IntentSender statusReceiver, + @NonNull UserHandle userHandle, boolean showUnarchivalConfirmation) { Objects.requireNonNull(packageName); Objects.requireNonNull(callerPackageName); Objects.requireNonNull(statusReceiver); @@ -597,8 +628,8 @@ public class PackageArchiver { + "an unarchival."); } - if (!hasInstallPackages) { - requestUnarchiveConfirmation(packageName, statusReceiver); + if (!hasInstallPackages || showUnarchivalConfirmation) { + requestUnarchiveConfirmation(packageName, statusReceiver, userHandle); return; } @@ -622,7 +653,8 @@ public class PackageArchiver { () -> unarchiveInternal(packageName, userHandle, installerPackage, draftSessionId)); } - private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver) { + private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver, + UserHandle user) { final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_DIALOG); dialogIntent.putExtra(EXTRA_UNARCHIVE_INTENT_SENDER, statusReceiver); dialogIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName); @@ -632,6 +664,7 @@ public class PackageArchiver { broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION); broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent); + broadcastIntent.putExtra(Intent.EXTRA_USER, user); sendIntent(statusReceiver, packageName, /* message= */ "", broadcastIntent); } @@ -656,6 +689,7 @@ public class PackageArchiver { int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId); // Handles case of repeated unarchival calls for the same package. + // TODO(b/316881759) Allow attaching multiple intentSenders to one session. int existingSessionId = mPm.mInstallerService.getExistingDraftSessionId(installerUid, sessionParams, userId); @@ -849,7 +883,13 @@ public class PackageArchiver { void notifyUnarchivalListener(int status, String installerPackageName, String appPackageName, long requiredStorageBytes, @Nullable PendingIntent userActionIntent, - IntentSender unarchiveIntentSender, int userId) { + @Nullable IntentSender unarchiveIntentSender, int userId) { + if (unarchiveIntentSender == null) { + // Maybe this can happen if the installer calls reportUnarchivalStatus twice in quick + // succession. + return; + } + final Intent broadcastIntent = new Intent(); broadcastIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, appPackageName); broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status); @@ -863,6 +903,7 @@ public class PackageArchiver { return; } broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent); + broadcastIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); } final BroadcastOptions options = BroadcastOptions.makeBasic(); @@ -874,6 +915,10 @@ public class PackageArchiver { options.toBundle()); } catch (IntentSender.SendIntentException e) { Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e); + } finally { + synchronized (mLauncherIntentSenders) { + mLauncherIntentSenders.remove(Pair.create(userId, appPackageName)); + } } } @@ -883,6 +928,7 @@ public class PackageArchiver { long requiredStorageBytes, PendingIntent userActionIntent, int userId) { final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_ERROR_DIALOG); dialogIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status); + dialogIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); if (requiredStorageBytes > 0) { dialogIntent.putExtra(EXTRA_REQUIRED_BYTES, requiredStorageBytes); } @@ -1118,4 +1164,25 @@ public class PackageArchiver { return activities.toArray(new ArchivedActivityParcel[activities.size()]); } + + private class UnarchiveIntentSender extends IIntentSender.Stub { + @Override + public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) + throws RemoteException { + int status = intent.getExtras().getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS, + STATUS_PENDING_USER_ACTION); + if (status == UNARCHIVAL_OK) { + return; + } + Intent extraIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class); + UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class); + if (extraIntent != null && user != null + && mAppStateHelper.isAppTopVisible( + getCurrentLauncherPackageName(user.getIdentifier()))) { + extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivityAsUser(extraIntent, user); + } + } + } } diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index 02e28ddf24c7..7bd6a43969ba 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -487,6 +487,8 @@ final class RemovePackageHelper { synchronized (mPm.mInstallLock) { cleanUpResourcesLI(codeFile, instructionSets); } + // TODO: open logging to help debug, will delete or add debug flag + Slog.d(TAG, "cleanUpResources for " + codeFile); if (packageName == null) { return; } diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 3ecc9853be91..fdff89af64a3 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -958,7 +958,8 @@ public class Notifier { final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0; if (vibrate) { - mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, + mVibrator.vibrate(Process.SYSTEM_UID, mContext.getOpPackageName(), + CHARGING_VIBRATION_EFFECT, /* reason= */ "Charging started", HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 1577cef9de00..2d584c428d4c 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -98,6 +98,8 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.util.TraceBuffer; import com.android.internal.util.function.pooled.PooledLambda; @@ -297,6 +299,18 @@ final class AccessibilityController { } } + /** It is only used by unit test. */ + @VisibleForTesting + Surface forceShowMagnifierSurface(int displayId) { + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.mMagnifedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport + .ViewportWindow.AnimationController.MAX_ALPHA); + return displayMagnifier.mMagnifedViewport.mWindow.mSurface; + } + return null; + } + void onWindowLayersChanged(int displayId) { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { @@ -448,6 +462,7 @@ final class AccessibilityController { } } + // TODO(b/318327737): Remove parameter 't' when removing flag DRAW_IN_WM_LOCK. void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { mAccessibilityTracing.logTrace( @@ -1106,11 +1121,19 @@ final class AccessibilityController { } void setMagnifiedRegionBorderShown(boolean shown, boolean animate) { - if (shown) { + if (ViewportWindow.DRAW_IN_WM_LOCK) { + if (shown) { + mFullRedrawNeeded = true; + mOldMagnificationRegion.set(0, 0, 0, 0); + } + mWindow.setShown(shown, animate); + return; + } + if (mWindow.setShown(shown, animate)) { mFullRedrawNeeded = true; + // Clear the old region, so recomputeBounds will refresh the current region. mOldMagnificationRegion.set(0, 0, 0, 0); } - mWindow.setShown(shown, animate); } void getMagnifiedFrameInContentCoords(Rect rect) { @@ -1130,7 +1153,11 @@ final class AccessibilityController { void drawWindowIfNeeded(SurfaceControl.Transaction t) { recomputeBounds(); - mWindow.drawIfNeeded(t); + if (ViewportWindow.DRAW_IN_WM_LOCK) { + mWindow.drawOrRemoveIfNeeded(t); + return; + } + mWindow.postDrawIfNeeded(); } void destroyWindow() { @@ -1158,23 +1185,28 @@ final class AccessibilityController { mWindow.dump(pw, prefix); } - private final class ViewportWindow { + private final class ViewportWindow implements Runnable { private static final String SURFACE_TITLE = "Magnification Overlay"; + // TODO(b/318327737): Remove if it is stable. + static final boolean DRAW_IN_WM_LOCK = !Flags.drawMagnifierBorderOutsideWmlock(); private final Region mBounds = new Region(); private final Rect mDirtyRect = new Rect(); private final Paint mPaint = new Paint(); private final SurfaceControl mSurfaceControl; + /** After initialization, it should only be accessed from animation thread. */ + private final SurfaceControl.Transaction mTransaction; private final BLASTBufferQueue mBlastBufferQueue; private final Surface mSurface; private final AnimationController mAnimationController; private boolean mShown; + private boolean mLastSurfaceShown; private int mAlpha; - private boolean mInvalidated; + private volatile boolean mInvalidated; ViewportWindow(Context context) { SurfaceControl surfaceControl = null; @@ -1202,6 +1234,7 @@ final class AccessibilityController { InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t, mDisplayContent.getDisplayId(), "Magnification Overlay"); t.apply(); + mTransaction = t; mSurface = mBlastBufferQueue.createSurface(); mAnimationController = new AnimationController(context, @@ -1219,10 +1252,11 @@ final class AccessibilityController { mInvalidated = true; } - void setShown(boolean shown, boolean animate) { + /** Returns {@code true} if the state is changed to shown. */ + boolean setShown(boolean shown, boolean animate) { synchronized (mService.mGlobalLock) { if (mShown == shown) { - return; + return false; } mShown = shown; mAnimationController.onFrameShownStateChanged(shown, animate); @@ -1230,6 +1264,7 @@ final class AccessibilityController { Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown); } } + return shown; } @SuppressWarnings("unused") @@ -1285,7 +1320,22 @@ final class AccessibilityController { mService.scheduleAnimationLocked(); } - void drawIfNeeded(SurfaceControl.Transaction t) { + void postDrawIfNeeded() { + if (mInvalidated) { + mService.mAnimationHandler.post(this); + } + } + + @Override + public void run() { + drawOrRemoveIfNeeded(mTransaction); + } + + /** + * This method must only be called by animation handler directly to make sure + * thread safe and there is no lock held outside. + */ + private void drawOrRemoveIfNeeded(SurfaceControl.Transaction t) { // Drawing variables (alpha, dirty rect, and bounds) access is synchronized // using WindowManagerGlobalLock. Grab copies of these values before // drawing on the canvas so that drawing can be performed outside of the lock. @@ -1293,6 +1343,14 @@ final class AccessibilityController { Rect drawingRect = null; Region drawingBounds = null; synchronized (mService.mGlobalLock) { + if (!DRAW_IN_WM_LOCK && mBlastBufferQueue.mNativeObject == 0) { + // Complete removal since releaseSurface has been called. + if (mSurface.isValid()) { + mTransaction.remove(mSurfaceControl).apply(); + mSurface.release(); + } + return; + } if (!mInvalidated) { return; } @@ -1314,6 +1372,7 @@ final class AccessibilityController { } } + final boolean showSurface; // Draw without holding WindowManagerGlobalLock. if (alpha > 0) { Canvas canvas = null; @@ -1329,18 +1388,38 @@ final class AccessibilityController { mPaint.setAlpha(alpha); canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint); mSurface.unlockCanvasAndPost(canvas); - t.show(mSurfaceControl); + if (DRAW_IN_WM_LOCK) { + t.show(mSurfaceControl); + return; + } + showSurface = true; } else { - t.hide(mSurfaceControl); + if (DRAW_IN_WM_LOCK) { + t.hide(mSurfaceControl); + return; + } + showSurface = false; + } + + if (showSurface && !mLastSurfaceShown) { + mTransaction.show(mSurfaceControl).apply(); + mLastSurfaceShown = true; + } else if (!showSurface && mLastSurfaceShown) { + mTransaction.hide(mSurfaceControl).apply(); + mLastSurfaceShown = false; } } + @GuardedBy("mService.mGlobalLock") void releaseSurface() { - if (mBlastBufferQueue != null) { - mBlastBufferQueue.destroy(); + mBlastBufferQueue.destroy(); + if (DRAW_IN_WM_LOCK) { + mService.mTransactionFactory.get().remove(mSurfaceControl).apply(); + mSurface.release(); + return; } - mService.mTransactionFactory.get().remove(mSurfaceControl).apply(); - mSurface.release(); + // Post to perform cleanup on the thread which handles mSurface. + mService.mAnimationHandler.post(this); } void dump(PrintWriter pw, String prefix) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index e0232b1e1fc5..14dc0ebb8ad8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -32,6 +32,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; import android.app.BroadcastOptions; +import android.app.admin.BooleanPolicyValue; import android.app.admin.DevicePolicyIdentifiers; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyState; @@ -142,6 +143,67 @@ final class DevicePolicyEngine { mAdminPolicySize = new SparseArray<>(); } + private void maybeForceEnforcementRefreshLocked(@NonNull PolicyDefinition<?> policyDefinition) { + try { + if (shouldForceEnforcementRefresh(policyDefinition)) { + // This is okay because it's only true for user restrictions which are all <Boolean> + forceEnforcementRefreshLocked((PolicyDefinition<Boolean>) policyDefinition); + } + } catch (Throwable e) { + // Catch any possible exceptions just to be on the safe side + Log.e(TAG, "Exception throw during maybeForceEnforcementRefreshLocked", e); + } + } + + private boolean shouldForceEnforcementRefresh(@NonNull PolicyDefinition<?> policyDefinition) { + // These are all "not nullable" but for the purposes of maximum safety for a lightly tested + // change we check here + if (policyDefinition == null) { + return false; + } + PolicyKey policyKey = policyDefinition.getPolicyKey(); + if (policyKey == null) { + return false; + } + + if (policyKey instanceof UserRestrictionPolicyKey) { + // b/307481299 We must force all user restrictions to re-sync local + // + global on each set/clear + return true; + } + + return false; + } + + private void forceEnforcementRefreshLocked(PolicyDefinition<Boolean> policyDefinition) { + Binder.withCleanCallingIdentity(() -> { + // Sync global state + PolicyValue<Boolean> globalValue = new BooleanPolicyValue(false); + try { + PolicyState<Boolean> policyState = getGlobalPolicyStateLocked(policyDefinition); + globalValue = policyState.getCurrentResolvedPolicy(); + } catch (IllegalArgumentException e) { + // Expected for local-only policies + } + + enforcePolicy(policyDefinition, globalValue, UserHandle.USER_ALL); + + // Loop through each user and sync that user's state + for (UserInfo user : mUserManager.getUsers()) { + PolicyValue<Boolean> localValue = new BooleanPolicyValue(false); + try { + PolicyState<Boolean> localPolicyState = getLocalPolicyStateLocked( + policyDefinition, user.id); + localValue = localPolicyState.getCurrentResolvedPolicy(); + } catch (IllegalArgumentException e) { + // Expected for global-only policies + } + + enforcePolicy(policyDefinition, localValue, user.id); + } + }); + } + /** * Set the policy for the provided {@code policyDefinition} (see {@link PolicyDefinition}) and * {@code enforcingAdmin} to the provided {@code value}. @@ -188,6 +250,7 @@ final class DevicePolicyEngine { // No need to notify admins as no new policy is actually enforced, we're just filling in // the data structures. if (!skipEnforcePolicy) { + maybeForceEnforcementRefreshLocked(policyDefinition); if (policyChanged) { onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId); } @@ -278,6 +341,7 @@ final class DevicePolicyEngine { Objects.requireNonNull(enforcingAdmin); synchronized (mLock) { + maybeForceEnforcementRefreshLocked(policyDefinition); if (!hasLocalPolicyLocked(policyDefinition, userId)) { return; } @@ -451,6 +515,7 @@ final class DevicePolicyEngine { // No need to notify admins as no new policy is actually enforced, we're just filling in // the data structures. if (!skipEnforcePolicy) { + maybeForceEnforcementRefreshLocked(policyDefinition); if (policyChanged) { onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } @@ -506,6 +571,7 @@ final class DevicePolicyEngine { boolean policyChanged = policyState.removePolicy(enforcingAdmin); + maybeForceEnforcementRefreshLocked(policyDefinition); if (policyChanged) { onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 113511ef8197..321d945e9c15 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -73,6 +73,7 @@ android_test { // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows "testng", "compatibility-device-util-axt", + "flag-junit", ], libs: [ diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java index 57326b2e1e82..d08cdc718a82 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java @@ -343,10 +343,12 @@ public class ApexManagerTest { List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo); mApexManager.notifyScanResult(scanResults); - assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName)).isNull(); + final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName( + activeApex.apexModuleName); + assertThat(mApexManager.getApkInApexInstallError(apexPackageName)).isNull(); mApexManager.reportErrorWithApkInApex(activeApex.apexDirectory.getAbsolutePath(), "Some random error"); - assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName)) + assertThat(mApexManager.getApkInApexInstallError(apexPackageName)) .isEqualTo("Some random error"); } @@ -370,9 +372,11 @@ public class ApexManagerTest { List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo); mApexManager.notifyScanResult(scanResults); - assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty(); + final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName( + activeApex.apexModuleName); + assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty(); mApexManager.registerApkInApex(fakeApkInApex); - assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty(); + assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty(); } @Test diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java index 91d8ceb28ec9..a9ff3a133f6e 100644 --- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.content.Context; @@ -113,7 +114,8 @@ public class NotifierTest { mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device vibrates once - verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class)); + verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(), + any(VibrationAttributes.class)); } @Test @@ -129,7 +131,7 @@ public class NotifierTest { mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device doesn't vibrate - verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class)); + verifyZeroInteractions(mVibrator); } @Test @@ -145,14 +147,15 @@ public class NotifierTest { mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device vibrates once - verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class)); + verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(), + any(VibrationAttributes.class)); } @Test public void testVibrateDisabled_wirelessCharging() { createNotifier(); - // GIVEN the charging vibration is disabeld + // GIVEN the charging vibration is disabled enableChargingVibration(false); // WHEN wireless charging starts @@ -161,7 +164,7 @@ public class NotifierTest { mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device doesn't vibrate - verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class)); + verifyZeroInteractions(mVibrator); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 8bf4833bc2ca..21fee7286a7b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -37,6 +37,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; @@ -85,6 +86,7 @@ import android.view.IWindow; import android.view.InputChannel; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.Surface; import android.view.SurfaceControl; import android.view.View; import android.view.WindowInsets; @@ -952,6 +954,57 @@ public class WindowManagerServiceTests extends WindowTestsBase { } @Test + public void testDrawMagnifiedViewport() { + final int displayId = mDisplayContent.mDisplayId; + // Use real surface, so ViewportWindow's BlastBufferQueue can be created. + final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>(); + mWm.mSurfaceControlFactory = s -> new SurfaceControl.Builder() { + @Override + public SurfaceControl build() { + final SurfaceControl sc = super.build(); + surfaceControls.add(sc); + return sc; + } + }; + mWm.mAccessibilityController.setMagnificationCallbacks(displayId, + mock(WindowManagerInternal.MagnificationCallbacks.class)); + final boolean[] lockCanvasInWmLock = { false }; + final Surface surface = mWm.mAccessibilityController.forceShowMagnifierSurface(displayId); + spyOn(surface); + doAnswer(invocationOnMock -> { + lockCanvasInWmLock[0] |= Thread.holdsLock(mWm.mGlobalLock); + invocationOnMock.callRealMethod(); + return null; + }).when(surface).lockCanvas(any()); + mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction); + waitUntilHandlersIdle(); + try { + verify(surface).lockCanvas(any()); + + clearInvocations(surface); + // Invalidate and redraw. + mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent); + mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction); + // Turn off magnification to release surface. + mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null); + if (!com.android.window.flags.Flags.drawMagnifierBorderOutsideWmlock()) { + verify(surface).release(); + assertTrue(lockCanvasInWmLock[0]); + return; + } + waitUntilHandlersIdle(); + // lockCanvas must not be called after releasing. + verify(surface, never()).lockCanvas(any()); + verify(surface).release(); + assertFalse(lockCanvasInWmLock[0]); + } finally { + for (int i = surfaceControls.size() - 1; i >= 0; --i) { + surfaceControls.get(i).release(); + } + } + } + + @Test public void testRequestKeyboardShortcuts_noWindow() { doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString()); doReturn(null).when(mWm).getFocusedWindowLocked(); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 9e292be7f767..1b47dfe0eba1 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -510,7 +510,7 @@ public class TelephonyManager { /** @hide */ @UnsupportedAppUsage public TelephonyManager(Context context) { - this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); + this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); } /** @hide */ @@ -2140,10 +2140,14 @@ public class TelephonyManager { * the IMEI/SV for GSM phones. Return null if the software version is * not available. * <p> + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. */ @RequiresPermission(anyOf = { android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) @Nullable public String getDeviceSoftwareVersion() { return getDeviceSoftwareVersion(getSlotIndex()); @@ -2158,10 +2162,13 @@ public class TelephonyManager { * * @param slotIndex of which deviceID is returned * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) @Nullable public String getDeviceSoftwareVersion(int slotIndex) { ITelephony telephony = getITelephony(); @@ -2288,6 +2295,9 @@ public class TelephonyManager { * * See {@link #getImei(int)} for details on the required permissions and behavior * when the caller does not hold sufficient permissions. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -2330,6 +2340,9 @@ public class TelephonyManager { * </ul> * * @param slotIndex of which IMEI is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -2350,6 +2363,9 @@ public class TelephonyManager { /** * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not * available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM) @Nullable @@ -2362,6 +2378,9 @@ public class TelephonyManager { * available. * * @param slotIndex of which Type Allocation Code is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM) @Nullable @@ -2407,6 +2426,9 @@ public class TelephonyManager { * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or * higher, then a SecurityException is thrown.</li> * </ul> + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -2446,6 +2468,9 @@ public class TelephonyManager { * </ul> * * @param slotIndex of which MEID is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -2472,6 +2497,9 @@ public class TelephonyManager { /** * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not * available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) @Nullable @@ -2484,6 +2512,9 @@ public class TelephonyManager { * available. * * @param slotIndex of which Type Allocation Code is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) @Nullable @@ -2528,6 +2559,9 @@ public class TelephonyManager { * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or * higher, then a SecurityException is thrown.</li> * </ul> + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -2563,10 +2597,14 @@ public class TelephonyManager { *<p> * @return Current location of the device or null if not available. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API. */ @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public CellLocation getCellLocation() { try { ITelephony telephony = getITelephony(); @@ -2596,12 +2634,15 @@ public class TelephonyManager { * * @return List of NeighboringCellInfo or null if info unavailable. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @removed * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information * from NeighboringCellInfo, including LTE cell information. */ @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public List<NeighboringCellInfo> getNeighboringCellInfo() { try { ITelephony telephony = getITelephony(); @@ -2648,9 +2689,12 @@ public class TelephonyManager { * @see #PHONE_TYPE_CDMA * @see #PHONE_TYPE_SIP * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. * {@hide} */ @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) public int getCurrentPhoneType() { return getCurrentPhoneType(getSubId()); } @@ -2663,9 +2707,13 @@ public class TelephonyManager { * @see #PHONE_TYPE_CDMA * * @param subId for which phone type is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. * @hide */ @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) public int getCurrentPhoneType(int subId) { int phoneId; if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { @@ -2712,7 +2760,11 @@ public class TelephonyManager { * @see #PHONE_TYPE_GSM * @see #PHONE_TYPE_CDMA * @see #PHONE_TYPE_SIP + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) public int getPhoneType() { if (!isVoiceCapable()) { return PHONE_TYPE_NONE; @@ -2912,6 +2964,9 @@ public class TelephonyManager { * @see CarrierConfigManager#getConfigForSubId(int) * @see #createForSubscriptionId(int) * @see #createForPhoneAccountHandle(PhoneAccountHandle) + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @WorkerThread @@ -2958,6 +3013,9 @@ public class TelephonyManager { * <p> * @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string if not * available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public String getNetworkCountryIso() { @@ -2980,6 +3038,8 @@ public class TelephonyManager { * available. * * @throws IllegalArgumentException when the slotIndex is invalid. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -3108,9 +3168,13 @@ public class TelephonyManager { * * @deprecated use {@link #getDataNetworkType()} * @return the NETWORK_TYPE_xxxx for current data connection. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public @NetworkType int getNetworkType() { return getNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); } @@ -3199,12 +3263,15 @@ public class TelephonyManager { * @see #NETWORK_TYPE_EHRPD * @see #NETWORK_TYPE_HSPAP * @see #NETWORK_TYPE_NR + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(anyOf = { android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) - @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public @NetworkType int getDataNetworkType() { return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); } @@ -3245,6 +3312,9 @@ public class TelephonyManager { * or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE * READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges * (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(anyOf = { @@ -3597,6 +3667,9 @@ public class TelephonyManager { * of whether an active SIM profile is present or not so this API would always return true. * * @return true if a ICC card is present. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public boolean hasIccCard() { @@ -3640,6 +3713,9 @@ public class TelephonyManager { * @see #SIM_STATE_PERM_DISABLED * @see #SIM_STATE_CARD_IO_ERROR * @see #SIM_STATE_CARD_RESTRICTED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public @SimState int getSimState() { @@ -3681,6 +3757,8 @@ public class TelephonyManager { * @see #SIM_STATE_CARD_RESTRICTED * @see #SIM_STATE_PRESENT * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -3701,11 +3779,14 @@ public class TelephonyManager { * @see #SIM_STATE_CARD_RESTRICTED * @see #SIM_STATE_PRESENT * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide * @deprecated instead use {@link #getSimCardState(int, int)} */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @Deprecated public @SimState int getSimCardState(int physicalSlotIndex) { int activePort = getFirstActivePortIndex(physicalSlotIndex); @@ -3727,6 +3808,8 @@ public class TelephonyManager { * @see #SIM_STATE_CARD_RESTRICTED * @see #SIM_STATE_PRESENT * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -3785,6 +3868,8 @@ public class TelephonyManager { * @see #SIM_STATE_PERM_DISABLED * @see #SIM_STATE_LOADED * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -3808,11 +3893,14 @@ public class TelephonyManager { * @see #SIM_STATE_PERM_DISABLED * @see #SIM_STATE_LOADED * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide * @deprecated instead use {@link #getSimApplicationState(int, int)} */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @Deprecated public @SimState int getSimApplicationState(int physicalSlotIndex) { int activePort = getFirstActivePortIndex(physicalSlotIndex); @@ -3836,6 +3924,8 @@ public class TelephonyManager { * @see #SIM_STATE_PERM_DISABLED * @see #SIM_STATE_LOADED * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -3876,6 +3966,9 @@ public class TelephonyManager { * * @param appType the uicc app type like {@link APPTYPE_CSIM} * @return true if the specified type of application in UICC CARD or false if no uicc or error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -3908,6 +4001,9 @@ public class TelephonyManager { * @see #SIM_STATE_PERM_DISABLED * @see #SIM_STATE_CARD_IO_ERROR * @see #SIM_STATE_CARD_RESTRICTED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public @SimState int getSimState(int slotIndex) { @@ -4105,6 +4201,9 @@ public class TelephonyManager { * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or * higher, then a SecurityException is thrown.</li> * </ul> + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -4172,6 +4271,9 @@ public class TelephonyManager { * * @return {@code true} if 3GPP and 3GPP2 radio technologies can be supported at the same time * {@code false} if not supported or unknown + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -4219,6 +4321,9 @@ public class TelephonyManager { * through a factory reset. * * @return card ID of the default eUICC card, if loaded. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC) public int getCardIdForDefaultEuicc() { @@ -4252,6 +4357,9 @@ public class TelephonyManager { * @return a list of UiccCardInfo objects, representing information on the currently inserted * UICCs and eUICCs. Each UiccCardInfo in the list will have private information filtered out if * the caller does not have adequate permissions for that card. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -4276,6 +4384,8 @@ public class TelephonyManager { * * @return UiccSlotInfo array. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -4319,6 +4429,9 @@ public class TelephonyManager { * @param physicalSlots The content of the array represents the physical slot index. The array * size should be same as {@link #getUiccSlotsInfo()}. * @return boolean Return true if the switch succeeds, false if the switch fails. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide * @deprecated {@link #setSimSlotMapping(Collection, Executor, Consumer)} */ @@ -4328,6 +4441,7 @@ public class TelephonyManager { @SystemApi @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public boolean switchSlots(int[] physicalSlots) { try { ITelephony telephony = getITelephony(); @@ -4420,6 +4534,8 @@ public class TelephonyManager { * @throws IllegalArgumentException if the caller passes in an invalid collection of * UiccSlotMapping like duplicate data, etc * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -4452,11 +4568,14 @@ public class TelephonyManager { * @return a map indicates the mapping from logical slots to physical slots. The size of the map * should be {@link #getPhoneCount()} if success, otherwise return an empty map. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide * @deprecated use {@link #getSimSlotMapping()} instead. */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @NonNull @Deprecated public Map<Integer, Integer> getLogicalToPhysicalSlotMapping() { @@ -4484,6 +4603,9 @@ public class TelephonyManager { * * @return a collection of {@link UiccSlotMapping} which indicates the mapping from logical * slots to ports and physical slots. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -4541,6 +4663,9 @@ public class TelephonyManager { * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or * higher, then a SecurityException is thrown.</li> * </ul> + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -4593,6 +4718,8 @@ public class TelephonyManager { * found, and the carrier does not require a key. * @throws IllegalArgumentException when an invalid key is found or when key is required but * not found. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -4638,6 +4765,9 @@ public class TelephonyManager { * Requires Permission: MODIFY_PHONE_STATE. * * @see #getCarrierInfoForImsiEncryption + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -4840,6 +4970,9 @@ public class TelephonyManager { * from disk, as well as on which {@code callback} will be called. * @param callback A callback called when the upload operation terminates, either in success * or in error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public void uploadCallComposerPicture(@NonNull Path pictureToUpload, @@ -4947,6 +5080,9 @@ public class TelephonyManager { * read, as well as on which the callback will be called. * @param callback A callback called when the upload operation terminates, either in success * or in error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload, @@ -5081,6 +5217,9 @@ public class TelephonyManager { * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @@ -5139,6 +5278,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * for apps targeting SDK API level 29 and below. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead. */ @Deprecated @@ -5148,6 +5289,7 @@ public class TelephonyManager { android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public String getLine1Number() { return getLine1Number(getSubId()); } @@ -5214,9 +5356,13 @@ public class TelephonyManager { * @param alphaTag alpha-tagging of the dailing nubmer * @param number The dialing number * @return true if the operation was executed correctly. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @deprecated use {@link SubscriptionManager#setCarrierPhoneNumber(int, String)} instead. */ @Deprecated + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public boolean setLine1NumberForDisplay(String alphaTag, String number) { return setLine1NumberForDisplay(getSubId(), alphaTag, number); } @@ -5336,6 +5482,8 @@ public class TelephonyManager { * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group, * otherwise return an empty array if there is a failure. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -5421,6 +5569,9 @@ public class TelephonyManager { * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @@ -5459,6 +5610,9 @@ public class TelephonyManager { * * @param alphaTag The alpha tag to display. * @param number The voicemail number. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public boolean setVoiceMailNumber(String alphaTag, String number) { @@ -5533,6 +5687,8 @@ public class TelephonyManager { * @see #KEY_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL * @see #KEY_VOICEMAIL_SCRAMBLED_PIN_STRING * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @SystemApi @@ -5563,6 +5719,9 @@ public class TelephonyManager { * @see #createForSubscriptionId(int) * @see #createForPhoneAccountHandle(PhoneAccountHandle) * @see VisualVoicemailService + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @Nullable @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @@ -5593,6 +5752,9 @@ public class TelephonyManager { * * @see TelecomManager#getDefaultDialerPackage() * @see CarrierConfigManager#KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public void setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings settings) { @@ -5623,6 +5785,9 @@ public class TelephonyManager { * * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent) * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent) + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public void sendVisualVoicemailSms(String number, int port, String text, @@ -5808,6 +5973,9 @@ public class TelephonyManager { * @see #SIM_ACTIVATION_STATE_ACTIVATING * @see #SIM_ACTIVATION_STATE_ACTIVATED * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING} * @hide */ @SystemApi @@ -5856,6 +6024,9 @@ public class TelephonyManager { * @see #SIM_ACTIVATION_STATE_ACTIVATED * @see #SIM_ACTIVATION_STATE_DEACTIVATED * @see #SIM_ACTIVATION_STATE_RESTRICTED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @SystemApi @@ -5904,6 +6075,9 @@ public class TelephonyManager { * @see #SIM_ACTIVATION_STATE_ACTIVATING * @see #SIM_ACTIVATION_STATE_ACTIVATED * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @SystemApi @@ -5954,6 +6128,9 @@ public class TelephonyManager { * @see #SIM_ACTIVATION_STATE_ACTIVATED * @see #SIM_ACTIVATION_STATE_DEACTIVATED * @see #SIM_ACTIVATION_STATE_RESTRICTED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @SystemApi @@ -6032,6 +6209,9 @@ public class TelephonyManager { * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @@ -6072,7 +6252,10 @@ public class TelephonyManager { * * @throws SecurityException if the caller does not have carrier privileges or is not the * current default dialer + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public void sendDialerSpecialCode(String inputCode) { try { final ITelephony telephony = getITelephony(); @@ -6143,6 +6326,9 @@ public class TelephonyManager { * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @Nullable @@ -6168,6 +6354,9 @@ public class TelephonyManager { * Returns the IMS public user identities (IMPU) that were loaded from the ISIM. * @return an array of IMPU strings, with one IMPU per string, or null if * not present or not loaded + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide * @deprecated use {@link #getImsPublicUserIdentities()} */ @@ -6199,6 +6388,8 @@ public class TelephonyManager { * EF_IMPU is not available. * @throws IllegalStateException in case the ISIM hasn’t been loaded * @throws SecurityException if the caller does not have the required permission/privilege + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @NonNull @@ -6254,6 +6445,10 @@ public class TelephonyManager { * targeting API level 31+. * * @return the current call state. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELECOM}. + * * @deprecated Use {@link #getCallStateForSubscription} to retrieve the call state for a * specific telephony subscription (which allows carrier privileged apps), * {@link TelephonyCallback.CallStateListener} for real-time call state updates, or @@ -6261,6 +6456,7 @@ public class TelephonyManager { * device. */ @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true) + @RequiresFeature(PackageManager.FEATURE_TELECOM) @Deprecated public @CallState int getCallState() { if (mContext != null) { @@ -6281,6 +6477,9 @@ public class TelephonyManager { * @see TelephonyManager#createForSubscriptionId(int) * @see TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle) * @return The call state of the subscription associated with this TelephonyManager instance. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) @@ -6341,6 +6540,9 @@ public class TelephonyManager { * @see #DATA_ACTIVITY_OUT * @see #DATA_ACTIVITY_INOUT * @see #DATA_ACTIVITY_DORMANT + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public int getDataActivity() { @@ -6414,6 +6616,9 @@ public class TelephonyManager { * @see #DATA_SUSPENDED * @see #DATA_DISCONNECTING * @see #DATA_HANDOVER_IN_PROGRESS + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public int getDataState() { @@ -6599,10 +6804,14 @@ public class TelephonyManager { * Returns the CDMA ERI icon display number. The number is assigned by * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers. * Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) public @EriIconIndex int getCdmaEnhancedRoamingIndicatorDisplayNumber() { return getCdmaEriIconIndex(getSubId()); } @@ -6810,6 +7019,9 @@ public class TelephonyManager { * * @return List of {@link android.telephony.CellInfo}; null if cell * information is unavailable. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -6905,6 +7117,9 @@ public class TelephonyManager { * * @param executor the executor on which callback will be invoked. * @param callback a callback to receive CellInfo. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -6966,6 +7181,9 @@ public class TelephonyManager { * @param workSource the requestor to whom the power consumption for this should be attributed. * @param executor the executor on which callback will be invoked. * @param callback a callback to receive CellInfo. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -7052,6 +7270,9 @@ public class TelephonyManager { /** * Returns the MMS user agent. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) public String getMmsUserAgent() { @@ -7068,6 +7289,9 @@ public class TelephonyManager { /** * Returns the MMS user agent profile URL. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) public String getMmsUAProfUrl() { @@ -7111,8 +7335,12 @@ public class TelephonyManager { * * @param AID Application id. See ETSI 102.221 and 101.220. * @return an IccOpenLogicalChannelResponse object. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @deprecated Replaced by {@link #iccOpenLogicalChannel(String, int)} */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @Deprecated public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID) { return iccOpenLogicalChannel(getSubId(), AID, -1); @@ -7145,6 +7373,8 @@ public class TelephonyManager { * @param aid Application id. See ETSI 102.221 and 101.220. * @param p2 P2 parameter (described in ISO 7816-4). * @return an IccOpenLogicalChannelResponse object. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP), * instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)} @@ -7200,9 +7430,13 @@ public class TelephonyManager { * @param aid Application id. See ETSI 102.221 and 101.220. * @param p2 P2 parameter (described in ISO 7816-4). * @return an IccOpenLogicalChannelResponse object. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @SystemApi @NonNull public IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int slotIndex, @@ -7255,6 +7489,9 @@ public class TelephonyManager { * @param AID Application id. See ETSI 102.221 and 101.220. * @param p2 P2 parameter (described in ISO 7816-4). * @return an IccOpenLogicalChannelResponse object. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) { @@ -7321,6 +7558,9 @@ public class TelephonyManager { * @param channel is the channel id to be closed as returned by a successful * iccOpenLogicalChannel. * @return true if the channel was closed successfully. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP), * instead use {@link #iccCloseLogicalChannelByPort(int, int, int)} @@ -7365,9 +7605,12 @@ public class TelephonyManager { * @throws IllegalStateException if the Telephony process is not currently available or modem * currently can't process this command. * @throws IllegalArgumentException if invalid arguments are passed. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @SystemApi public void iccCloseLogicalChannelByPort(int slotIndex, int portIndex, int channel) { try { @@ -7403,6 +7646,8 @@ public class TelephonyManager { * iccOpenLogicalChannel. * @return true if the channel was closed successfully. * @throws IllegalArgumentException if input parameters are wrong. e.g., invalid channel + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public boolean iccCloseLogicalChannel(int channel) { @@ -7469,6 +7714,8 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at the end, or null if * there is an issue connecting to the Telephony service. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP), * instead use @@ -7516,9 +7763,13 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at the end, or null if * there is an issue connecting to the Telephony service. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @SystemApi @NonNull public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel, @@ -7563,6 +7814,9 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at * the end. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public String iccTransmitApduLogicalChannel(int channel, int cla, @@ -7628,6 +7882,9 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at * the end. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP), * instead use @@ -7673,9 +7930,13 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at * the end. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @SystemApi @NonNull public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex, int cla, @@ -7712,6 +7973,9 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at * the end. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public String iccTransmitApduBasicChannel(int cla, @@ -7768,6 +8032,9 @@ public class TelephonyManager { * @param p3 P3 value of the APDU command. * @param filePath * @return The APDU response. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3, @@ -7817,6 +8084,9 @@ public class TelephonyManager { * @return The APDU response from the ICC card in hexadecimal format * with the last 4 bytes being the status word. If the command fails, * returns an empty string. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public String sendEnvelopeWithStatus(String content) { @@ -7978,6 +8248,8 @@ public class TelephonyManager { * * @return {@code true} on success; {@code false} on any failure. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @@ -8008,6 +8280,8 @@ public class TelephonyManager { * * @deprecated Using {@link #rebootModem()} instead. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @@ -8035,6 +8309,8 @@ public class TelephonyManager { * app has carrier privileges (see {@link #hasCarrierPrivileges}). * @throws IllegalStateException if the Telephony process is not currently available. * @throws RuntimeException + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -8166,6 +8442,9 @@ public class TelephonyManager { * {@link #getMaxNumberVerificationTimeoutMillis()}, whichever is lesser. * @param executor The {@link Executor} that callbacks should be executed on. * @param callback The callback to use for delivering results. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @@ -8378,6 +8657,9 @@ public class TelephonyManager { * See 3GPP TS 31.103 (Section 4.2.7) for the definition and more information on this table. * * @return IMS Service Table or null if not present or not loaded + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @Nullable @@ -8496,6 +8778,9 @@ public class TelephonyManager { * Key freshness failure * Authentication error, no memory space available * Authentication error, no memory space available in EFMUK + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since @@ -8552,6 +8837,9 @@ public class TelephonyManager { * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @return an array of forbidden PLMNs or null if not available + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @@ -8602,6 +8890,9 @@ public class TelephonyManager { * @return number of PLMNs that were successfully written to the SIM FPLMN list. * This may be less than the number of PLMNs passed in where the SIM file does not have enough * room for all of the values passed in. Return -1 in the event of an unexpected failure + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -8637,6 +8928,8 @@ public class TelephonyManager { * @param appType of type int of either {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}. * @return HexString represents sim service table else null. * @throws SecurityException if the caller does not have the required permission/privileges + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @@ -8673,6 +8966,9 @@ public class TelephonyManager { * state. * * @param slotIndex the sim slot to reset the IMS stack on. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @SystemApi @WorkerThread @@ -9084,12 +9380,15 @@ public class TelephonyManager { * * @return The bitmask of preferred network types. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide * @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead. */ @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public @NetworkTypeBitMask long getPreferredNetworkTypeBitmask() { return getAllowedNetworkTypesBitmask(); } @@ -9108,6 +9407,8 @@ public class TelephonyManager { * * @return The bitmask of allowed network types. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -9133,10 +9434,14 @@ public class TelephonyManager { * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @return the allowed network type bitmask + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide * @deprecated Use {@link #getAllowedNetworkTypesForReason} instead. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @SystemApi @Deprecated public @NetworkTypeBitMask long getAllowedNetworkTypes() { @@ -9161,6 +9466,9 @@ public class TelephonyManager { * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -9247,6 +9555,9 @@ public class TelephonyManager { * tasks one at a time in serial order. * @param callback Returns network scan results or errors. * @return A NetworkScan obj which contains a callback which can be used to stop the scan. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(allOf = { @@ -9295,11 +9606,15 @@ public class TelephonyManager { * tasks one at a time in serial order. * @param callback Returns network scan results or errors. * @return A NetworkScan obj which contains a callback which can be used to stop the scan. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(allOf = { android.Manifest.permission.MODIFY_PHONE_STATE }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public @Nullable NetworkScan requestNetworkScan( @IncludeLocationData int includeLocationData, @NonNull NetworkScanRequest request, @@ -9317,6 +9632,9 @@ public class TelephonyManager { } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * * @deprecated * Use {@link * #requestNetworkScan(NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)} @@ -9327,6 +9645,7 @@ public class TelephonyManager { android.Manifest.permission.MODIFY_PHONE_STATE, Manifest.permission.ACCESS_FINE_LOCATION }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public NetworkScan requestNetworkScan( NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) { return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback); @@ -9347,6 +9666,9 @@ public class TelephonyManager { * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume * normal network selection next time. * @return {@code true} on success; {@code false} on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -9378,6 +9700,9 @@ public class TelephonyManager { * {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select * the next best RAN for network registration. * @return {@code true} on success; {@code false} on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -9430,6 +9755,9 @@ public class TelephonyManager { * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @return the network selection mode. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // No support for carrier privileges (b/72967236). @RequiresPermission(anyOf = { @@ -9459,6 +9787,9 @@ public class TelephonyManager { * (see {@link #hasCarrierPrivileges}) * * @return manually selected network info on success or empty string on failure + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // No support carrier privileges (b/72967236). @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @@ -9488,6 +9819,8 @@ public class TelephonyManager { * * @return {@code true} if this device is in emergency SMS mode, {@code false} otherwise. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. * @hide */ @SystemApi @@ -9557,11 +9890,15 @@ public class TelephonyManager { * * @param networkTypeBitmask The bitmask of preferred network types. * @return true on success; false on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead. */ @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @SystemApi public boolean setPreferredNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) { try { @@ -9603,6 +9940,10 @@ public class TelephonyManager { * * @param allowedNetworkTypes The bitmask of allowed network types. * @return true on success; false on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * * @hide * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead with reason * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}. @@ -9690,11 +10031,16 @@ public class TelephonyManager { * @throws IllegalStateException if the Telephony process is not currently available. * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. * @throws SecurityException if the caller does not have the required privileges or if the + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * caller tries to use one of the following security-based reasons without * {@link android.Manifest.permission#MODIFY_PHONE_STATE} permissions. * <ol> * <li>{@code TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}</li> * </ol> + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature( @@ -9734,6 +10080,8 @@ public class TelephonyManager { * @throws IllegalStateException if the Telephony process is not currently available. * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. * @throws SecurityException if the caller does not have the required permission/privileges + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature( @@ -9808,6 +10156,9 @@ public class TelephonyManager { * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @return true on success; false on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public boolean setPreferredNetworkTypeToGlobal() { @@ -9833,6 +10184,9 @@ public class TelephonyManager { * Requires Permission: MODIFY_PHONE_STATE. * * @return {@code true} if DUN APN is required for tethering. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -9886,6 +10240,9 @@ public class TelephonyManager { * is a superset of the checks done in SubscriptionManager#canManageSubscription. * * @return true if the app has carrier privileges. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public boolean hasCarrierPrivileges() { @@ -9933,6 +10290,9 @@ public class TelephonyManager { * * @param brand The brand name to display/set. * @return true if the operation was executed correctly. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public boolean setOperatorBrandOverride(String brand) { @@ -10034,7 +10394,11 @@ public class TelephonyManager { * Expose the rest of ITelephony to @SystemApi */ - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) @@ -10042,7 +10406,11 @@ public class TelephonyManager { return getCdmaMdn(getSubId()); } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) @@ -10059,7 +10427,11 @@ public class TelephonyManager { } } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) @@ -10067,7 +10439,11 @@ public class TelephonyManager { return getCdmaMin(getSubId()); } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) @@ -10084,7 +10460,11 @@ public class TelephonyManager { } } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -10101,7 +10481,11 @@ public class TelephonyManager { return CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -10118,14 +10502,22 @@ public class TelephonyManager { return CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ @SystemApi @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public List<String> getCarrierPackageNamesForIntent(Intent intent) { return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId()); } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -10152,10 +10544,13 @@ public class TelephonyManager { * @return The system-selected package that provides the {@link CarrierService} implementation * for the current subscription, or {@code null} if none is resolved * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public @Nullable String getCarrierServicePackageName() { return getCarrierServicePackageNameForLogicalSlot(getPhoneId()); } @@ -10169,10 +10564,13 @@ public class TelephonyManager { * @return The system-selected package that provides the {@link CarrierService} implementation * for the slot, or {@code null} if none is resolved * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) { try { ITelephony telephony = getITelephony(); @@ -10206,6 +10604,8 @@ public class TelephonyManager { /** * Get the names of packages with carrier privileges for all the active subscriptions. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -10256,6 +10656,8 @@ public class TelephonyManager { * * @throws IllegalArgumentException if requested state is invalid. * @throws SecurityException if the caller does not have the permission. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) @@ -10285,6 +10687,9 @@ public class TelephonyManager { * * @return the user-set status for enriched calling with call composer, either of * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) @@ -10301,7 +10706,11 @@ public class TelephonyManager { return CALL_COMPOSER_STATUS_OFF; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ @SystemApi @SuppressLint("RequiresPermission") @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) @@ -10316,6 +10725,9 @@ public class TelephonyManager { } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * * @deprecated Use {@link android.telecom.TelecomManager#placeCall(Uri address, * Bundle extras)} instead. * @hide @@ -10323,6 +10735,7 @@ public class TelephonyManager { @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.CALL_PHONE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public void call(String callingPackage, String number) { try { ITelephony telephony = getITelephony(); @@ -10369,6 +10782,8 @@ public class TelephonyManager { } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELECOM}. * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead * @hide */ @@ -10378,12 +10793,15 @@ public class TelephonyManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE }) + @RequiresFeature(PackageManager.FEATURE_TELECOM) public boolean isOffhook() { TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE); return tm.isInCall(); } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELECOM}. * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead * @hide */ @@ -10393,12 +10811,15 @@ public class TelephonyManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE }) + @RequiresFeature(PackageManager.FEATURE_TELECOM) public boolean isRinging() { TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE); return tm.isRinging(); } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELECOM}. * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead * @hide */ @@ -10408,12 +10829,15 @@ public class TelephonyManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE }) + @RequiresFeature(PackageManager.FEATURE_TELECOM) public boolean isIdle() { TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE); return !tm.isInCall(); } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @deprecated Use {@link android.telephony.TelephonyManager#getServiceState} instead * @hide */ @@ -10423,6 +10847,7 @@ public class TelephonyManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public boolean isRadioOn() { try { ITelephony telephony = getITelephony(); @@ -10434,7 +10859,11 @@ public class TelephonyManager { return false; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -10449,7 +10878,11 @@ public class TelephonyManager { return false; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -10465,11 +10898,15 @@ public class TelephonyManager { } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * * @deprecated use {@link #supplyIccLockPin(String)} instead. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @Deprecated public int[] supplyPinReportResult(String pin) { try { @@ -10483,11 +10920,15 @@ public class TelephonyManager { } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * * @deprecated use {@link #supplyIccLockPuk(String, String)} instead. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @Deprecated public int[] supplyPukReportResult(String puk, String pin) { try { @@ -10513,6 +10954,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -10549,6 +10992,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -10622,6 +11067,9 @@ public class TelephonyManager { * @param callback called by the framework to inform the caller of the result of executing the * USSD request (see {@link UssdResponseCallback}). * @param handler the {@link Handler} to run the request on. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresPermission(android.Manifest.permission.CALL_PHONE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -10666,6 +11114,9 @@ public class TelephonyManager { * voice and data simultaneously. This can change based on location or network condition. * * @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public boolean isConcurrentVoiceAndDataSupported() { @@ -10679,9 +11130,13 @@ public class TelephonyManager { return false; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public boolean handlePinMmi(String dialString) { try { ITelephony telephony = getITelephony(); @@ -10693,9 +11148,14 @@ public class TelephonyManager { return false; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public boolean handlePinMmiForSubscriber(int subId, String dialString) { try { ITelephony telephony = getITelephony(); @@ -10707,7 +11167,11 @@ public class TelephonyManager { return false; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -10725,6 +11189,8 @@ public class TelephonyManager { * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and * {@link clearRadioPowerOffForReason}. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @Deprecated @@ -10752,6 +11218,8 @@ public class TelephonyManager { * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and * {@link clearRadioPowerOffForReason}. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @Deprecated @@ -10799,6 +11267,8 @@ public class TelephonyManager { * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission. * @throws IllegalStateException if the Telephony service is not currently available. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -10828,6 +11298,8 @@ public class TelephonyManager { * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission. * @throws IllegalStateException if the Telephony service is not currently available. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -10857,6 +11329,8 @@ public class TelephonyManager { * @throws SecurityException if the caller does not have READ_PRIVILEGED_PHONE_STATE permission. * @throws IllegalStateException if the Telephony service is not currently available. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -10886,6 +11360,8 @@ public class TelephonyManager { * <p>To know when the radio has completed powering off, use * {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -10907,6 +11383,9 @@ public class TelephonyManager { * Check if any radio is on over all the slot indexes. * * @return {@code true} if any radio is on over any slot index. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -10953,6 +11432,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -10982,7 +11463,11 @@ public class TelephonyManager { Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()"); } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) @@ -10997,7 +11482,11 @@ public class TelephonyManager { return false; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) @@ -11012,7 +11501,11 @@ public class TelephonyManager { return false; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ @SystemApi @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public boolean isDataConnectivityPossible() { @@ -11027,7 +11520,11 @@ public class TelephonyManager { return false; } - /** @hide */ + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ @SystemApi @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public boolean needsOtaServiceProvisioning() { @@ -11078,23 +11575,29 @@ public class TelephonyManager { * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @param enable Whether to enable mobile data. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @deprecated use setDataEnabledForReason with reason DATA_ENABLED_REASON_USER instead. * */ @Deprecated @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public void setDataEnabled(boolean enable) { setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable); } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide * @deprecated use {@link #setDataEnabledForReason(int, boolean)} instead. */ @SystemApi @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public void setDataEnabled(int subId, boolean enable) { try { setDataEnabledForReason(subId, DATA_ENABLED_REASON_USER, enable); @@ -11105,10 +11608,14 @@ public class TelephonyManager { /** * @deprecated use {@link #isDataEnabled()} instead. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @SystemApi @Deprecated + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public boolean getDataEnabled() { return isDataEnabled(); } @@ -11132,6 +11639,9 @@ public class TelephonyManager { * {@link ConnectivityManager#getRestrictBackgroundStatus}. * * @return true if mobile data is enabled. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, @@ -11162,6 +11672,9 @@ public class TelephonyManager { * has carrier privileges (see {@link #hasCarrierPrivileges}). * * @return {@code true} if the data roaming is enabled on the subscription, otherwise return + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * {@code false}. */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, @@ -11201,6 +11714,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. * @hide */ @SystemApi @@ -11243,6 +11758,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. * @hide */ @SystemApi @@ -11311,6 +11828,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. * @hide */ @SystemApi @@ -11349,6 +11868,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. * @hide */ @SystemApi @@ -11384,6 +11905,8 @@ public class TelephonyManager { * * @param isEnabled {@code true} to enable mobile data roaming, otherwise disable it. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @SystemApi @@ -11402,11 +11925,15 @@ public class TelephonyManager { } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * * @deprecated use {@link #isDataEnabled()} instead. * @hide */ @Deprecated @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public boolean getDataEnabled(int subId) { try { return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER); @@ -11417,6 +11944,8 @@ public class TelephonyManager { } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @deprecated Use {@link android.telephony.ims.ImsMmTelManager#setVtSettingEnabled(boolean)} * instead. * @hide @@ -11424,6 +11953,7 @@ public class TelephonyManager { @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) public void enableVideoCalling(boolean enable) { try { ITelephony telephony = getITelephony(); @@ -11435,6 +11965,8 @@ public class TelephonyManager { } /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @deprecated Use {@link ImsMmTelManager#isVtSettingEnabled()} instead to check if the user * has enabled the Video Calling setting, {@link ImsMmTelManager#isAvailable(int, int)} to * determine if video calling is available, or {@link ImsMmTelManager#isCapable(int, int)} to @@ -11447,6 +11979,7 @@ public class TelephonyManager { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) public boolean isVideoCallingEnabled() { try { ITelephony telephony = getITelephony(); @@ -11462,6 +11995,9 @@ public class TelephonyManager { * Whether the device supports configuring the DTMF tone length. * * @return {@code true} if the DTMF tone length can be changed, and {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public boolean canChangeDtmfToneLength() { @@ -11483,7 +12019,11 @@ public class TelephonyManager { * Whether the device is a world phone. * * @return {@code true} if the device is a world phone, and {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) public boolean isWorldPhone() { try { ITelephony telephony = getITelephony(); @@ -11504,8 +12044,11 @@ public class TelephonyManager { * * @return {@code true} if the device supports TTY mode, and {@code false} otherwise. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELECOM}. */ @Deprecated + @RequiresFeature(PackageManager.FEATURE_TELECOM) public boolean isTtyModeSupported() { try { TelecomManager telecomManager = null; @@ -11526,6 +12069,9 @@ public class TelephonyManager { * support for the feature and device firmware support. * * @return {@code true} if the device and carrier both support RTT, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_IMS}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) public boolean isRttSupported() { @@ -11546,6 +12092,9 @@ public class TelephonyManager { * * @return {@code true} if the device supports hearing aid compatibility, and {@code false} * otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public boolean isHearingAidCompatibilitySupported() { @@ -11808,11 +12357,14 @@ public class TelephonyManager { * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * {@hide} **/ @SystemApi @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public void setSimPowerState(int state) { setSimPowerStateForSlot(getSlotIndex(), state); } @@ -11834,11 +12386,14 @@ public class TelephonyManager { * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * {@hide} **/ @SystemApi @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public void setSimPowerStateForSlot(int slotIndex, int state) { try { ITelephony telephony = getITelephony(); @@ -11871,6 +12426,8 @@ public class TelephonyManager { * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * {@hide} **/ @SystemApi @@ -11901,6 +12458,8 @@ public class TelephonyManager { * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * {@hide} **/ @SystemApi @@ -12055,6 +12614,9 @@ public class TelephonyManager { * application currently configured for this user. * @return component name of the app and class to direct Respond Via Message intent to, or * {@code null} if the functionality is not supported. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. * @hide */ @SystemApi @@ -12077,6 +12639,9 @@ public class TelephonyManager { * user associated with this subscription. * @return component name of the app and class to direct Respond Via Message intent to, or * {@code null} if the functionality is not supported. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. * @hide */ @SystemApi @@ -12213,9 +12778,13 @@ public class TelephonyManager { * @return The {@link PhoneAccountHandle} associated with the TelphonyManager, or {@code null} * if there is no associated {@link PhoneAccountHandle}; this can happen if the subscription is * data-only or an opportunistic subscription. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public @Nullable PhoneAccountHandle getPhoneAccountHandle() { return getPhoneAccountHandleForSubscriptionId(getSubId()); } @@ -12277,10 +12846,14 @@ public class TelephonyManager { * Resets Telephony and IMS settings back to factory defaults only for the subscription * associated with this instance. * @see #createForSubscriptionId(int) + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.CONNECTIVITY_INTERNAL) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) public void resetSettings() { try { Log.d(TAG, "resetSettings: subId=" + getSubId()); @@ -12302,6 +12875,8 @@ public class TelephonyManager { * * @see Locale#toLanguageTag() * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -12394,10 +12969,14 @@ public class TelephonyManager { * @param callback A callback object to which the result will be delivered. If there was an * error processing the request, {@link OutcomeReceiver#onError} will be called * with more details about the error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) { Objects.requireNonNull(executor); @@ -12484,6 +13063,9 @@ public class TelephonyManager { * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. * May return {@code null} when the subscription is inactive or when there was an error * communicating with the phone process. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(allOf = { @@ -12516,12 +13098,16 @@ public class TelephonyManager { * location related information. * May return {@code null} when the subscription is inactive or when there was an error * communicating with the phone process. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(allOf = { Manifest.permission.READ_PHONE_STATE, Manifest.permission.ACCESS_COARSE_LOCATION }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public @Nullable ServiceState getServiceState(@IncludeLocationData int includeLocationData) { return getServiceStateForSubscriber(getSubId(), includeLocationData != INCLUDE_LOCATION_DATA_FINE, @@ -12580,6 +13166,9 @@ public class TelephonyManager { * voicemail ringtone. * @return The URI for the ringtone to play when receiving a voicemail from a specific * PhoneAccount. May be {@code null} if no ringtone is set. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) { @@ -12606,10 +13195,13 @@ public class TelephonyManager { * @param uri The URI for the ringtone to play when receiving a voicemail from a specific * PhoneAccount. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS} * instead. */ @Deprecated + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public void setVoicemailRingtoneUri(PhoneAccountHandle phoneAccountHandle, Uri uri) { try { ITelephony service = getITelephony(); @@ -12627,6 +13219,9 @@ public class TelephonyManager { * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the * voicemail vibration setting. * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) { @@ -12653,10 +13248,13 @@ public class TelephonyManager { * @param enabled Whether to enable or disable vibration for voicemail notifications from a * specific PhoneAccount. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS} * instead. */ @Deprecated + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public void setVoicemailVibrationEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled) { try { @@ -12682,6 +13280,9 @@ public class TelephonyManager { * * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the * subscription is unavailable or the carrier cannot be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public int getSimCarrierId() { @@ -12707,6 +13308,9 @@ public class TelephonyManager { * * @return Carrier name of the current subscription. Return {@code null} if the subscription is * unavailable or the carrier cannot be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public @Nullable CharSequence getSimCarrierIdName() { @@ -12745,6 +13349,9 @@ public class TelephonyManager { * @return Returns fine-grained carrier id of the current subscription. * Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot * be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public int getSimSpecificCarrierId() { @@ -12771,6 +13378,9 @@ public class TelephonyManager { * * @return user-facing name of the subscription specific carrier id. Return {@code null} if the * subscription is unavailable or the carrier cannot be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public @Nullable CharSequence getSimSpecificCarrierIdName() { @@ -12799,6 +13409,9 @@ public class TelephonyManager { * * @return matching carrier id from sim MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the * subscription is unavailable or the carrier cannot be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public int getCarrierIdFromSimMccMnc() { @@ -12874,6 +13487,9 @@ public class TelephonyManager { * * @param appType the uicc app type. * @return Application ID for specified app type or {@code null} if no uicc or error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @Nullable @@ -12939,6 +13555,9 @@ public class TelephonyManager { * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission * * @return PRLVersion or null if error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. * @hide */ @SystemApi @@ -13004,6 +13623,9 @@ public class TelephonyManager { * * @return The number of carriers set successfully. Should be length of * carrierList on success; -1 if carrierList null or on error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. * @hide */ @SystemApi @@ -13130,6 +13752,9 @@ public class TelephonyManager { * @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success. * {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the * configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. * @hide */ @SystemApi @@ -13163,11 +13788,15 @@ public class TelephonyManager { * * @return List of {@link android.telephony.CarrierIdentifier}; empty list * means all carriers are allowed. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. * @hide */ @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK) public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) { if (SubscriptionManager.isValidPhoneId(slotIndex)) { CarrierRestrictionRules carrierRestrictionRule = getCarrierRestrictionRules(); @@ -13189,6 +13818,9 @@ public class TelephonyManager { * @return {@link CarrierRestrictionRules} which contains the allowed carrier list and the * excluded carrier list with the priority between the two lists. Returns {@code null} * in case of error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. * @hide */ @SystemApi @@ -13257,6 +13889,8 @@ public class TelephonyManager { * status result fetched from the radio * @throws SecurityException if the caller does not have the required permission/privileges or * if the caller is not pre-registered. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @@ -13323,11 +13957,15 @@ public class TelephonyManager { * @see #resetAllCarrierActions() * @deprecated use {@link #setDataEnabledForReason(int, boolean) with * reason {@link #DATA_ENABLED_REASON_CARRIER}} instead. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public void setCarrierDataEnabled(boolean enabled) { try { setDataEnabledForReason(DATA_ENABLED_REASON_CARRIER, enabled); @@ -13351,6 +13989,8 @@ public class TelephonyManager { * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and * {@link clearRadioPowerOffForReason}. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @Deprecated @@ -13470,6 +14110,9 @@ public class TelephonyManager { * * @param report control start/stop reporting network status. * @see #resetAllCarrierActions() + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -13496,6 +14139,8 @@ public class TelephonyManager { * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -13618,6 +14263,8 @@ public class TelephonyManager { * has {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} irrespective of * the reason. * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) @@ -13661,6 +14308,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE} * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE} * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, @@ -13717,6 +14366,9 @@ public class TelephonyManager { * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * @return true if phone is in emergency callback mode. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @SystemApi @@ -13756,6 +14408,9 @@ public class TelephonyManager { * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}. * * @return {@code true} if manual network selection is allowed, otherwise return {@code false}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // No support carrier privileges (b/72967236). @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRECISE_PHONE_STATE, @@ -13779,6 +14434,9 @@ public class TelephonyManager { * Get the most recent SignalStrength information reported by the modem. Due * to power saving this information may not always be current. * @return the most recent cached signal strength info from the modem + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @Nullable @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -13805,6 +14463,9 @@ public class TelephonyManager { * <LI>And possibly others.</LI> * </UL> * @return {@code true} if the overall data connection is allowed; {@code false} if not. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, @@ -13975,6 +14636,9 @@ public class TelephonyManager { * * @param enable enable(True) or disable(False) * @return returns true if successfully set. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -14004,6 +14668,9 @@ public class TelephonyManager { * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -14205,6 +14872,8 @@ public class TelephonyManager { * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @throws SecurityException if the caller does not have the required permission + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -14240,6 +14909,8 @@ public class TelephonyManager { * <p> Requires permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -14267,6 +14938,8 @@ public class TelephonyManager { * <p> Requires permission: * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) @@ -14293,6 +14966,8 @@ public class TelephonyManager { * <p> Requires permission: * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) @@ -14353,6 +15028,9 @@ public class TelephonyManager { * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value * as the list of {@link EmergencyNumber}; empty Map if this information is not available; * or throw a SecurityException if the caller does not have the permission. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @NonNull @@ -14409,6 +15087,8 @@ public class TelephonyManager { * as the list of {@link EmergencyNumber}; empty Map if this information is not available; * or throw a SecurityException if the caller does not have the permission. * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @NonNull @@ -14477,6 +15157,8 @@ public class TelephonyManager { * @return {@code true} if the given number is an emergency number based on current locale, * SIM card(s), Android database, modem, network or defaults; {@code false} otherwise. * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public boolean isEmergencyNumber(@NonNull String number) { @@ -14514,6 +15196,8 @@ public class TelephonyManager { * network; {@code false} if it is not; or throw an SecurityException if the caller does not * have the required permission/privileges * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)} instead. * @hide @@ -14543,6 +15227,8 @@ public class TelephonyManager { * <p>Requires Permission: * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @SystemApi @@ -14694,6 +15380,9 @@ public class TelephonyManager { * @param callback Callback will be triggered once it succeeds or failed. * See the {@code SET_OPPORTUNISTIC_SUB_*} constants * for more details. Pass null if don't care about the result. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public void setPreferredOpportunisticDataSubscription(int subId, boolean needValidation, @@ -14754,6 +15443,8 @@ public class TelephonyManager { * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred * subscription id * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, @@ -14793,6 +15484,8 @@ public class TelephonyManager { * @param executor The executor of where the callback will execute. * @param callback Callback will be triggered once it succeeds or failed. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) @@ -14853,10 +15546,13 @@ public class TelephonyManager { * @param enable whether to enable or disable the modem stack. * @return whether the operation is successful. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) public boolean enableModemForSlot(int slotIndex, boolean enable) { boolean ret = false; try { @@ -14879,10 +15575,14 @@ public class TelephonyManager { * {@link #hasCarrierPrivileges()}). * * @param slotIndex which slot it's checking. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) public boolean isModemEnabledForSlot(int slotIndex) { try { ITelephony telephony = getITelephony(); @@ -14945,6 +15645,8 @@ public class TelephonyManager { * @param isMultiSimCarrierRestricted true if usage of multiple SIMs is restricted, false * otherwise. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. * @hide */ @SystemApi @@ -15000,6 +15702,9 @@ public class TelephonyManager { * {@link #MULTISIM_NOT_SUPPORTED_BY_HARDWARE} if the device does not support multiple SIMs. * {@link #MULTISIM_NOT_SUPPORTED_BY_CARRIER} in the device supports multiple SIMs, but the * functionality is restricted by the carrier. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @@ -15031,6 +15736,8 @@ public class TelephonyManager { * * @param numOfSims number of live SIMs we want to switch to * @throws android.os.RemoteException + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -15058,6 +15765,9 @@ public class TelephonyManager { * * @return {@code true} if reboot will be triggered after making changes to modem * configurations, otherwise return {@code false}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresPermission(Manifest.permission.READ_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -15220,6 +15930,8 @@ public class TelephonyManager { * {@link #CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED}, or * {@link #CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES} * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -15334,6 +16046,8 @@ public class TelephonyManager { * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}. * @return whether data is enabled for a apn type. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @SystemApi @@ -15356,6 +16070,8 @@ public class TelephonyManager { * Whether an APN type is metered or not. It will be evaluated with the subId associated * with the TelephonyManager instance. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @SystemApi @@ -15385,6 +16101,9 @@ public class TelephonyManager { * @param executor The executor to execute the callback on * @param callback The callback that gets invoked when the radio responds to the request. Called * with {@code true} if the request succeeded, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -15403,6 +16122,9 @@ public class TelephonyManager { * Same as {@link #setSystemSelectionChannels(List, Executor, Consumer<Boolean>)}, but to be * used when the caller does not need feedback on the results of the operation. * @param specifiers which bands to scan. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -15450,6 +16172,8 @@ public class TelephonyManager { * @return a list of {@link RadioAccessSpecifier}, or an empty list if no bands are specified. * @throws IllegalStateException if the Telephony process is not currently available. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -15478,6 +16202,8 @@ public class TelephonyManager { * @return {@code true} if input mccmnc and mvno matches with data from sim operator. * {@code false} otherwise. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * {@hide} */ @SystemApi @@ -15568,6 +16294,8 @@ public class TelephonyManager { * {@link CallForwardingInfo#REASON_UNCONDITIONAL}, {@link CallForwardingInfo#REASON_BUSY}, * {@link CallForwardingInfo#REASON_NO_REPLY}, {@link CallForwardingInfo#REASON_NOT_REACHABLE}, * {@link CallForwardingInfo#REASON_ALL}, or {@link CallForwardingInfo#REASON_ALL_CONDITIONAL} + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * * @hide */ @@ -15645,6 +16373,8 @@ public class TelephonyManager { * <li>{@link CallForwardingInfo#getTimeoutSeconds()} returns a non-positive value when * enabling call forwarding</li> * </ul> + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -15769,6 +16499,9 @@ public class TelephonyManager { * <li>{@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}</li> * <li>{@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE}}</li> * </ul> + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @SystemApi @@ -15819,6 +16552,9 @@ public class TelephonyManager { * {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or * {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} or * {@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE} if it failed. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. * @hide */ @SystemApi @@ -15919,6 +16655,9 @@ public class TelephonyManager { * * @param policy The data policy to enable. * @param enabled Whether to enable or disable the policy. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @SystemApi @@ -15940,6 +16679,9 @@ public class TelephonyManager { * * @param policy The data policy that you want the status for. * @return {@code true} if enabled, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @hide */ @SystemApi @@ -15975,6 +16717,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @WorkerThread @@ -16009,6 +16753,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -16051,6 +16797,8 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -16199,6 +16947,8 @@ public class TelephonyManager { * </ol> * @return operation result. * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -16233,6 +16983,8 @@ public class TelephonyManager { * connectivity is active. It means the device is allowed to connect to both primary and * secondary cell. * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -16470,6 +17222,8 @@ public class TelephonyManager { * * @throws IllegalStateException if the Telephony process is not currently available. * @throws SecurityException if the caller doesn't have the permission. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @@ -16586,6 +17340,9 @@ public class TelephonyManager { * * @param capability the name of the capability to check for * @return the availability of the capability + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public boolean isRadioInterfaceCapabilitySupported( @@ -16705,6 +17462,8 @@ public class TelephonyManager { * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters or * if the device's modem does not support data throttling. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. * @hide */ @SystemApi @@ -17037,6 +17796,9 @@ public class TelephonyManager { * contain the GBA Ks_NAF/Ks_ext_NAF when available. If the NAF keys are * available and valid at the time of call and bootstrapping is not requested, * then the callback shall be invoked with the available keys. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -17135,6 +17897,8 @@ public class TelephonyManager { * @param request the SignalStrengthUpdateRequest to be set into the System * * @throws IllegalStateException if a new request is set with same subId from the same caller + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -17165,6 +17929,9 @@ public class TelephonyManager { * @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest) * * @param request the SignalStrengthUpdateRequest to be cleared from the System + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -17188,10 +17955,14 @@ public class TelephonyManager { * @return the PhoneCapability which describes the data connection capability of modem. * It's used to evaluate possible phone config change, for example from single * SIM device to multi-SIM device. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public @NonNull PhoneCapability getPhoneCapability() { try { ITelephony telephony = getITelephony(); @@ -17256,11 +18027,15 @@ public class TelephonyManager { * at least one SIM card for which the user needs to manually enter the PIN * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case * of error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.REBOOT) @PrepareUnattendedRebootResult + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public int prepareForUnattendedReboot() { try { ITelephony service = getITelephony(); @@ -17363,6 +18138,9 @@ public class TelephonyManager { * * @param executor the executor on which callback will be invoked. * @param callback a callback to receive the current slicing configuration. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. */ @RequiresFeature( enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", @@ -17444,8 +18222,11 @@ public class TelephonyManager { * @param capability The premium capability to check. * @return Whether the given premium capability is available to purchase. * @throws SecurityException if the caller does not hold permission READ_BASIC_PHONE_STATE. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. */ @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public boolean isPremiumCapabilityAvailableForPurchase(@PremiumCapability int capability) { try { ITelephony telephony = getITelephony(); @@ -17685,10 +18466,13 @@ public class TelephonyManager { * @param callback The result of the purchase request. * @throws SecurityException if the caller does not hold permissions * READ_BASIC_PHONE_STATE or INTERNET. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. * @see #isPremiumCapabilityAvailableForPurchase(int) to check whether the capability is valid. */ @RequiresPermission(allOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE, android.Manifest.permission.INTERNET}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) public void purchasePremiumCapability(@PremiumCapability int capability, @NonNull @CallbackExecutor Executor executor, @NonNull @PurchasePremiumCapabilityResult Consumer<Integer> callback) { @@ -18142,10 +18926,14 @@ public class TelephonyManager { * Get current cell broadcast message identifier ranges. * * @throws SecurityException if the caller does not have the required permission + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. + * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) @NonNull public List<CellBroadcastIdRange> getCellBroadcastIdRanges() { try { @@ -18299,10 +19087,13 @@ public class TelephonyManager { * the result when the operation completes. * @throws SecurityException if the caller does not have the required permission * @throws IllegalArgumentException when the ranges are invalid. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) public void setCellBroadcastIdRanges(@NonNull List<CellBroadcastIdRange> ranges, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Integer> callback) { @@ -18378,7 +19169,8 @@ public class TelephonyManager { * </ul> * * @return Primary IMEI of type string - * @throws UnsupportedOperationException if the radio doesn't support this feature. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. * @throws SecurityException if the caller does not have the required permission/privileges */ @NonNull diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index 9c335768de55..bbd4567a4454 100644 --- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -36,12 +36,12 @@ import android.util.proto.ProtoOutputStream import android.view.InputDevice import android.view.inputmethod.InputMethodInfo import android.view.inputmethod.InputMethodSubtype -import androidx.test.core.R import androidx.test.core.app.ApplicationProvider import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.os.KeyboardConfiguredProto import com.android.internal.util.FrameworkStatsLog import com.android.modules.utils.testing.ExtendedMockitoRule +import com.android.test.input.R import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals |