diff options
369 files changed, 7160 insertions, 5176 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 9e308435c9bc..edd9d3af5926 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -71,6 +71,8 @@ aconfig_srcjars = [ ":android.appwidget.flags-aconfig-java{.generated_srcjars}", ":android.webkit.flags-aconfig-java{.generated_srcjars}", ":android.provider.flags-aconfig-java{.generated_srcjars}", + ":android.chre.flags-aconfig-java{.generated_srcjars}", + ":android.speech.flags-aconfig-java{.generated_srcjars}", ] filegroup { @@ -196,7 +198,7 @@ java_aconfig_library { aconfig_declarations { name: "android.nfc.flags-aconfig", package: "android.nfc", - srcs: ["core/java/android/nfc/*.aconfig"], + srcs: ["nfc/java/android/nfc/*.aconfig"], } cc_aconfig_library { @@ -214,7 +216,7 @@ cc_aconfig_library { java_aconfig_library { name: "android.nfc.flags-aconfig-java", aconfig_declarations: "android.nfc.flags-aconfig", - min_sdk_version: "VanillaIceCream", + min_sdk_version: "34", apex_available: [ "//apex_available:platform", "com.android.nfcservices", @@ -480,8 +482,8 @@ java_aconfig_library { apex_available: [ "//apex_available:platform", "com.android.permission", + "com.android.nfcservices", ], - } // SQLite @@ -743,6 +745,11 @@ aconfig_declarations { java_aconfig_library { name: "android.service.chooser.flags-aconfig-java", aconfig_declarations: "android.service.chooser.flags-aconfig", + min_sdk_version: "34", + apex_available: [ + "//apex_available:platform", + "com.android.nfcservices", + ], defaults: ["framework-minus-apex-aconfig-java-defaults"], } @@ -905,3 +912,23 @@ java_aconfig_library { aconfig_declarations: "android.provider.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// ContextHub +java_aconfig_library { + name: "android.chre.flags-aconfig-java", + aconfig_declarations: "chre_flags", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + +// Speech +aconfig_declarations { + name: "android.speech.flags-aconfig", + package: "android.speech.flags", + srcs: ["core/java/android/speech/flags/*.aconfig"], +} + +java_aconfig_library { + name: "android.speech.flags-aconfig-java", + aconfig_declarations: "android.speech.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/Android.bp b/Android.bp index f3b2ebb4fc17..a3e39018e147 100644 --- a/Android.bp +++ b/Android.bp @@ -175,9 +175,6 @@ java_library { // and remove this line. "//frameworks/base/tools/hoststubgen:__subpackages__", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // AIDL files under these paths are mixture of public and private ones. @@ -270,9 +267,6 @@ java_library { ], sdk_version: "core_platform", installable: false, - lint: { - baseline_filename: "lint-baseline.xml", - }, } // NOTE: This filegroup is exposed for vendor libraries to depend on and is referenced in @@ -437,13 +431,9 @@ java_library { name: "framework-non-updatable-unbundled-impl-libs", static_libs: [ "framework-location.impl", - "framework-nfc.impl", ], sdk_version: "core_platform", installable: false, - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Separated so framework-minus-apex-defaults can be used without the libs dependency @@ -487,9 +477,6 @@ java_library { ], compile_dex: false, headers_only: true, - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -534,7 +521,7 @@ java_library { }, lint: { enabled: false, - baseline_filename: "lint-baseline.xml", + }, } @@ -559,9 +546,6 @@ java_library { ], sdk_version: "core_platform", apex_available: ["//apex_available:platform"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -577,9 +561,6 @@ java_library { "calendar-provider-compat-config", "contacts-provider-platform-compat-config", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } platform_compat_config { @@ -634,9 +615,6 @@ java_library { "rappor", ], dxflags: ["--core-library"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // utility classes statically linked into framework-wifi and dynamically linked diff --git a/TEST_MAPPING b/TEST_MAPPING index d59775f4060b..ecfd86c584e0 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -138,15 +138,14 @@ } ], "postsubmit-ravenwood": [ - // TODO(ravenwood) promote it to presubmit - // TODO: Enable it once the infra knows how to run it. -// { -// "name": "CtsUtilTestCasesRavenwood", -// "file_patterns": [ -// "*Ravenwood*", -// "*ravenwood*" -// ] -// } + { + "name": "CtsUtilTestCasesRavenwood", + "host": true, + "file_patterns": [ + "*Ravenwood*", + "*ravenwood*" + ] + } ], "postsubmit-managedprofile-stress": [ { diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp index bcfb68ffd04d..7ae3224e7500 100644 --- a/api/ApiDocs.bp +++ b/api/ApiDocs.bp @@ -63,6 +63,7 @@ stubs_defaults { ":framework-graphics-srcs", ":framework-mediaprovider-sources", ":framework-nearby-sources", + ":framework-nfc-updatable-sources", ":framework-ondevicepersonalization-sources", ":framework-permission-sources", ":framework-permission-s-sources", @@ -183,6 +184,7 @@ doc_defaults { "-federationapi AndroidX $(location :current-androidx-api)", // doclava contains checks for a few issues that are have been migrated to metalava. // disable them in doclava, to avoid mistriggering or double triggering. + "-hide 101", // TODO: turn Lint 101 back into an error again "-hide 111", // HIDDEN_SUPERCLASS "-hide 113", // DEPRECATION_MISMATCH "-hide 125", // REQUIRES_PERMISSION diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index ef1fa6097056..74344cd4b5a5 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -635,7 +635,6 @@ java_defaults { api_contributions: [ "framework-virtualization.stubs.source.test.api.contribution", "framework-location.stubs.source.test.api.contribution", - "framework-nfc.stubs.source.test.api.contribution", ], } diff --git a/api/api.go b/api/api.go index 2668999c572e..b975c55c5af9 100644 --- a/api/api.go +++ b/api/api.go @@ -31,7 +31,6 @@ const conscrypt = "conscrypt.module.public.api" const i18n = "i18n.module.public.api" const virtualization = "framework-virtualization" const location = "framework-location" -const nfc = "framework-nfc" var core_libraries_modules = []string{art, conscrypt, i18n} @@ -43,7 +42,7 @@ var core_libraries_modules = []string{art, conscrypt, i18n} // APIs. // In addition, the modules in this list are allowed to contribute to test APIs // stubs. -var non_updatable_modules = []string{virtualization, location, nfc} +var non_updatable_modules = []string{virtualization, location} // The intention behind this soong plugin is to generate a number of "merged" // API-related modules that would otherwise require a large amount of very @@ -64,6 +63,7 @@ type CombinedApisProperties struct { type CombinedApis struct { android.ModuleBase + android.DefaultableModuleBase properties CombinedApisProperties } @@ -74,6 +74,7 @@ func init() { func registerBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory) + ctx.RegisterModuleType("combined_apis_defaults", CombinedApisModuleDefaultsFactory) } var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents) @@ -409,6 +410,7 @@ func combinedApisModuleFactory() android.Module { module := &CombinedApis{} module.AddProperties(&module.properties) android.InitAndroidModule(module) + android.InitDefaultableModule(module) android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) return module } @@ -445,3 +447,16 @@ func remove(s []string, v string) []string { } return s2 } + +// Defaults +type CombinedApisModuleDefaults struct { + android.ModuleBase + android.DefaultsModuleBase +} + +func CombinedApisModuleDefaultsFactory() android.Module { + module := &CombinedApisModuleDefaults{} + module.AddProperties(&CombinedApisProperties{}) + android.InitDefaultsModule(module) + return module +} diff --git a/core/api/current.txt b/core/api/current.txt index 7c34812464c0..902f0fc17a38 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1602,6 +1602,7 @@ package android { field public static final int switchTextOff = 16843628; // 0x101036c field public static final int switchTextOn = 16843627; // 0x101036b field public static final int syncable = 16842777; // 0x1010019 + field @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") public static final int systemUserOnly; field public static final int tabStripEnabled = 16843453; // 0x10102bd field public static final int tabStripLeft = 16843451; // 0x10102bb field public static final int tabStripRight = 16843452; // 0x10102bc @@ -4371,7 +4372,7 @@ package android.app { method public final android.media.session.MediaController getMediaController(); method @NonNull public android.view.MenuInflater getMenuInflater(); method @NonNull public android.window.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); - method @Deprecated public final android.app.Activity getParent(); + method public final android.app.Activity getParent(); method @Nullable public android.content.Intent getParentActivityIntent(); method public android.content.SharedPreferences getPreferences(int); method @Nullable public android.net.Uri getReferrer(); @@ -4389,7 +4390,7 @@ package android.app { method public void invalidateOptionsMenu(); method public boolean isActivityTransitionRunning(); method public boolean isChangingConfigurations(); - method @Deprecated public final boolean isChild(); + method public final boolean isChild(); method public boolean isDestroyed(); method public boolean isFinishing(); method public boolean isImmersive(); @@ -28748,460 +28749,6 @@ package android.net.vcn { } -package android.nfc { - - public final class AvailableNfcAntenna implements android.os.Parcelable { - ctor public AvailableNfcAntenna(int, int); - method public int describeContents(); - method public int getLocationX(); - method public int getLocationY(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.AvailableNfcAntenna> CREATOR; - } - - public class FormatException extends java.lang.Exception { - ctor public FormatException(); - ctor public FormatException(String); - ctor public FormatException(String, Throwable); - } - - public final class NdefMessage implements android.os.Parcelable { - ctor public NdefMessage(byte[]) throws android.nfc.FormatException; - ctor public NdefMessage(android.nfc.NdefRecord, android.nfc.NdefRecord...); - ctor public NdefMessage(android.nfc.NdefRecord[]); - method public int describeContents(); - method public int getByteArrayLength(); - method public android.nfc.NdefRecord[] getRecords(); - method public byte[] toByteArray(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NdefMessage> CREATOR; - } - - public final class NdefRecord implements android.os.Parcelable { - ctor public NdefRecord(short, byte[], byte[], byte[]); - ctor @Deprecated public NdefRecord(byte[]) throws android.nfc.FormatException; - method public static android.nfc.NdefRecord createApplicationRecord(String); - method public static android.nfc.NdefRecord createExternal(String, String, byte[]); - method public static android.nfc.NdefRecord createMime(String, byte[]); - method public static android.nfc.NdefRecord createTextRecord(String, String); - method public static android.nfc.NdefRecord createUri(android.net.Uri); - method public static android.nfc.NdefRecord createUri(String); - method public int describeContents(); - method public byte[] getId(); - method public byte[] getPayload(); - method public short getTnf(); - method public byte[] getType(); - method @Deprecated public byte[] toByteArray(); - method public String toMimeType(); - method public android.net.Uri toUri(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NdefRecord> CREATOR; - field public static final byte[] RTD_ALTERNATIVE_CARRIER; - field public static final byte[] RTD_HANDOVER_CARRIER; - field public static final byte[] RTD_HANDOVER_REQUEST; - field public static final byte[] RTD_HANDOVER_SELECT; - field public static final byte[] RTD_SMART_POSTER; - field public static final byte[] RTD_TEXT; - field public static final byte[] RTD_URI; - field public static final short TNF_ABSOLUTE_URI = 3; // 0x3 - field public static final short TNF_EMPTY = 0; // 0x0 - field public static final short TNF_EXTERNAL_TYPE = 4; // 0x4 - field public static final short TNF_MIME_MEDIA = 2; // 0x2 - field public static final short TNF_UNCHANGED = 6; // 0x6 - field public static final short TNF_UNKNOWN = 5; // 0x5 - field public static final short TNF_WELL_KNOWN = 1; // 0x1 - } - - public final class NfcAdapter { - method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean allowTransaction(); - method public void disableForegroundDispatch(android.app.Activity); - method public void disableReaderMode(android.app.Activity); - method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean disallowTransaction(); - method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]); - method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle); - method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context); - method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo(); - method @FlaggedApi("android.nfc.enable_nfc_charging") @Nullable public android.nfc.WlcLDeviceInfo getWlcLDeviceInfo(); - method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler); - method public boolean isEnabled(); - method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported(); - method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled(); - method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported(); - 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"; - field public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; - field public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; - field @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT) public static final String ACTION_TRANSACTION_DETECTED = "android.nfc.action.TRANSACTION_DETECTED"; - field public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE"; - field public static final String EXTRA_AID = "android.nfc.extra.AID"; - field public static final String EXTRA_DATA = "android.nfc.extra.DATA"; - field public static final String EXTRA_ID = "android.nfc.extra.ID"; - field public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; - field public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON = "android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON"; - 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 - field public static final int FLAG_READER_NFC_F = 4; // 0x4 - field public static final int FLAG_READER_NFC_V = 8; // 0x8 - field public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 256; // 0x100 - field public static final int FLAG_READER_SKIP_NDEF_CHECK = 128; // 0x80 - field public static final int PREFERRED_PAYMENT_CHANGED = 2; // 0x2 - field public static final int PREFERRED_PAYMENT_LOADED = 1; // 0x1 - field public static final int PREFERRED_PAYMENT_UPDATED = 3; // 0x3 - field public static final int STATE_OFF = 1; // 0x1 - field public static final int STATE_ON = 3; // 0x3 - field public static final int STATE_TURNING_OFF = 4; // 0x4 - field public static final int STATE_TURNING_ON = 2; // 0x2 - } - - @Deprecated public static interface NfcAdapter.CreateBeamUrisCallback { - method @Deprecated public android.net.Uri[] createBeamUris(android.nfc.NfcEvent); - } - - @Deprecated public static interface NfcAdapter.CreateNdefMessageCallback { - method @Deprecated public android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent); - } - - @Deprecated public static interface NfcAdapter.OnNdefPushCompleteCallback { - method @Deprecated public void onNdefPushComplete(android.nfc.NfcEvent); - } - - public static interface NfcAdapter.OnTagRemovedListener { - method public void onTagRemoved(); - } - - public static interface NfcAdapter.ReaderCallback { - method public void onTagDiscovered(android.nfc.Tag); - } - - public final class NfcAntennaInfo implements android.os.Parcelable { - ctor public NfcAntennaInfo(int, int, boolean, @NonNull java.util.List<android.nfc.AvailableNfcAntenna>); - method public int describeContents(); - method @NonNull public java.util.List<android.nfc.AvailableNfcAntenna> getAvailableNfcAntennas(); - method public int getDeviceHeight(); - method public int getDeviceWidth(); - method public boolean isDeviceFoldable(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NfcAntennaInfo> CREATOR; - } - - public final class NfcEvent { - field public final android.nfc.NfcAdapter nfcAdapter; - field public final int peerLlcpMajorVersion; - field public final int peerLlcpMinorVersion; - } - - public final class NfcManager { - method public android.nfc.NfcAdapter getDefaultAdapter(); - } - - public final class Tag implements android.os.Parcelable { - method public int describeContents(); - method public byte[] getId(); - method public String[] getTechList(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.Tag> CREATOR; - } - - public class TagLostException extends java.io.IOException { - ctor public TagLostException(); - ctor public TagLostException(String); - } - - @FlaggedApi("android.nfc.enable_nfc_charging") public final class WlcLDeviceInfo implements android.os.Parcelable { - ctor public WlcLDeviceInfo(double, double, double, int); - method public int describeContents(); - method public double getBatteryLevel(); - method public double getProductId(); - method public int getState(); - method public double getTemperature(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final int CONNECTED_CHARGING = 2; // 0x2 - field public static final int CONNECTED_DISCHARGING = 3; // 0x3 - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.WlcLDeviceInfo> CREATOR; - field public static final int DISCONNECTED = 1; // 0x1 - } - -} - -package android.nfc.cardemulation { - - public final class CardEmulation { - method public boolean categoryAllowsForegroundPreference(String); - method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public java.util.List<java.lang.String> getAidsForPreferredPaymentService(); - method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, String); - method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public CharSequence getDescriptionForPreferredPaymentService(); - method public static android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter); - method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getRouteDestinationForPreferredPaymentService(); - method public int getSelectionModeForCategory(String); - method public boolean isDefaultServiceForAid(android.content.ComponentName, String); - method public boolean isDefaultServiceForCategory(android.content.ComponentName, String); - method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>); - method public boolean removeAidsForService(android.content.ComponentName, String); - method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String); - method public boolean setPreferredService(android.app.Activity, android.content.ComponentName); - method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setServiceObserveModeDefault(@NonNull android.content.ComponentName, boolean); - method public boolean supportsAidPrefixRegistration(); - method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName); - method public boolean unsetPreferredService(android.app.Activity); - field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; - field public static final String CATEGORY_OTHER = "other"; - field public static final String CATEGORY_PAYMENT = "payment"; - field public static final String EXTRA_CATEGORY = "category"; - field public static final String EXTRA_SERVICE_COMPONENT = "component"; - field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1 - field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2 - field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0 - } - - public abstract class HostApduService extends android.app.Service { - ctor public HostApduService(); - method public final void notifyUnhandled(); - method public final android.os.IBinder onBind(android.content.Intent); - method public abstract void onDeactivated(int); - method public abstract byte[] processCommandApdu(byte[], android.os.Bundle); - method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.os.Bundle>); - method public final void sendResponseApdu(byte[]); - field public static final int DEACTIVATION_DESELECTED = 1; // 0x1 - field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0 - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA"; - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN"; - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP"; - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_A = 65; // 0x0041 'A' - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_B = 66; // 0x0042 'B' - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_F = 70; // 0x0046 'F' - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE"; - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_OFF = 88; // 0x0058 'X' - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_ON = 79; // 0x004f 'O' - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x0055 'U' - field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE"; - field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service"; - } - - public abstract class HostNfcFService extends android.app.Service { - ctor public HostNfcFService(); - method public final android.os.IBinder onBind(android.content.Intent); - method public abstract void onDeactivated(int); - method public abstract byte[] processNfcFPacket(byte[], android.os.Bundle); - method public final void sendResponsePacket(byte[]); - field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0 - field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_NFCF_SERVICE"; - field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_nfcf_service"; - } - - public final class NfcFCardEmulation { - method public boolean disableService(android.app.Activity) throws java.lang.RuntimeException; - method public boolean enableService(android.app.Activity, android.content.ComponentName) throws java.lang.RuntimeException; - method public static android.nfc.cardemulation.NfcFCardEmulation getInstance(android.nfc.NfcAdapter); - method public String getNfcid2ForService(android.content.ComponentName) throws java.lang.RuntimeException; - method public String getSystemCodeForService(android.content.ComponentName) throws java.lang.RuntimeException; - method public boolean registerSystemCodeForService(android.content.ComponentName, String) throws java.lang.RuntimeException; - method public boolean setNfcid2ForService(android.content.ComponentName, String) throws java.lang.RuntimeException; - method public boolean unregisterSystemCodeForService(android.content.ComponentName) throws java.lang.RuntimeException; - } - - public abstract class OffHostApduService extends android.app.Service { - ctor public OffHostApduService(); - field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"; - field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service"; - } - -} - -package android.nfc.tech { - - public final class IsoDep implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.IsoDep get(android.nfc.Tag); - method public byte[] getHiLayerResponse(); - method public byte[] getHistoricalBytes(); - method public int getMaxTransceiveLength(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public boolean isConnected(); - method public boolean isExtendedLengthApduSupported(); - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public final class MifareClassic implements android.nfc.tech.TagTechnology { - method public boolean authenticateSectorWithKeyA(int, byte[]) throws java.io.IOException; - method public boolean authenticateSectorWithKeyB(int, byte[]) throws java.io.IOException; - method public int blockToSector(int); - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public void decrement(int, int) throws java.io.IOException; - method public static android.nfc.tech.MifareClassic get(android.nfc.Tag); - method public int getBlockCount(); - method public int getBlockCountInSector(int); - method public int getMaxTransceiveLength(); - method public int getSectorCount(); - method public int getSize(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public int getType(); - method public void increment(int, int) throws java.io.IOException; - method public boolean isConnected(); - method public byte[] readBlock(int) throws java.io.IOException; - method public void restore(int) throws java.io.IOException; - method public int sectorToBlock(int); - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - method public void transfer(int) throws java.io.IOException; - method public void writeBlock(int, byte[]) throws java.io.IOException; - field public static final int BLOCK_SIZE = 16; // 0x10 - field public static final byte[] KEY_DEFAULT; - field public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY; - field public static final byte[] KEY_NFC_FORUM; - field public static final int SIZE_1K = 1024; // 0x400 - field public static final int SIZE_2K = 2048; // 0x800 - field public static final int SIZE_4K = 4096; // 0x1000 - field public static final int SIZE_MINI = 320; // 0x140 - field public static final int TYPE_CLASSIC = 0; // 0x0 - field public static final int TYPE_PLUS = 1; // 0x1 - field public static final int TYPE_PRO = 2; // 0x2 - field public static final int TYPE_UNKNOWN = -1; // 0xffffffff - } - - public final class MifareUltralight implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.MifareUltralight get(android.nfc.Tag); - method public int getMaxTransceiveLength(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public int getType(); - method public boolean isConnected(); - method public byte[] readPages(int) throws java.io.IOException; - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - method public void writePage(int, byte[]) throws java.io.IOException; - field public static final int PAGE_SIZE = 4; // 0x4 - field public static final int TYPE_ULTRALIGHT = 1; // 0x1 - field public static final int TYPE_ULTRALIGHT_C = 2; // 0x2 - field public static final int TYPE_UNKNOWN = -1; // 0xffffffff - } - - public final class Ndef implements android.nfc.tech.TagTechnology { - method public boolean canMakeReadOnly(); - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.Ndef get(android.nfc.Tag); - method public android.nfc.NdefMessage getCachedNdefMessage(); - method public int getMaxSize(); - method public android.nfc.NdefMessage getNdefMessage() throws android.nfc.FormatException, java.io.IOException; - method public android.nfc.Tag getTag(); - method public String getType(); - method public boolean isConnected(); - method public boolean isWritable(); - method public boolean makeReadOnly() throws java.io.IOException; - method public void writeNdefMessage(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException; - field public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic"; - field public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1"; - field public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2"; - field public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3"; - field public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4"; - } - - public final class NdefFormatable implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public void format(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException; - method public void formatReadOnly(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException; - method public static android.nfc.tech.NdefFormatable get(android.nfc.Tag); - method public android.nfc.Tag getTag(); - method public boolean isConnected(); - } - - public final class NfcA implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcA get(android.nfc.Tag); - method public byte[] getAtqa(); - method public int getMaxTransceiveLength(); - method public short getSak(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public boolean isConnected(); - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public final class NfcB implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcB get(android.nfc.Tag); - method public byte[] getApplicationData(); - method public int getMaxTransceiveLength(); - method public byte[] getProtocolInfo(); - method public android.nfc.Tag getTag(); - method public boolean isConnected(); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public final class NfcBarcode implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcBarcode get(android.nfc.Tag); - method public byte[] getBarcode(); - method public android.nfc.Tag getTag(); - method public int getType(); - method public boolean isConnected(); - field public static final int TYPE_KOVIO = 1; // 0x1 - field public static final int TYPE_UNKNOWN = -1; // 0xffffffff - } - - public final class NfcF implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcF get(android.nfc.Tag); - method public byte[] getManufacturer(); - method public int getMaxTransceiveLength(); - method public byte[] getSystemCode(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public boolean isConnected(); - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public final class NfcV implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcV get(android.nfc.Tag); - method public byte getDsfId(); - method public int getMaxTransceiveLength(); - method public byte getResponseFlags(); - method public android.nfc.Tag getTag(); - method public boolean isConnected(); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public interface TagTechnology extends java.io.Closeable { - method public void connect() throws java.io.IOException; - method public android.nfc.Tag getTag(); - method public boolean isConnected(); - } - -} - package android.opengl { public class EGL14 { @@ -33913,7 +33460,6 @@ package android.os { @FlaggedApi("android.os.adpf_gpu_report_actual_work_duration") public final class WorkDuration implements android.os.Parcelable { ctor public WorkDuration(); - ctor public WorkDuration(long, long, long, long); method public int describeContents(); method public long getActualCpuDurationNanos(); method public long getActualGpuDurationNanos(); @@ -41552,6 +41098,8 @@ package android.speech { field public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL"; field public static final String EXTRA_LANGUAGE_PREFERENCE = "android.speech.extra.LANGUAGE_PREFERENCE"; field public static final String EXTRA_LANGUAGE_SWITCH_ALLOWED_LANGUAGES = "android.speech.extra.LANGUAGE_SWITCH_ALLOWED_LANGUAGES"; + field @FlaggedApi("android.speech.flags.multilang_extra_launch") public static final String EXTRA_LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS = "android.speech.extra.LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS"; + field @FlaggedApi("android.speech.flags.multilang_extra_launch") public static final String EXTRA_LANGUAGE_SWITCH_MAX_SWITCHES = "android.speech.extra.LANGUAGE_SWITCH_MAX_SWITCHES"; field public static final String EXTRA_MASK_OFFENSIVE_WORDS = "android.speech.extra.MASK_OFFENSIVE_WORDS"; field public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS"; field public static final String EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE = "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE"; @@ -45343,7 +44891,7 @@ package android.telephony { method public void addOnSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void addSubscriptionsIntoGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid); method public boolean canManageSubscription(android.telephony.SubscriptionInfo); - method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles(); + method @FlaggedApi("com.android.internal.telephony.flags.enforce_subscription_user_filter") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>); method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context); method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList(); @@ -51848,6 +51396,10 @@ package android.view { ctor public SurfaceControl.TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int); } + @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public interface SurfaceControlInputReceiver { + method public boolean onInputEvent(@NonNull android.view.InputEvent); + } + public class SurfaceControlViewHost { ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder); method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage(); @@ -53971,10 +53523,13 @@ package android.view { method @Deprecated public android.view.Display getDefaultDisplay(); method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics(); method public default boolean isCrossWindowBlurEnabled(); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.os.IBinder registerBatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver); method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void registerTrustedPresentationListener(@NonNull android.os.IBinder, @NonNull android.window.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.os.IBinder registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver); method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer); method public void removeViewImmediate(android.view.View); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void unregisterSurfaceControlInputReceiver(@NonNull android.os.IBinder); method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void unregisterTrustedPresentationListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"; field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"; diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt index f331e7f5fa84..162f54cc6d5a 100644 --- a/core/api/lint-baseline.txt +++ b/core/api/lint-baseline.txt @@ -181,12 +181,6 @@ BroadcastBehavior: android.media.tv.TvContract#ACTION_WATCH_NEXT_PROGRAM_BROWSAB Field 'ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED' is missing @BroadcastBehavior BroadcastBehavior: android.net.Proxy#PROXY_CHANGE_ACTION: Field 'PROXY_CHANGE_ACTION' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED: - Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED: - Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED: - Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior BroadcastBehavior: android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED: Field 'ACTION_DROPBOX_ENTRY_ADDED' is missing @BroadcastBehavior BroadcastBehavior: android.provider.CalendarContract#ACTION_EVENT_REMINDER: @@ -715,86 +709,6 @@ RequiresPermission: android.net.sip.SipAudioCall#setSpeakerMode(boolean): Method 'setSpeakerMode' documentation mentions permissions without declaring @RequiresPermission RequiresPermission: android.net.sip.SipAudioCall#startAudio(): Method 'startAudio' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity): - Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]): - Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String): - Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String): - Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String): - Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]): - Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]): - Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int): - Method 'decrement' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int): - Method 'increment' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int): - Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#restore(int): - Method 'restore' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transfer(int): - Method 'transfer' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]): - Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int): - Method 'readPages' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]): - Method 'writePage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#getNdefMessage(): - Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#isWritable(): - Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#makeReadOnly(): - Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage): - Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage): - Method 'format' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage): - Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#close(): - Method 'close' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#connect(): - Method 'connect' documentation mentions permissions without declaring @RequiresPermission RequiresPermission: android.os.BugreportManager#cancelBugreport(): Method 'cancelBugreport' documentation mentions permissions without declaring @RequiresPermission RequiresPermission: android.os.Build#getSerial(): diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index c1b9f64b9e8a..24b923326baa 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -319,11 +319,6 @@ package android.net.wifi { package android.nfc { - public class NfcFrameworkInitializer { - method public static void registerServiceWrappers(); - method public static void setNfcServiceManager(@NonNull android.nfc.NfcServiceManager); - } - public class NfcServiceManager { method @NonNull public android.nfc.NfcServiceManager.ServiceRegisterer getNfcManagerServiceRegisterer(); } diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt index a6a948c7f64c..a2179bc59707 100644 --- a/core/api/module-lib-lint-baseline.txt +++ b/core/api/module-lib-lint-baseline.txt @@ -235,14 +235,6 @@ BroadcastBehavior: android.net.NetworkScoreManager#ACTION_SCORE_NETWORKS: Field 'ACTION_SCORE_NETWORKS' is missing @BroadcastBehavior BroadcastBehavior: android.net.Proxy#PROXY_CHANGE_ACTION: Field 'PROXY_CHANGE_ACTION' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED: - Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED: - Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: - Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED: - Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior BroadcastBehavior: android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED: Field 'ACTION_DROPBOX_ENTRY_ADDED' is missing @BroadcastBehavior BroadcastBehavior: android.provider.CalendarContract#ACTION_EVENT_REMINDER: @@ -1009,86 +1001,6 @@ RequiresPermission: android.net.vcn.VcnManager#applyVcnNetworkPolicy(android.net Method 'applyVcnNetworkPolicy' documentation mentions permissions already declared by @RequiresPermission RequiresPermission: android.net.vcn.VcnManager#removeVcnNetworkPolicyChangeListener(android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener): Method 'removeVcnNetworkPolicyChangeListener' documentation mentions permissions already declared by @RequiresPermission -RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity): - Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]): - Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String): - Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String): - Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String): - Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]): - Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]): - Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int): - Method 'decrement' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int): - Method 'increment' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int): - Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#restore(int): - Method 'restore' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transfer(int): - Method 'transfer' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]): - Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int): - Method 'readPages' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]): - Method 'writePage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#getNdefMessage(): - Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#isWritable(): - Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#makeReadOnly(): - Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage): - Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage): - Method 'format' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage): - Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#close(): - Method 'close' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#connect(): - Method 'connect' documentation mentions permissions without declaring @RequiresPermission RequiresPermission: android.os.BugreportManager#cancelBugreport(): Method 'cancelBugreport' documentation mentions permissions without declaring @RequiresPermission RequiresPermission: android.os.BugreportManager#preDumpUiData(): @@ -1769,8 +1681,6 @@ SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_PORT_COMPLIANCE_CHANGED: Field 'ACTION_USB_PORT_COMPLIANCE_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_STATE: Field 'ACTION_USB_STATE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) -SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: - Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) SdkConstant: android.service.euicc.EuiccService#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED: Field 'ACTION_DELETE_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) SdkConstant: android.service.euicc.EuiccService#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED: diff --git a/core/api/removed.txt b/core/api/removed.txt index b58c822e39bc..3c7c0d6e6ea1 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -190,22 +190,6 @@ package android.net { } -package android.nfc { - - public final class NfcAdapter { - method @Deprecated public void disableForegroundNdefPush(android.app.Activity); - method @Deprecated public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage); - method @Deprecated public boolean invokeBeam(android.app.Activity); - method @Deprecated public boolean isNdefPushEnabled(); - method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity); - method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity); - method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...); - method @Deprecated public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...); - method @Deprecated public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...); - } - -} - package android.os { public class BatteryManager { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c9e12c0b5948..ebd05446e963 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -862,10 +862,6 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR; } - @FlaggedApi("android.app.bic_client") public final class BackgroundInstallControlManager { - method @FlaggedApi("android.app.bic_client") @NonNull @RequiresPermission(android.Manifest.permission.QUERY_ALL_PACKAGES) public java.util.List<android.content.pm.PackageInfo> getBackgroundInstalledPackages(long); - } - public class BroadcastOptions { method public void clearRequireCompatChange(); method public int getPendingIntentBackgroundActivityStartMode(); @@ -9883,49 +9879,6 @@ package android.net.wifi.sharedconnectivity.service { } -package android.nfc { - - public final class NfcAdapter { - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler, String[]); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable(); - method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean); - method @FlaggedApi("android.nfc.enable_nfc_charging") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableWlc(boolean); - method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState(); - method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn(); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported(); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagIntentAppPreferenceSupported(); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener); - method @FlaggedApi("android.nfc.enable_nfc_charging") public void registerWlcStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.WlcStateListener); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean); - method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderMode(boolean); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener); - method @FlaggedApi("android.nfc.enable_nfc_charging") public void unregisterWlcStateListener(@NonNull android.nfc.NfcAdapter.WlcStateListener); - field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC"; - field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff - field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0 - field public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; // 0xfffffffe - } - - public static interface NfcAdapter.ControllerAlwaysOnListener { - method public void onControllerAlwaysOnChanged(boolean); - } - - public static interface NfcAdapter.NfcUnlockHandler { - method public boolean onUnlockAttempted(android.nfc.Tag); - } - - @FlaggedApi("android.nfc.enable_nfc_charging") public static interface NfcAdapter.WlcStateListener { - method public void onWlcStateChanged(@NonNull android.nfc.WlcLDeviceInfo); - } - -} - package android.nfc.cardemulation { @FlaggedApi("android.nfc.enable_nfc_mainline") public final class AidGroup implements android.os.Parcelable { @@ -9974,11 +9927,6 @@ package android.nfc.cardemulation { field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR; } - public final class CardEmulation { - method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public android.nfc.cardemulation.ApduServiceInfo getPreferredPaymentService(); - method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int); - } - @FlaggedApi("android.nfc.enable_nfc_mainline") public final class NfcFServiceInfo implements android.os.Parcelable { ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents(); diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index 0505af4488ff..6c83fd04b76b 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -239,14 +239,6 @@ BroadcastBehavior: android.net.NetworkScoreManager#ACTION_SCORE_NETWORKS: Field 'ACTION_SCORE_NETWORKS' is missing @BroadcastBehavior BroadcastBehavior: android.net.Proxy#PROXY_CHANGE_ACTION: Field 'PROXY_CHANGE_ACTION' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED: - Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED: - Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: - Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED: - Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior BroadcastBehavior: android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED: Field 'ACTION_DROPBOX_ENTRY_ADDED' is missing @BroadcastBehavior BroadcastBehavior: android.provider.CalendarContract#ACTION_EVENT_REMINDER: @@ -1077,86 +1069,6 @@ RequiresPermission: android.net.vcn.VcnManager#applyVcnNetworkPolicy(android.net Method 'applyVcnNetworkPolicy' documentation mentions permissions already declared by @RequiresPermission RequiresPermission: android.net.vcn.VcnManager#removeVcnNetworkPolicyChangeListener(android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener): Method 'removeVcnNetworkPolicyChangeListener' documentation mentions permissions already declared by @RequiresPermission -RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity): - Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]): - Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String): - Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String): - Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String): - Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]): - Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]): - Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int): - Method 'decrement' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int): - Method 'increment' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int): - Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#restore(int): - Method 'restore' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transfer(int): - Method 'transfer' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]): - Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int): - Method 'readPages' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]): - Method 'writePage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#getNdefMessage(): - Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#isWritable(): - Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#makeReadOnly(): - Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage): - Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage): - Method 'format' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage): - Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#close(): - Method 'close' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#connect(): - Method 'connect' documentation mentions permissions without declaring @RequiresPermission RequiresPermission: android.os.BugreportManager#cancelBugreport(): Method 'cancelBugreport' documentation mentions permissions without declaring @RequiresPermission RequiresPermission: android.os.BugreportManager#preDumpUiData(): @@ -1863,10 +1775,6 @@ SamShouldBeLast: android.media.session.MediaSessionManager#addOnActiveSessionsCh SAM-compatible parameters (such as parameter 1, "sessionListener", in android.media.session.MediaSessionManager.addOnActiveSessionsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.session.MediaSessionManager#addOnSession2TokensChangedListener(android.media.session.MediaSessionManager.OnSession2TokensChangedListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.media.session.MediaSessionManager.addOnSession2TokensChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions -SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle): - SAM-compatible parameters (such as parameter 2, "callback", in android.nfc.NfcAdapter.enableReaderMode) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions -SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler): - SAM-compatible parameters (such as parameter 3, "tagRemovedListener", in android.nfc.NfcAdapter.ignore) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String): SAM-compatible parameters (such as parameter 1, "owner", in android.os.Binder.attachInterface) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int): @@ -1933,8 +1841,6 @@ SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_PORT_COMPLIANCE_CHANGED: Field 'ACTION_USB_PORT_COMPLIANCE_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_STATE: Field 'ACTION_USB_STATE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) -SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: - Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) SdkConstant: android.service.euicc.EuiccService#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED: Field 'ACTION_DELETE_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) SdkConstant: android.service.euicc.EuiccService#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED: diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt index 51b8a11e1791..bbfa0ec3f3c2 100644 --- a/core/api/system-removed.txt +++ b/core/api/system-removed.txt @@ -142,17 +142,6 @@ package android.media.tv { } -package android.nfc { - - public final class NfcAdapter { - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush(); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush(); - method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int); - field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1 - } - -} - package android.os { public class Build { diff --git a/core/java/Android.bp b/core/java/Android.bp index fb1e16a27d0b..eba500dd32b4 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -14,20 +14,12 @@ aidl_library { hdrs: ["android/hardware/HardwareBuffer.aidl"], } -// TODO (b/303286040): Remove this once |ENABLE_NFC_MAINLINE_FLAG| is rolled out -filegroup { - name: "framework-core-nfc-infcadapter-sources", - srcs: [ - "android/nfc/INfcAdapter.aidl", - ], - visibility: ["//frameworks/base/services/core"], -} - filegroup { name: "framework-core-sources", srcs: [ "**/*.java", "**/*.aidl", + ":framework-nfc-non-updatable-sources", ], visibility: ["//frameworks/base"], } diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index 1e1f1554d3a2..5840f02a8650 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -25,8 +25,6 @@ import android.util.Log; import android.util.Property; import android.view.animation.AccelerateDecelerateInterpolator; -import java.lang.ref.WeakReference; - /** * This subclass of {@link ValueAnimator} provides support for animating properties on target objects. * The constructors of this class take parameters to define the target object that will be animated @@ -73,11 +71,7 @@ public final class ObjectAnimator extends ValueAnimator { private static final boolean DBG = false; - /** - * A weak reference to the target object on which the property exists, set - * in the constructor. We'll cancel the animation if this goes away. - */ - private WeakReference<Object> mTarget; + private Object mTarget; private String mPropertyName; @@ -919,7 +913,7 @@ public final class ObjectAnimator extends ValueAnimator { */ @Nullable public Object getTarget() { - return mTarget == null ? null : mTarget.get(); + return mTarget; } @Override @@ -929,7 +923,7 @@ public final class ObjectAnimator extends ValueAnimator { if (isStarted()) { cancel(); } - mTarget = target == null ? null : new WeakReference<Object>(target); + mTarget = target; // New target should cause re-initialization prior to starting mInitialized = false; } @@ -977,13 +971,6 @@ public final class ObjectAnimator extends ValueAnimator { @Override void animateValue(float fraction) { final Object target = getTarget(); - if (mTarget != null && target == null) { - // We lost the target reference, cancel and clean up. Note: we allow null target if the - /// target has never been set. - cancel(); - return; - } - super.animateValue(fraction); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index be2582f6175d..2103055afe50 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1177,23 +1177,12 @@ public class Activity extends ContextThemeWrapper return mApplication; } - /** - * Whether this is a child {@link Activity} of an {@link ActivityGroup}. - * - * @deprecated {@link ActivityGroup} is deprecated. - */ - @Deprecated + /** Is this activity embedded inside of another activity? */ public final boolean isChild() { return mParent != null; } - /** - * Returns the parent {@link Activity} if this is a child {@link Activity} of an - * {@link ActivityGroup}. - * - * @deprecated {@link ActivityGroup} is deprecated. - */ - @Deprecated + /** Return the parent activity if this view is an embedded child. */ public final Activity getParent() { return mParent; } diff --git a/core/java/android/app/BackgroundInstallControlManager.java b/core/java/android/app/BackgroundInstallControlManager.java deleted file mode 100644 index f5b68788f0ea..000000000000 --- a/core/java/android/app/BackgroundInstallControlManager.java +++ /dev/null @@ -1,101 +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 android.app; - -import static android.Manifest.permission.QUERY_ALL_PACKAGES; -import static android.annotation.SystemApi.Client.PRIVILEGED_APPS; - -import android.annotation.FlaggedApi; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SystemApi; -import android.annotation.SystemService; -import android.content.Context; -import android.content.pm.IBackgroundInstallControlService; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.ServiceManager; - -import java.util.List; - -/** - * BackgroundInstallControlManager client allows apps to query apps installed in background. - * - * <p>Any applications that was installed without an accompanying installer UI activity paired - * with recorded user interaction event is considered background installed. This is determined by - * analysis of user-activity logs. - * - * <p>Warning: BackgroundInstallControl should not be considered a reliable or accurate - * determination of background install application. Consumers can use this as a supplementary - * signal, but must perform additional due diligence to confirm the install nature of the package. - * - * @hide - */ -@FlaggedApi(Flags.FLAG_BIC_CLIENT) -@SystemApi(client = PRIVILEGED_APPS) -@SystemService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE) -public final class BackgroundInstallControlManager { - - private static final String TAG = "BackgroundInstallControlManager"; - private static IBackgroundInstallControlService sService; - private final Context mContext; - - BackgroundInstallControlManager(Context context) { - mContext = context; - } - - private static IBackgroundInstallControlService getService() { - if (sService == null) { - sService = - IBackgroundInstallControlService.Stub.asInterface( - ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); - } - return sService; - } - - /** - * Returns a full list of {@link PackageInfo} of apps currently installed that are considered - * installed in the background. - * - * <p>Refer to top level doc {@link BackgroundInstallControlManager} for more details on - * background-installed applications. - * <p> - * - * @param flags - Flags will be used to call - * {@link PackageManager#getInstalledPackages(PackageInfoFlags)} to retrieve installed packages. - * @return A list of packages retrieved from {@link PackageManager} with non-background - * installed app filter applied. - * - * @hide - */ - @FlaggedApi(Flags.FLAG_BIC_CLIENT) - @SystemApi - @RequiresPermission(QUERY_ALL_PACKAGES) - public @NonNull List<PackageInfo> getBackgroundInstalledPackages( - @PackageManager.PackageInfoFlagsBits long flags) { - try { - return getService() - .getBackgroundInstalledPackages(flags, mContext.getUserId()) - .getList(); - } catch (SecurityException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 390fa2212298..9cf732abb86a 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1603,20 +1603,6 @@ public final class SystemServiceRegistry { } }); - // DO NOT do a flag check like this unless the flag is read-only. - // (because this code is executed during preload in zygote.) - // If the flag is mutable, the check should be inside CachedServiceFetcher. - if (Flags.bicClient()) { - registerService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, - BackgroundInstallControlManager.class, - new CachedServiceFetcher<BackgroundInstallControlManager>() { - @Override - public BackgroundInstallControlManager createService(ContextImpl ctx) { - return new BackgroundInstallControlManager(ctx); - } - }); - } - sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/core/java/android/app/background_install_control_manager.aconfig b/core/java/android/app/background_install_control_manager.aconfig deleted file mode 100644 index 029b93ab4534..000000000000 --- a/core/java/android/app/background_install_control_manager.aconfig +++ /dev/null @@ -1,9 +0,0 @@ -package: "android.app" - -flag { - namespace: "background_install_control" - name: "bic_client" - description: "System API for background install control." - is_fixed_read_only: true - bug: "287507984" -} diff --git a/core/java/android/app/wearable/OWNERS b/core/java/android/app/wearable/OWNERS index 073e2d79850b..497eaf0e40f1 100644 --- a/core/java/android/app/wearable/OWNERS +++ b/core/java/android/app/wearable/OWNERS @@ -1,3 +1,5 @@ charliewang@google.com +hackz@google.com oni@google.com +tomchan@google.com volnov@google.com
\ No newline at end of file diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index c7a75ed5ea9c..e9b94c9f5791 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -41,6 +41,7 @@ import android.content.res.Configuration; import android.database.Cursor; import android.database.MatrixCursor; import android.database.SQLException; +import android.multiuser.Flags; import android.net.Uri; import android.os.AsyncTask; import android.os.Binder; @@ -146,6 +147,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall private boolean mExported; private boolean mNoPerms; private boolean mSingleUser; + private boolean mSystemUserOnly; private SparseBooleanArray mUsersRedirectedToOwnerForMedia = new SparseBooleanArray(); private ThreadLocal<AttributionSource> mCallingAttributionSource; @@ -377,7 +379,9 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall != PermissionChecker.PERMISSION_GRANTED && getContext().checkUriPermission(userUri, Binder.getCallingPid(), callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION) - != PackageManager.PERMISSION_GRANTED) { + != PackageManager.PERMISSION_GRANTED + && !deniedAccessSystemUserOnlyProvider(callingUserId, + mSystemUserOnly)) { FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION, enumCheckUriPermission, callingUid, uri.getAuthority(), type); @@ -865,6 +869,10 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall boolean checkUser(int pid, int uid, Context context) { final int callingUserId = UserHandle.getUserId(uid); + if (deniedAccessSystemUserOnlyProvider(callingUserId, mSystemUserOnly)) { + return false; + } + if (callingUserId == context.getUserId() || mSingleUser) { return true; } @@ -987,6 +995,9 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall // last chance, check against any uri grants final int callingUserId = UserHandle.getUserId(uid); + if (deniedAccessSystemUserOnlyProvider(callingUserId, mSystemUserOnly)) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid)) ? maybeAddUserId(uri, callingUserId) : uri; if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION) @@ -2623,6 +2634,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall setPathPermissions(info.pathPermissions); mExported = info.exported; mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0; + mSystemUserOnly = (info.flags & ProviderInfo.FLAG_SYSTEM_USER_ONLY) != 0; setAuthorities(info.authority); } if (Build.IS_DEBUGGABLE) { @@ -2756,6 +2768,11 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall String auth = uri.getAuthority(); if (!mSingleUser) { int userId = getUserIdFromAuthority(auth, UserHandle.USER_CURRENT); + if (deniedAccessSystemUserOnlyProvider(mContext.getUserId(), + mSystemUserOnly)) { + throw new SecurityException("Trying to query a SYSTEM user only content" + + " provider from user:" + mContext.getUserId()); + } if (userId != UserHandle.USER_CURRENT && userId != mContext.getUserId() // Since userId specified in content uri, the provider userId would be @@ -2929,4 +2946,16 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall Trace.traceBegin(traceTag, methodName + subInfo); } } + /** + * Return true if access to content provider is denied because it's a SYSTEM user only + * provider and the calling user is not the SYSTEM user. + * + * @param callingUserId UserId of the caller accessing the content provider. + * @param systemUserOnly true when the content provider is only available for the SYSTEM user. + */ + private static boolean deniedAccessSystemUserOnlyProvider(int callingUserId, + boolean systemUserOnly) { + return Flags.enableSystemUserOnlyForServicesAndProviders() + && (callingUserId != UserHandle.USER_SYSTEM && systemUserOnly); + } } diff --git a/core/java/android/content/pm/IBackgroundInstallControlService.aidl b/core/java/android/content/pm/IBackgroundInstallControlService.aidl index 4bc8fe16b249..c8e7caebc821 100644 --- a/core/java/android/content/pm/IBackgroundInstallControlService.aidl +++ b/core/java/android/content/pm/IBackgroundInstallControlService.aidl @@ -16,20 +16,11 @@ package android.content.pm; - import android.content.pm.ParceledListSlice; -import android.os.IRemoteCallback; /** * {@hide} */ interface IBackgroundInstallControlService { - @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest.permission.QUERY_ALL_PACKAGES)") ParceledListSlice getBackgroundInstalledPackages(long flags, int userId); - - @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})") - void registerBackgroundInstallCallback(IRemoteCallback callback); - - @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})") - void unregisterBackgroundInstallCallback(IRemoteCallback callback); -}
\ No newline at end of file +} diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java index 9e553dbfb719..de33fa8b2328 100644 --- a/core/java/android/content/pm/ProviderInfo.java +++ b/core/java/android/content/pm/ProviderInfo.java @@ -89,6 +89,15 @@ public final class ProviderInfo extends ComponentInfo public static final int FLAG_VISIBLE_TO_INSTANT_APP = 0x100000; /** + * Bit in {@link #flags}: If set, this provider will only be available + * for the system user. + * Set from the android.R.attr#systemUserOnly attribute. + * In Sync with {@link ActivityInfo#FLAG_SYSTEM_USER_ONLY} + * @hide + */ + public static final int FLAG_SYSTEM_USER_ONLY = ActivityInfo.FLAG_SYSTEM_USER_ONLY; + + /** * Bit in {@link #flags}: If set, a single instance of the provider will * run for all users on the device. Set from the * {@link android.R.attr#singleUser} attribute. diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java index ae46c027505e..2b378b1f09d0 100644 --- a/core/java/android/content/pm/ServiceInfo.java +++ b/core/java/android/content/pm/ServiceInfo.java @@ -101,6 +101,14 @@ public class ServiceInfo extends ComponentInfo public static final int FLAG_VISIBLE_TO_INSTANT_APP = 0x100000; /** + * @hide Bit in {@link #flags}: If set, this service will only be available + * for the system user. + * Set from the android.R.attr#systemUserOnly attribute. + * In Sync with {@link ActivityInfo#FLAG_SYSTEM_USER_ONLY} + */ + public static final int FLAG_SYSTEM_USER_ONLY = ActivityInfo.FLAG_SYSTEM_USER_ONLY; + + /** * Bit in {@link #flags}: If set, a single instance of the service will * run for all users on the device. Set from the * {@link android.R.attr#singleUser} attribute. diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index c7797c719e2c..10368653f0c4 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -70,4 +70,11 @@ flag { namespace: "profile_experiences" description: "Add support for Private Space in resolver sheet" bug: "307515485" -}
\ No newline at end of file +} +flag { + name: "enable_system_user_only_for_services_and_providers" + namespace: "multiuser" + description: "Enable systemUserOnly manifest attribute for services and providers." + bug: "302354856" + is_fixed_read_only: true +} diff --git a/core/java/android/credentials/GetCandidateCredentialsException.java b/core/java/android/credentials/GetCandidateCredentialsException.java index 40650d02a93e..0ac5f6c01212 100644 --- a/core/java/android/credentials/GetCandidateCredentialsException.java +++ b/core/java/android/credentials/GetCandidateCredentialsException.java @@ -46,6 +46,17 @@ public class GetCandidateCredentialsException extends Exception { "android.credentials.GetCandidateCredentialsException.TYPE_NO_CREDENTIAL"; @NonNull + public static final String TYPE_USER_CANCELED = + "android.credentials.GetCredentialException.TYPE_USER_CANCELED"; + /** + * The error type value for when the given operation failed due to internal interruption. + * Retrying the same operation should fix the error. + */ + @NonNull + public static final String TYPE_INTERRUPTED = + "android.credentials.GetCredentialException.TYPE_INTERRUPTED"; + + @NonNull private final String mType; /** Returns the specific exception type. */ diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index c727a6034006..561db9c8a8ce 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -102,6 +102,24 @@ public class VcnManager { public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY = "vcn_network_selection_wifi_exit_rssi_threshold"; + /** + * Key for the interval to poll IpSecTransformState for packet loss monitoring + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY = + "vcn_network_selection_poll_ipsec_state_interval_seconds"; + + /** + * Key for the threshold of IPSec packet loss rate + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY = + "vcn_network_selection_ipsec_packet_loss_percent_threshold"; + // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz /** @@ -148,6 +166,8 @@ public class VcnManager { new String[] { VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, + VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY, + VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY, VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index 67a1906d48ed..7afd72195fcb 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -12,4 +12,11 @@ flag { namespace: "vcn" description: "Feature flag for adjustable safe mode timeout" bug: "317406085" +} + +flag{ + name: "network_metric_monitor" + namespace: "vcn" + description: "Feature flag for enabling network metric monitor" + bug: "282996138" }
\ No newline at end of file diff --git a/core/java/android/nfc/TEST_MAPPING b/core/java/android/nfc/TEST_MAPPING deleted file mode 100644 index 5b5ea3790010..000000000000 --- a/core/java/android/nfc/TEST_MAPPING +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presubmit": [ - { - "name": "NfcManagerTests" - }, - { - "name": "CtsNfcTestCases" - } - ] -} diff --git a/core/java/android/nfc/tech/OWNERS b/core/java/android/nfc/tech/OWNERS deleted file mode 100644 index 35e9713f5715..000000000000 --- a/core/java/android/nfc/tech/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 48448 -include platform/packages/apps/Nfc:/OWNERS diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java index 37bde3db5e14..746278fc296c 100644 --- a/core/java/android/os/PerformanceHintManager.java +++ b/core/java/android/os/PerformanceHintManager.java @@ -204,7 +204,7 @@ public final class PerformanceHintManager { } /** - * Updates this session's target duration for each cycle of work. + * Updates this session's target total duration for each cycle of work. * * @param targetDurationNanos the new desired duration in nanoseconds */ diff --git a/core/java/android/os/WorkDuration.java b/core/java/android/os/WorkDuration.java index 4fdc34fb60d1..2ebcd830be29 100644 --- a/core/java/android/os/WorkDuration.java +++ b/core/java/android/os/WorkDuration.java @@ -26,7 +26,7 @@ import java.util.Objects; * in each component, see * {@link PerformanceHintManager.Session#reportActualWorkDuration(WorkDuration)}. * - * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}. + * All timings should be in {@link SystemClock#uptimeNanos()} and measured in wall time. */ @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) public final class WorkDuration implements Parcelable { @@ -50,17 +50,9 @@ public final class WorkDuration implements Parcelable { public WorkDuration() {} - public WorkDuration(long workPeriodStartTimestampNanos, - long actualTotalDurationNanos, - long actualCpuDurationNanos, - long actualGpuDurationNanos) { - mWorkPeriodStartTimestampNanos = workPeriodStartTimestampNanos; - mActualTotalDurationNanos = actualTotalDurationNanos; - mActualCpuDurationNanos = actualCpuDurationNanos; - mActualGpuDurationNanos = actualGpuDurationNanos; - } - /** + * Constructor for testing. + * * @hide */ public WorkDuration(long workPeriodStartTimestampNanos, @@ -86,7 +78,7 @@ public final class WorkDuration implements Parcelable { /** * Sets the work period start timestamp in nanoseconds. * - * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}. + * All timings should be in {@link SystemClock#uptimeNanos()}. */ public void setWorkPeriodStartTimestampNanos(long workPeriodStartTimestampNanos) { if (workPeriodStartTimestampNanos <= 0) { @@ -99,7 +91,7 @@ public final class WorkDuration implements Parcelable { /** * Sets the actual total duration in nanoseconds. * - * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}. + * All timings should be in {@link SystemClock#uptimeNanos()}. */ public void setActualTotalDurationNanos(long actualTotalDurationNanos) { if (actualTotalDurationNanos <= 0) { @@ -111,7 +103,7 @@ public final class WorkDuration implements Parcelable { /** * Sets the actual CPU duration in nanoseconds. * - * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}. + * All timings should be in {@link SystemClock#uptimeNanos()}. */ public void setActualCpuDurationNanos(long actualCpuDurationNanos) { if (actualCpuDurationNanos <= 0) { @@ -123,7 +115,7 @@ public final class WorkDuration implements Parcelable { /** * Sets the actual GPU duration in nanoseconds. * - * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}. + * All timings should be in {@link SystemClock#uptimeNanos()}. */ public void setActualGpuDurationNanos(long actualGpuDurationNanos) { if (actualGpuDurationNanos < 0) { @@ -135,7 +127,7 @@ public final class WorkDuration implements Parcelable { /** * Returns the work period start timestamp based in nanoseconds. * - * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}. + * All timings should be in {@link SystemClock#uptimeNanos()}. */ public long getWorkPeriodStartTimestampNanos() { return mWorkPeriodStartTimestampNanos; @@ -144,7 +136,7 @@ public final class WorkDuration implements Parcelable { /** * Returns the actual total duration in nanoseconds. * - * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}. + * All timings should be in {@link SystemClock#uptimeNanos()}. */ public long getActualTotalDurationNanos() { return mActualTotalDurationNanos; @@ -153,7 +145,7 @@ public final class WorkDuration implements Parcelable { /** * Returns the actual CPU duration in nanoseconds. * - * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}. + * All timings should be in {@link SystemClock#uptimeNanos()}. */ public long getActualCpuDurationNanos() { return mActualCpuDurationNanos; @@ -162,7 +154,7 @@ public final class WorkDuration implements Parcelable { /** * Returns the actual GPU duration in nanoseconds. * - * All timings should be in {@link SystemClock#elapsedRealtimeNanos()}. + * All timings should be in {@link SystemClock#uptimeNanos()}. */ public long getActualGpuDurationNanos() { return mActualGpuDurationNanos; diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 7d9c0a37a13f..2d657c2813a5 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -445,6 +445,19 @@ public class DreamService extends Service implements Window.Callback { } /** + * Retrieves the current {@link android.app.Activity} associated with the dream. + * This method behaves similarly to calling {@link android.app.Activity#getActivity()}. + * + * @return The current activity, or null if the dream is not associated with an activity + * or not started. + * + * @hide + */ + public Activity getActivity() { + return mActivity; + } + + /** * Inflates a layout resource and set it to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(int)}. * diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 92c516c38dcc..7658af53a7f8 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -42,6 +42,7 @@ import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.os.BadParcelableException; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -1056,7 +1057,7 @@ public abstract class NotificationListenerService extends Service { ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface() .getActiveNotificationsFromListener(mWrapper, keys, trim); return cleanUpNotificationList(parceledList); - } catch (android.os.RemoteException ex) { + } catch (android.os.RemoteException | BadParcelableException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } return null; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 45a0c205a09b..54248be74e04 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -64,6 +64,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.time.Instant; import java.util.Arrays; import java.util.Calendar; import java.util.Date; @@ -233,6 +234,7 @@ public class ZenModeConfig implements Parcelable { private static final String MANUAL_TAG = "manual"; private static final String AUTOMATIC_TAG = "automatic"; + private static final String AUTOMATIC_DELETED_TAG = "deleted"; private static final String RULE_ATT_ID = "ruleId"; private static final String RULE_ATT_ENABLED = "enabled"; @@ -251,6 +253,7 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_USER_MODIFIED_FIELDS = "userModifiedFields"; private static final String RULE_ATT_ICON = "rule_icon"; private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc"; + private static final String RULE_ATT_DELETION_INSTANT = "deletionInstant"; private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale"; private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY = @@ -292,6 +295,10 @@ public class ZenModeConfig implements Parcelable { @UnsupportedAppUsage public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>(); + // Note: Map is *pkg|conditionId* (see deletedRuleKey()) -> ZenRule, + // unlike automaticRules (which is id -> rule). + public final ArrayMap<String, ZenRule> deletedRules = new ArrayMap<>(); + @UnsupportedAppUsage public ZenModeConfig() { } @@ -306,15 +313,9 @@ public class ZenModeConfig implements Parcelable { allowMessagesFrom = source.readInt(); user = source.readInt(); manualRule = source.readParcelable(null, ZenRule.class); - final int len = source.readInt(); - if (len > 0) { - final String[] ids = new String[len]; - final ZenRule[] rules = new ZenRule[len]; - source.readStringArray(ids); - source.readTypedArray(rules, ZenRule.CREATOR); - for (int i = 0; i < len; i++) { - automaticRules.put(ids[i], rules[i]); - } + readRulesFromParcel(automaticRules, source); + if (Flags.modesApi()) { + readRulesFromParcel(deletedRules, source); } allowAlarms = source.readInt() == 1; allowMedia = source.readInt() == 1; @@ -328,6 +329,19 @@ public class ZenModeConfig implements Parcelable { } } + private static void readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source) { + final int len = source.readInt(); + if (len > 0) { + final String[] ids = new String[len]; + final ZenRule[] rules = new ZenRule[len]; + source.readStringArray(ids); + source.readTypedArray(rules, ZenRule.CREATOR); + for (int i = 0; i < len; i++) { + ruleMap.put(ids[i], rules[i]); + } + } + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(allowCalls ? 1 : 0); @@ -339,19 +353,9 @@ public class ZenModeConfig implements Parcelable { dest.writeInt(allowMessagesFrom); dest.writeInt(user); dest.writeParcelable(manualRule, 0); - if (!automaticRules.isEmpty()) { - final int len = automaticRules.size(); - final String[] ids = new String[len]; - final ZenRule[] rules = new ZenRule[len]; - for (int i = 0; i < len; i++) { - ids[i] = automaticRules.keyAt(i); - rules[i] = automaticRules.valueAt(i); - } - dest.writeInt(len); - dest.writeStringArray(ids); - dest.writeTypedArray(rules, 0); - } else { - dest.writeInt(0); + writeRulesToParcel(automaticRules, dest); + if (Flags.modesApi()) { + writeRulesToParcel(deletedRules, dest); } dest.writeInt(allowAlarms ? 1 : 0); dest.writeInt(allowMedia ? 1 : 0); @@ -365,6 +369,23 @@ public class ZenModeConfig implements Parcelable { } } + private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest) { + if (!ruleMap.isEmpty()) { + final int len = ruleMap.size(); + final String[] ids = new String[len]; + final ZenRule[] rules = new ZenRule[len]; + for (int i = 0; i < len; i++) { + ids[i] = ruleMap.keyAt(i); + rules[i] = ruleMap.valueAt(i); + } + dest.writeInt(len); + dest.writeStringArray(ids); + dest.writeTypedArray(rules, 0); + } else { + dest.writeInt(0); + } + } + @Override public String toString() { StringBuilder sb = new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[') @@ -389,23 +410,26 @@ public class ZenModeConfig implements Parcelable { } else { sb.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd); } - return sb.append(",\nautomaticRules=").append(rulesToString()) - .append(",\nmanualRule=").append(manualRule) - .append(']').toString(); + sb.append(",\nautomaticRules=").append(rulesToString(automaticRules)) + .append(",\nmanualRule=").append(manualRule); + if (Flags.modesApi()) { + sb.append(",\ndeletedRules=").append(rulesToString(deletedRules)); + } + return sb.append(']').toString(); } - private String rulesToString() { - if (automaticRules.isEmpty()) { + private static String rulesToString(ArrayMap<String, ZenRule> ruleList) { + if (ruleList.isEmpty()) { return "{}"; } - StringBuilder buffer = new StringBuilder(automaticRules.size() * 28); + StringBuilder buffer = new StringBuilder(ruleList.size() * 28); buffer.append("{\n"); - for (int i = 0; i < automaticRules.size(); i++) { + for (int i = 0; i < ruleList.size(); i++) { if (i > 0) { buffer.append(",\n"); } - Object value = automaticRules.valueAt(i); + Object value = ruleList.valueAt(i); buffer.append(value); } buffer.append('}'); @@ -487,7 +511,9 @@ public class ZenModeConfig implements Parcelable { && other.allowConversations == allowConversations && other.allowConversationsFrom == allowConversationsFrom; if (Flags.modesApi()) { - return eq && other.allowPriorityChannels == allowPriorityChannels; + return eq + && Objects.equals(other.deletedRules, deletedRules) + && other.allowPriorityChannels == allowPriorityChannels; } return eq; } @@ -644,12 +670,20 @@ public class ZenModeConfig implements Parcelable { DEFAULT_SUPPRESSED_VISUAL_EFFECTS); } else if (MANUAL_TAG.equals(tag)) { rt.manualRule = readRuleXml(parser); - } else if (AUTOMATIC_TAG.equals(tag)) { + } else if (AUTOMATIC_TAG.equals(tag) + || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) { final String id = parser.getAttributeValue(null, RULE_ATT_ID); final ZenRule automaticRule = readRuleXml(parser); if (id != null && automaticRule != null) { automaticRule.id = id; - rt.automaticRules.put(id, automaticRule); + if (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag)) { + String deletedRuleKey = deletedRuleKey(automaticRule); + if (deletedRuleKey != null) { + rt.deletedRules.put(deletedRuleKey, automaticRule); + } + } else if (AUTOMATIC_TAG.equals(tag)) { + rt.automaticRules.put(id, automaticRule); + } } } else if (STATE_TAG.equals(tag)) { rt.areChannelsBypassingDnd = safeBoolean(parser, @@ -660,13 +694,24 @@ public class ZenModeConfig implements Parcelable { throw new IllegalStateException("Failed to reach END_DOCUMENT"); } + /** Generates the map key used for a {@link ZenRule} in {@link #deletedRules}. */ + @Nullable + public static String deletedRuleKey(ZenRule rule) { + if (rule.pkg != null && rule.conditionId != null) { + return rule.pkg + "|" + rule.conditionId.toString(); + } else { + return null; + } + } + /** * Writes XML of current ZenModeConfig * @param out serializer * @param version uses XML_VERSION if version is null * @throws IOException */ - public void writeXml(TypedXmlSerializer out, Integer version) throws IOException { + public void writeXml(TypedXmlSerializer out, Integer version, boolean forBackup) + throws IOException { out.startTag(null, ZEN_TAG); out.attribute(null, ZEN_ATT_VERSION, version == null ? Integer.toString(XML_VERSION) : Integer.toString(version)); @@ -707,6 +752,15 @@ public class ZenModeConfig implements Parcelable { writeRuleXml(automaticRule, out); out.endTag(null, AUTOMATIC_TAG); } + if (Flags.modesApi() && !forBackup) { + for (int i = 0; i < deletedRules.size(); i++) { + final ZenRule deletedRule = deletedRules.valueAt(i); + out.startTag(null, AUTOMATIC_DELETED_TAG); + out.attribute(null, RULE_ATT_ID, deletedRule.id); + writeRuleXml(deletedRule, out); + out.endTag(null, AUTOMATIC_DELETED_TAG); + } + } out.startTag(null, STATE_TAG); out.attributeBoolean(null, STATE_ATT_CHANNELS_BYPASSING_DND, areChannelsBypassingDnd); @@ -752,6 +806,11 @@ public class ZenModeConfig implements Parcelable { rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC); rt.type = safeInt(parser, RULE_ATT_TYPE, AutomaticZenRule.TYPE_UNKNOWN); rt.userModifiedFields = safeInt(parser, RULE_ATT_USER_MODIFIED_FIELDS, 0); + Long deletionInstant = tryParseLong( + parser.getAttributeValue(null, RULE_ATT_DELETION_INSTANT), null); + if (deletionInstant != null) { + rt.deletionInstant = Instant.ofEpochMilli(deletionInstant); + } } return rt; } @@ -799,6 +858,10 @@ public class ZenModeConfig implements Parcelable { } out.attributeInt(null, RULE_ATT_TYPE, rule.type); out.attributeInt(null, RULE_ATT_USER_MODIFIED_FIELDS, rule.userModifiedFields); + if (rule.deletionInstant != null) { + out.attributeLong(null, RULE_ATT_DELETION_INSTANT, + rule.deletionInstant.toEpochMilli()); + } } } @@ -1998,6 +2061,7 @@ public class ZenModeConfig implements Parcelable { public String iconResName; public boolean allowManualInvocation; public int userModifiedFields; + @Nullable public Instant deletionInstant; // Only set on deleted rules. public ZenRule() { } @@ -2031,6 +2095,9 @@ public class ZenModeConfig implements Parcelable { triggerDescription = source.readString(); type = source.readInt(); userModifiedFields = source.readInt(); + if (source.readInt() == 1) { + deletionInstant = Instant.ofEpochMilli(source.readLong()); + } } } @@ -2091,6 +2158,12 @@ public class ZenModeConfig implements Parcelable { dest.writeString(triggerDescription); dest.writeInt(type); dest.writeInt(userModifiedFields); + if (deletionInstant != null) { + dest.writeInt(1); + dest.writeLong(deletionInstant.toEpochMilli()); + } else { + dest.writeInt(0); + } } } @@ -2121,6 +2194,9 @@ public class ZenModeConfig implements Parcelable { .append(",triggerDescription=").append(triggerDescription) .append(",type=").append(type) .append(",userModifiedFields=").append(userModifiedFields); + if (deletionInstant != null) { + sb.append(",deletionInstant=").append(deletionInstant); + } } return sb.append(']').toString(); @@ -2180,7 +2256,8 @@ public class ZenModeConfig implements Parcelable { && Objects.equals(other.iconResName, iconResName) && Objects.equals(other.triggerDescription, triggerDescription) && other.type == type - && other.userModifiedFields == userModifiedFields; + && other.userModifiedFields == userModifiedFields + && Objects.equals(other.deletionInstant, deletionInstant); } return finalEquals; @@ -2192,7 +2269,7 @@ public class ZenModeConfig implements Parcelable { return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, component, configurationActivity, pkg, id, enabler, zenPolicy, zenDeviceEffects, modified, allowManualInvocation, iconResName, - triggerDescription, type, userModifiedFields); + triggerDescription, type, userModifiedFields, deletionInstant); } return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, component, configurationActivity, pkg, id, enabler, zenPolicy, modified); diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java index 8902368072bf..91ef11cf1d2d 100644 --- a/core/java/android/service/notification/ZenModeDiff.java +++ b/core/java/android/service/notification/ZenModeDiff.java @@ -30,6 +30,11 @@ import java.util.Set; /** * ZenModeDiff is a utility class meant to encapsulate the diff between ZenModeConfigs and their * subcomponents (automatic and manual ZenRules). + * + * <p>Note that this class is intended to detect <em>meaningful</em> differences, so objects that + * are not identical (as per their {@code equals()} implementation) can still produce an empty diff + * if only "metadata" fields are updated. + * * @hide */ public class ZenModeDiff { @@ -467,7 +472,6 @@ public class ZenModeDiff { public static final String FIELD_ICON_RES = "iconResName"; public static final String FIELD_TRIGGER_DESCRIPTION = "triggerDescription"; public static final String FIELD_TYPE = "type"; - public static final String FIELD_USER_MODIFIED_FIELDS = "userModifiedFields"; // NOTE: new field strings must match the variable names in ZenModeConfig.ZenRule // Special field to track whether this rule became active or inactive @@ -563,10 +567,6 @@ public class ZenModeDiff { if (!Objects.equals(from.iconResName, to.iconResName)) { addField(FIELD_ICON_RES, new FieldDiff<>(from.iconResName, to.iconResName)); } - if (from.userModifiedFields != to.userModifiedFields) { - addField(FIELD_USER_MODIFIED_FIELDS, - new FieldDiff<>(from.userModifiedFields, to.userModifiedFields)); - } } } diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig index a2ade6a9473f..3008b8d45252 100644 --- a/core/java/android/service/notification/flags.aconfig +++ b/core/java/android/service/notification/flags.aconfig @@ -21,3 +21,11 @@ flag { description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices" bug: "306271190" } + +flag { + name: "callstyle_callback_api" + namespace: "systemui" + description: "Guards the new CallStyleNotificationEventsCallback" + bug: "305095040" + is_fixed_read_only: true +}
\ No newline at end of file diff --git a/core/java/android/service/wearable/OWNERS b/core/java/android/service/wearable/OWNERS index 073e2d79850b..eca48b742cef 100644 --- a/core/java/android/service/wearable/OWNERS +++ b/core/java/android/service/wearable/OWNERS @@ -1,3 +1 @@ -charliewang@google.com -oni@google.com -volnov@google.com
\ No newline at end of file +include /core/java/android/app/wearable/OWNERS
\ No newline at end of file diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index 118d0284fb9e..1ca7ac77158c 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -16,6 +16,9 @@ package android.speech; +import static android.speech.flags.Flags.FLAG_MULTILANG_EXTRA_LAUNCH; + +import android.annotation.FlaggedApi; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -653,4 +656,30 @@ public class RecognizerIntent { */ public static final String EXTRA_LANGUAGE_SWITCH_ALLOWED_LANGUAGES = "android.speech.extra.LANGUAGE_SWITCH_ALLOWED_LANGUAGES"; + + /** + * Optional integer to use for {@link #EXTRA_ENABLE_LANGUAGE_SWITCH}. If set, the language + * switch will be deactivated when LANGUAGE_SWITCH_MAX_SWITCHES reached. + * + * <p> Depending on the recognizer implementation, this flag may have no effect. + * + * @see #EXTRA_ENABLE_LANGUAGE_SWITCH + */ + @FlaggedApi(FLAG_MULTILANG_EXTRA_LAUNCH) + public static final String EXTRA_LANGUAGE_SWITCH_MAX_SWITCHES = + "android.speech.extra.LANGUAGE_SWITCH_MAX_SWITCHES"; + + /** + * Optional integer to use for {@link #EXTRA_ENABLE_LANGUAGE_SWITCH}. If set, the language + * switch will only be activated for this value of ms of audio since the START_OF_SPEECH. This + * could provide a more stable recognition result when the language switch is only required in + * the beginning of the session. + * + * <p> Depending on the recognizer implementation, this flag may have no effect. + * + * @see #EXTRA_ENABLE_LANGUAGE_SWITCH + */ + @FlaggedApi(FLAG_MULTILANG_EXTRA_LAUNCH) + public static final String EXTRA_LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS = + "android.speech.extra.LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS"; } diff --git a/core/java/android/speech/flags/speech_flags.aconfig b/core/java/android/speech/flags/speech_flags.aconfig new file mode 100644 index 000000000000..fd8012746a27 --- /dev/null +++ b/core/java/android/speech/flags/speech_flags.aconfig @@ -0,0 +1,8 @@ +package: "android.speech.flags" + +flag { + name: "multilang_extra_launch" + namespace: "machine_learning" + description: "Feature flag for adding new extra for multi-lang feature" + bug: "312489931" +} diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index a74cbe46b404..f0e673b3e3ac 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -378,6 +378,13 @@ public final class Choreographer { } /** + * @hide + */ + public Looper getLooper() { + return mLooper; + } + + /** * The amount of time, in milliseconds, between each frame of the animation. * <p> * This is a requested time that the animation will attempt to honor, but the actual delay diff --git a/core/java/android/view/SurfaceControlInputReceiver.java b/core/java/android/view/SurfaceControlInputReceiver.java new file mode 100644 index 000000000000..81e444859b76 --- /dev/null +++ b/core/java/android/view/SurfaceControlInputReceiver.java @@ -0,0 +1,43 @@ +/* + * Copyright 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.view; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; + +import com.android.window.flags.Flags; + +/** + * Provides a mechanism for a SurfaceControl to receive input events. + */ +@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) +public interface SurfaceControlInputReceiver { + /** + * When input events are batched, this is called at most once per frame. When non batched, this + * is called immediately for the input event. + * + * @param event The input event that was received. This input event object will become invalid + * and recycled after this method is invoked. If there is need to persist this + * object beyond the scope of this method, the overriding code should make a copy + * of this object. For example, using + * {@link MotionEvent#obtain(MotionEvent other)} or + * {@link KeyEvent#KeyEvent(KeyEvent)} } + * @return true if the event was handled, false otherwise. + */ + boolean onInputEvent(@NonNull InputEvent event); + +} diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index d8fa41589f29..96aba4c352e6 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -109,6 +109,7 @@ import android.graphics.Region; import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemProperties; @@ -6015,4 +6016,94 @@ public interface WindowManager extends ViewManager { default void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) { throw new UnsupportedOperationException(); } + + /** + * Registers a {@link SurfaceControlInputReceiver} for a {@link SurfaceControl} that will + * receive batched input event. For those events that are batched, the invocation will happen + * once per {@link Choreographer} frame, and other input events will be delivered immediately. + * This is different from + * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, + * SurfaceControlInputReceiver)} in that the input events are received batched. The caller must + * invoke {@link #unregisterSurfaceControlInputReceiver(IBinder)} to clean up the resources when + * no longer needing to use the {@link SurfaceControlInputReceiver} + * + * @param displayId The display that the SurfaceControl will be placed on. Input will + * only work + * if SurfaceControl is on that display and that display was touched. + * @param surfaceControl The SurfaceControl to register the InputChannel for + * @param hostToken The host token to link the InputChannel for. This is primarily for ANRs + * to ensure the host receives the ANR if any issues with touch on the + * InputChannel + * @param choreographer The Choreographer used for batching. This should match the rendering + * Choreographer. + * @param receiver The SurfaceControlInputReceiver that will receive the input events + * @return an {@link IBinder} token that is used to unregister the input receiver via + * {@link #unregisterSurfaceControlInputReceiver(IBinder)}. + * @see #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, + * SurfaceControlInputReceiver) + */ + @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) + @NonNull + default IBinder registerBatchedSurfaceControlInputReceiver(int displayId, + @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, + @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { + throw new UnsupportedOperationException( + "registerBatchedSurfaceControlInputReceiver is not implemented"); + } + + /** + * Registers a {@link SurfaceControlInputReceiver} for a {@link SurfaceControl} that will + * receive every input event. This is different than calling @link + * #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer, + * SurfaceControlInputReceiver)} in that the input events are received unbatched. The caller + * must invoke {@link #unregisterSurfaceControlInputReceiver(IBinder)} to clean up the resources + * when no longer needing to use the {@link SurfaceControlInputReceiver} + * + * @param displayId The display that the SurfaceControl will be placed on. Input will only + * work if SurfaceControl is on that display and that display was + * touched. + * @param hostToken The host token to link the InputChannel for. This is primarily for ANRs + * to ensure the host receives the ANR if any issues with touch on the + * InputChannel + * @param surfaceControl The SurfaceControl to register the InputChannel for + * @param looper The looper to use when invoking callbacks. + * @param receiver The SurfaceControlInputReceiver that will receive the input events + * @return an {@link IBinder} token that is used to unregister the input receiver via + * {@link #unregisterSurfaceControlInputReceiver(IBinder)}. + * @see #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer, + * SurfaceControlInputReceiver) + **/ + @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) + @NonNull + default IBinder registerUnbatchedSurfaceControlInputReceiver(int displayId, + @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, + @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { + throw new UnsupportedOperationException( + "registerUnbatchedSurfaceControlInputReceiver is not implemented"); + } + + /** + * Unregisters and cleans up the registered {@link SurfaceControlInputReceiver} for the + * specified token. + * <p> + * Must be called on the same {@link Looper} thread to which was passed to the + * {@link #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, + * Choreographer, + * SurfaceControlInputReceiver)} or + * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, + * SurfaceControlInputReceiver)} + * + * @param token The token that was returned via + * {@link #registerBatchedSurfaceControlInputReceiver(int, IBinder, + * SurfaceControl, + * Choreographer, SurfaceControlInputReceiver)} or + * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, + * SurfaceControl, + * Looper, SurfaceControlInputReceiver)} + */ + @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) + default void unregisterSurfaceControlInputReceiver(@NonNull IBinder token) { + throw new UnsupportedOperationException( + "unregisterSurfaceControlInputReceiver is not implemented"); + } } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index f1e406196abf..8d40f9a4f7b1 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; + import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; @@ -24,8 +26,10 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.graphics.HardwareRenderer; +import android.os.Binder; import android.os.Build; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; @@ -46,6 +50,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.IntConsumer; @@ -151,6 +156,9 @@ public final class WindowManagerGlobal { private final TrustedPresentationListener mTrustedPresentationListener = new TrustedPresentationListener(); + private final ConcurrentHashMap<IBinder, InputEventReceiver> mSurfaceControlInputReceivers = + new ConcurrentHashMap<>(); + private WindowManagerGlobal() { } @@ -808,6 +816,74 @@ public final class WindowManagerGlobal { mTrustedPresentationListener.removeListener(listener); } + IBinder registerBatchedSurfaceControlInputReceiver(int displayId, + @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, + @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { + IBinder clientToken = new Binder(); + InputChannel inputChannel = new InputChannel(); + try { + WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl, + clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, null, + surfaceControl.getName(), inputChannel); + } catch (RemoteException e) { + Log.e(TAG, "Failed to create input channel", e); + e.rethrowAsRuntimeException(); + } + + mSurfaceControlInputReceivers.put(clientToken, + new BatchedInputEventReceiver(inputChannel, choreographer.getLooper(), + choreographer) { + @Override + public void onInputEvent(InputEvent event) { + boolean handled = receiver.onInputEvent(event); + finishInputEvent(event, handled); + } + }); + return clientToken; + } + + IBinder registerUnbatchedSurfaceControlInputReceiver( + int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, + @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { + IBinder clientToken = new Binder(); + InputChannel inputChannel = new InputChannel(); + try { + WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl, + clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, null, + surfaceControl.getName(), inputChannel); + } catch (RemoteException e) { + Log.e(TAG, "Failed to create input channel", e); + e.rethrowAsRuntimeException(); + } + + mSurfaceControlInputReceivers.put(clientToken, + new InputEventReceiver(inputChannel, looper) { + @Override + public void onInputEvent(InputEvent event) { + boolean handled = receiver.onInputEvent(event); + finishInputEvent(event, handled); + } + }); + + return clientToken; + } + + void unregisterSurfaceControlInputReceiver(IBinder token) { + InputEventReceiver inputEventReceiver = mSurfaceControlInputReceivers.get(token); + if (inputEventReceiver == null) { + Log.w(TAG, "No registered input event receiver with token: " + token); + return; + } + try { + WindowManagerGlobal.getWindowSession().remove(token); + } catch (RemoteException e) { + Log.e(TAG, "Failed to remove input channel", e); + e.rethrowAsRuntimeException(); + } + + inputEventReceiver.dispose(); + } + private final class TrustedPresentationListener extends ITrustedPresentationListener.Stub { private static int sId = 0; diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index b4b1fde89a46..aaf5fcc6f095 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -32,6 +32,7 @@ import android.graphics.Bitmap; import android.graphics.Region; import android.os.Bundle; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.os.StrictMode; import android.util.Log; @@ -520,6 +521,28 @@ public final class WindowManagerImpl implements WindowManager { @Override public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) { mGlobal.unregisterTrustedPresentationListener(listener); + } + + @NonNull + @Override + public IBinder registerBatchedSurfaceControlInputReceiver(int displayId, + @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, + @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { + return mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, hostToken, + surfaceControl, choreographer, receiver); + } + @NonNull + @Override + public IBinder registerUnbatchedSurfaceControlInputReceiver( + int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, + @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { + return mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId, hostToken, + surfaceControl, looper, receiver); + } + + @Override + public void unregisterSurfaceControlInputReceiver(@NonNull IBinder token) { + mGlobal.unregisterSurfaceControlInputReceiver(token); } } diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig index a74b06a491e8..9f9b7b4b68a9 100644 --- a/core/java/android/view/flags/view_flags.aconfig +++ b/core/java/android/view/flags/view_flags.aconfig @@ -1,6 +1,13 @@ package: "android.view.flags" flag { + name: "enable_surface_native_alloc_registration" + namespace: "toolkit" + description: "Feature flag for registering surfaces with the VM for faster cleanup" + bug: "306193257" +} + +flag { name: "enable_use_measure_cache_during_force_layout" namespace: "toolkit" description: "Enables using the measure cache during a view force layout from the second " diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index bdaad2b68fc2..473b814fc4a7 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -47,6 +47,7 @@ import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -337,7 +338,14 @@ public final class SplashScreenView extends FrameLayout { "SplashScreenView"); ImageView imageView = new ImageView(viewContext); imageView.setBackground(mIconDrawable); - viewHost.setView(imageView, mIconSize, mIconSize); + final int windowFlag = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + final WindowManager.LayoutParams lp = + new WindowManager.LayoutParams(mIconSize, mIconSize, + WindowManager.LayoutParams.TYPE_APPLICATION, windowFlag, + PixelFormat.TRANSPARENT); + viewHost.setView(imageView, lp); SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage(); surfaceView.setChildSurfacePackage(surfacePackage); view.mSurfacePackage = surfacePackage; diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index 3794c5d647a4..3c3c8469b3a4 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -72,3 +72,11 @@ flag { is_fixed_read_only: true bug: "295038072" } + +flag { + namespace: "window_surfaces" + name: "surface_control_input_receiver" + description: "Enable public API to register an InputReceiver for a SurfaceControl" + is_fixed_read_only: true + bug: "278757236" +} diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index f2bce9c44001..bb16ad2cb8de 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -72,7 +72,7 @@ flag { name: "predictive_back_system_animations" namespace: "systemui" description: "Predictive back for system animations" - bug: "309545085" + bug: "319421778" is_fixed_read_only: true } diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java index e55c64199f45..fab8984ce067 100644 --- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java +++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java @@ -33,13 +33,14 @@ public class RefreshRateSettingsUtils { /** * Find the highest refresh rate among all the modes of the default display. * + * This method will acquire DisplayManager.mLock, so calling it while holding other locks + * should be done with care. * @param context The context * @return The highest refresh rate */ public static float findHighestRefreshRateForDefaultDisplay(Context context) { final DisplayManager dm = context.getSystemService(DisplayManager.class); final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY); - if (display == null) { Log.w(TAG, "No valid default display device"); return DEFAULT_REFRESH_RATE; diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java index 5d82d0469d56..12aff1c6669f 100644 --- a/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java @@ -29,6 +29,7 @@ import android.content.pm.parsing.result.ParseResult; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.multiuser.Flags; import android.os.Build; import android.os.PatternMatcher; import android.util.Slog; @@ -126,6 +127,10 @@ public class ParsedProviderUtils { .setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestProvider_singleUser, sa)); + if (Flags.enableSystemUserOnlyForServicesAndProviders()) { + provider.setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SYSTEM_USER_ONLY, + R.styleable.AndroidManifestProvider_systemUserOnly, sa)); + } visibleToEphemeral = sa.getBoolean( R.styleable.AndroidManifestProvider_visibleToInstantApps, false); if (visibleToEphemeral) { diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java index a1dd19a3bc90..4ac542f84226 100644 --- a/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java @@ -29,6 +29,7 @@ import android.content.pm.parsing.result.ParseResult; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.multiuser.Flags; import android.os.Build; import com.android.internal.R; @@ -105,6 +106,11 @@ public class ParsedServiceUtils { | flag(ServiceInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestService_singleUser, sa))); + if (Flags.enableSystemUserOnlyForServicesAndProviders()) { + service.setFlags(service.getFlags() | flag(ServiceInfo.FLAG_SYSTEM_USER_ONLY, + R.styleable.AndroidManifestService_systemUserOnly, sa)); + } + visibleToEphemeral = sa.getBoolean( R.styleable.AndroidManifestService_visibleToInstantApps, false); if (visibleToEphemeral) { diff --git a/core/proto/OWNERS b/core/proto/OWNERS index db391f7a8c35..a854e3626e78 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -18,6 +18,7 @@ per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/a per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS per-file android/hardware/sensorprivacy.proto = ntmyren@google.com,evanseverson@google.com per-file background_install_control.proto = wenhaowang@google.com,georgechan@google.com,billylau@google.com +per-file android/content/intent.proto = file:/PACKAGE_MANAGER_OWNERS # Biometrics jaggies@google.com @@ -31,5 +32,3 @@ jreck@google.com # Accessibility pweaver@google.com -hongmingjin@google.com -cbrower@google.com diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml index af305329da1a..31acd9af164c 100644 --- a/core/res/res/values-watch/config.xml +++ b/core/res/res/values-watch/config.xml @@ -90,4 +90,7 @@ {@link MotionEvent#AXIS_SCROLL} generated by {@link InputDevice#SOURCE_ROTARY_ENCODER} devices. --> <bool name="config_viewRotaryEncoderHapticScrollFedbackEnabled">true</bool> + + <!-- If this is true, allow wake from theater mode from motion. --> + <bool name="config_allowTheaterModeWakeFromMotion">true</bool> </resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 8fae6db4114a..601952437650 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -506,6 +506,12 @@ receivers, and providers; it can not be used with activities. --> <attr name="singleUser" format="boolean" /> + <!-- If set to true, only a single instance of this component will + run and be available for the SYSTEM user. Non SYSTEM users will not be + allowed to access the component if this flag is enabled. + This flag can be used with services, receivers, providers and activities. --> + <attr name="systemUserOnly" format="boolean" /> + <!-- Specify a specific process that the associated code is to run in. Use with the application tag (to supply a default process for all application components), or with the activity, receiver, service, @@ -2859,6 +2865,7 @@ Context.createAttributionContext() using the first attribution tag contained here. --> <attr name="attributionTags" /> + <attr name="systemUserOnly" format="boolean" /> </declare-styleable> <!-- Attributes that can be supplied in an AndroidManifest.xml @@ -3017,6 +3024,7 @@ ignored when the process is bound into a shared isolated process by a client. --> <attr name="allowSharedIsolatedProcess" format="boolean" /> + <attr name="systemUserOnly" format="boolean" /> </declare-styleable> <!-- @hide The <code>apex-system-service</code> tag declares an apex system service @@ -3144,7 +3152,7 @@ <attr name="uiOptions" /> <attr name="parentActivityName" /> <attr name="singleUser" /> - <!-- @hide This broadcast receiver or activity will only receive broadcasts for the + <!-- This broadcast receiver or activity will only receive broadcasts for the system user--> <attr name="systemUserOnly" format="boolean" /> <attr name="persistableMode" /> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 53b473e0ac1f..7b5c49c8d9aa 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -119,6 +119,8 @@ <public name="optional"/> <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") --> <public name="adServiceTypes" /> + <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") --> + <public name="systemUserOnly"/> </staging-public-group> <staging-public-group type="id" first-id="0x01bc0000"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 542e9d6f936f..f2858066b55b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1825,7 +1825,7 @@ <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized --> <string name="fingerprint_acquired_partial">Press firmly on the sensor</string> <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized --> - <string name="fingerprint_acquired_insufficient">Can\u2019t recognize fingerprint. Try again.</string> + <string name="fingerprint_acquired_insufficient">Fingerprint not recognized. Try again.</string> <!-- Message shown during fingerprint acquisision when the fingerprint sensor needs cleaning --> <string name="fingerprint_acquired_imager_dirty">Clean fingerprint sensor and try again</string> <string name="fingerprint_acquired_imager_dirty_alt">Clean sensor and try again</string> @@ -1959,7 +1959,7 @@ <!-- Message shown during face acquisition when the sensor needs to be recalibrated [CHAR LIMIT=50] --> <string name="face_acquired_recalibrate">Please re-enroll your face.</string> <!-- Message shown during face enrollment when a different person's face is detected [CHAR LIMIT=50] --> - <string name="face_acquired_too_different">Can\u2019t recognize face. Try again.</string> + <string name="face_acquired_too_different">Face not recognized. Try again.</string> <!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] --> <string name="face_acquired_too_similar">Change the position of your head slightly</string> <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] --> diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java index 12a28446b0e1..a28bb69244eb 100644 --- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java +++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java @@ -195,9 +195,30 @@ public class PerformanceHintManagerTest { Session s = createSession(); assumeNotNull(s); s.updateTargetWorkDuration(16); - s.reportActualWorkDuration(new WorkDuration(1, 12, 8, 6)); - s.reportActualWorkDuration(new WorkDuration(1, 33, 14, 20)); - s.reportActualWorkDuration(new WorkDuration(1, 14, 10, 6)); + { + WorkDuration workDuration = new WorkDuration(); + workDuration.setWorkPeriodStartTimestampNanos(1); + workDuration.setActualTotalDurationNanos(12); + workDuration.setActualCpuDurationNanos(8); + workDuration.setActualGpuDurationNanos(6); + s.reportActualWorkDuration(workDuration); + } + { + WorkDuration workDuration = new WorkDuration(); + workDuration.setWorkPeriodStartTimestampNanos(1); + workDuration.setActualTotalDurationNanos(33); + workDuration.setActualCpuDurationNanos(14); + workDuration.setActualGpuDurationNanos(20); + s.reportActualWorkDuration(workDuration); + } + { + WorkDuration workDuration = new WorkDuration(); + workDuration.setWorkPeriodStartTimestampNanos(1); + workDuration.setActualTotalDurationNanos(14); + workDuration.setActualCpuDurationNanos(10); + workDuration.setActualGpuDurationNanos(6); + s.reportActualWorkDuration(workDuration); + } } @Test @@ -206,25 +227,25 @@ public class PerformanceHintManagerTest { assumeNotNull(s); s.updateTargetWorkDuration(16); assertThrows(IllegalArgumentException.class, () -> { - s.reportActualWorkDuration(new WorkDuration(-1, 12, 8, 6)); + s.reportActualWorkDuration(new WorkDuration(-1, 12, 8, 6, 1)); }); assertThrows(IllegalArgumentException.class, () -> { - s.reportActualWorkDuration(new WorkDuration(0, 12, 8, 6)); + s.reportActualWorkDuration(new WorkDuration(0, 12, 8, 6, 1)); }); assertThrows(IllegalArgumentException.class, () -> { - s.reportActualWorkDuration(new WorkDuration(1, -1, 8, 6)); + s.reportActualWorkDuration(new WorkDuration(1, -1, 8, 6, 1)); }); assertThrows(IllegalArgumentException.class, () -> { - s.reportActualWorkDuration(new WorkDuration(1, 0, 8, 6)); + s.reportActualWorkDuration(new WorkDuration(1, 0, 8, 6, 1)); }); assertThrows(IllegalArgumentException.class, () -> { - s.reportActualWorkDuration(new WorkDuration(1, 12, -1, 6)); + s.reportActualWorkDuration(new WorkDuration(1, 12, -1, 6, 1)); }); assertThrows(IllegalArgumentException.class, () -> { - s.reportActualWorkDuration(new WorkDuration(1, 12, 0, 6)); + s.reportActualWorkDuration(new WorkDuration(1, 12, 0, 6, 1)); }); assertThrows(IllegalArgumentException.class, () -> { - s.reportActualWorkDuration(new WorkDuration(1, 12, 8, -1)); + s.reportActualWorkDuration(new WorkDuration(1, 12, 8, -1, 1)); }); } } diff --git a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java new file mode 100644 index 000000000000..c70da6e94385 --- /dev/null +++ b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java @@ -0,0 +1,77 @@ +/* + * 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 android.os; + +import static org.junit.Assert.assertThrows; + +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = WorkDuration.class) +public class WorkDurationUnitTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + // Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect. + @Rule + public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood() ? null + : DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) + public void testWorkDurationSetters_IllegalArgument() { + WorkDuration workDuration = new WorkDuration(); + assertThrows(IllegalArgumentException.class, () -> { + workDuration.setWorkPeriodStartTimestampNanos(-1); + }); + assertThrows(IllegalArgumentException.class, () -> { + workDuration.setWorkPeriodStartTimestampNanos(0); + }); + assertThrows(IllegalArgumentException.class, () -> { + workDuration.setActualTotalDurationNanos(-1); + }); + assertThrows(IllegalArgumentException.class, () -> { + workDuration.setActualTotalDurationNanos(0); + }); + assertThrows(IllegalArgumentException.class, () -> { + workDuration.setActualCpuDurationNanos(-1); + }); + assertThrows(IllegalArgumentException.class, () -> { + workDuration.setActualCpuDurationNanos(0); + }); + assertThrows(IllegalArgumentException.class, () -> { + workDuration.setActualGpuDurationNanos(-1); + }); + } +} diff --git a/core/tests/nfctests/OWNERS b/core/tests/nfctests/OWNERS deleted file mode 100644 index 34b095c7fda0..000000000000 --- a/core/tests/nfctests/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /core/java/android/nfc/OWNERS diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 5ad144d50b87..45540e0fbbb8 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -175,3 +175,74 @@ android_library { plugins: ["dagger2-compiler"], use_resource_processor: true, } + +android_app { + name: "WindowManagerShellRobolectric", + platform_apis: true, + static_libs: [ + "WindowManager-Shell", + ], + manifest: "multivalentTests/AndroidManifestRobolectric.xml", + use_resource_processor: true, +} + +android_robolectric_test { + name: "WMShellRobolectricTests", + instrumentation_for: "WindowManagerShellRobolectric", + upstream: true, + java_resource_dirs: [ + "multivalentTests/robolectric/config", + ], + srcs: [ + "multivalentTests/src/**/*.kt", + ], + static_libs: [ + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-robolectric-prebuilt", + "mockito-kotlin2", + "truth", + ], +} + +android_test { + name: "WMShellMultivalentTestsOnDevice", + srcs: [ + "multivalentTests/src/**/*.kt", + ], + static_libs: [ + "WindowManager-Shell", + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "frameworks-base-testutils", + "mockito-kotlin2", + "mockito-target-extended-minus-junit4", + "truth", + "platform-test-annotations", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "android.test.runner", + ], + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + kotlincflags: ["-Xjvm-default=all"], + optimize: { + enabled: false, + }, + test_suites: ["device-tests"], + platform_apis: true, + certificate: "platform", + aaptflags: [ + "--extra-packages", + "com.android.wm.shell", + ], + manifest: "multivalentTests/AndroidManifest.xml", +} diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml new file mode 100644 index 000000000000..f8f8338e5f04 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml @@ -0,0 +1,13 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.multivalenttests"> + + <application android:debuggable="true" android:supportsRtl="true" > + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:label="Multivalent tests for WindowManager-Shell" + android:targetPackage="com.android.wm.shell.multivalenttests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml new file mode 100644 index 000000000000..ffcd7d46fbae --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml @@ -0,0 +1,3 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.multivalenttests"> +</manifest> diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml new file mode 100644 index 000000000000..36fe8ec3370d --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Runs Tests for WindowManagerShellLib"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="WMShellMultivalentTestsOnDevice.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="WMShellMultivalentTestsOnDevice" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.wm.shell.multivalenttests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties new file mode 100644 index 000000000000..7a0527ccaafb --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties @@ -0,0 +1,2 @@ +sdk=NEWEST_SDK + diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt new file mode 100644 index 000000000000..ea7c6edd4b56 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt @@ -0,0 +1,481 @@ +/* + * 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.wm.shell.bubbles + +import android.content.Context +import android.content.Intent +import android.content.pm.ShortcutInfo +import android.graphics.Insets +import android.graphics.PointF +import android.graphics.Rect +import android.os.UserHandle +import android.view.WindowManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.wm.shell.R +import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests operations and the resulting state managed by [BubblePositioner]. */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class BubblePositionerTest { + + private lateinit var positioner: BubblePositioner + private val context = ApplicationProvider.getApplicationContext<Context>() + private val defaultDeviceConfig = + DeviceConfig( + windowBounds = Rect(0, 0, 1000, 2000), + isLargeScreen = false, + isSmallTablet = false, + isLandscape = false, + isRtl = false, + insets = Insets.of(0, 0, 0, 0) + ) + + @Before + fun setUp() { + val windowManager = context.getSystemService(WindowManager::class.java) + positioner = BubblePositioner(context, windowManager) + } + + @Test + fun testUpdate() { + val insets = Insets.of(10, 20, 5, 15) + val screenBounds = Rect(0, 0, 1000, 1200) + val availableRect = Rect(screenBounds) + availableRect.inset(insets) + positioner.update(defaultDeviceConfig.copy(insets = insets, windowBounds = screenBounds)) + assertThat(positioner.availableRect).isEqualTo(availableRect) + assertThat(positioner.isLandscape).isFalse() + assertThat(positioner.isLargeScreen).isFalse() + assertThat(positioner.insets).isEqualTo(insets) + } + + @Test + fun testShowBubblesVertically_phonePortrait() { + positioner.update(defaultDeviceConfig) + assertThat(positioner.showBubblesVertically()).isFalse() + } + + @Test + fun testShowBubblesVertically_phoneLandscape() { + positioner.update(defaultDeviceConfig.copy(isLandscape = true)) + assertThat(positioner.isLandscape).isTrue() + assertThat(positioner.showBubblesVertically()).isTrue() + } + + @Test + fun testShowBubblesVertically_tablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + assertThat(positioner.showBubblesVertically()).isTrue() + } + + /** If a resting position hasn't been set, calling it will return the default position. */ + @Test + fun testGetRestingPosition_returnsDefaultPosition() { + positioner.update(defaultDeviceConfig) + val restingPosition = positioner.getRestingPosition() + val defaultPosition = positioner.defaultStartPosition + assertThat(restingPosition).isEqualTo(defaultPosition) + } + + /** If a resting position has been set, it'll return that instead of the default position. */ + @Test + fun testGetRestingPosition_returnsRestingPosition() { + positioner.update(defaultDeviceConfig) + val restingPosition = PointF(100f, 100f) + positioner.restingPosition = restingPosition + assertThat(positioner.getRestingPosition()).isEqualTo(restingPosition) + } + + /** Test that the default resting position on phone is in upper left. */ + @Test + fun testGetRestingPosition_bubble_onPhone() { + positioner.update(defaultDeviceConfig) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_bubble_onPhone_RTL() { + positioner.update(defaultDeviceConfig.copy(isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + /** Test that the default resting position on tablet is middle left. */ + @Test + fun testGetRestingPosition_chatBubble_onTablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_chatBubble_onTablet_RTL() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + /** Test that the default resting position on tablet is middle right. */ + @Test + fun testGetDefaultPosition_appBubble_onTablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */) + assertThat(startPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(startPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_appBubble_onTablet_RTL() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */) + assertThat(startPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(startPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testHasUserModifiedDefaultPosition_false() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + positioner.restingPosition = positioner.defaultStartPosition + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + } + + @Test + fun testHasUserModifiedDefaultPosition_true() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + positioner.restingPosition = PointF(0f, 100f) + assertThat(positioner.hasUserModifiedDefaultPosition()).isTrue() + } + + @Test + fun testGetExpandedViewHeight_max() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT) + } + + @Test + fun testGetExpandedViewHeight_customHeight_valid() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + val minHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height) + val bubble = + Bubble( + "key", + ShortcutInfo.Builder(context, "id").build(), + minHeight + 100 /* desiredHeight */, + 0 /* desiredHeightResId */, + "title", + 0 /* taskId */, + null /* locus */, + true /* isDismissable */, + directExecutor()) {} + + // Ensure the height is the same as the desired value + assertThat(positioner.getExpandedViewHeight(bubble)) + .isEqualTo(bubble.getDesiredHeight(context)) + } + + @Test + fun testGetExpandedViewHeight_customHeight_tooSmall() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val bubble = + Bubble( + "key", + ShortcutInfo.Builder(context, "id").build(), + 10 /* desiredHeight */, + 0 /* desiredHeightResId */, + "title", + 0 /* taskId */, + null /* locus */, + true /* isDismissable */, + directExecutor()) {} + + // Ensure the height is the same as the desired value + val minHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height) + assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight) + } + + @Test + fun testGetMaxExpandedViewHeight_onLargeTablet() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val manageButtonHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height) + val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width) + val expandedViewPadding = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding) + val expectedHeight = + 1800 - 2 * 20 - manageButtonHeight - pointerWidth - expandedViewPadding * 2 + assertThat(positioner.getMaxExpandedViewHeight(false /* isOverflow */)) + .isEqualTo(expectedHeight) + } + + @Test + fun testAreBubblesBottomAligned_largeScreen_true() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isTrue() + } + + @Test + fun testAreBubblesBottomAligned_largeScreen_landscape_false() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testAreBubblesBottomAligned_smallTablet_false() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isSmallTablet = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testAreBubblesBottomAligned_phone_false() { + val deviceConfig = + defaultDeviceConfig.copy( + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testExpandedViewY_phoneLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height so it'll always be top aligned + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_phonePortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // Always top aligned in phone portrait + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_smallTabletLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isSmallTablet = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on small tablets + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_smallTabletPortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + isSmallTablet = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on small tablets + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_largeScreenLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on landscape, large tablet + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_largeScreenPortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + val manageButtonHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height) + val manageButtonPlusMargin = + manageButtonHeight + + 2 * context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_margin) + val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width) + + val expectedExpandedViewY = + positioner.availableRect.bottom - + manageButtonPlusMargin - + positioner.getExpandedViewHeightForLargeScreen() - + pointerWidth + + // Bubbles are bottom aligned on portrait, large tablet + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(expectedExpandedViewY) + } + + private val defaultYPosition: Float + /** + * Calculates the Y position bubbles should be placed based on the config. Based on the + * calculations in [BubblePositioner.getDefaultStartPosition] and + * [BubbleStackView.RelativeStackPosition]. + */ + get() { + val isTablet = positioner.isLargeScreen + + // On tablet the position is centered, on phone it is an offset from the top. + val desiredY = + if (isTablet) { + positioner.screenRect.height() / 2f - positioner.bubbleSize / 2f + } else { + context.resources + .getDimensionPixelOffset(R.dimen.bubble_stack_starting_offset_y) + .toFloat() + } + // Since we're visually centering the bubbles on tablet, use total screen height rather + // than the available height. + val height = + if (isTablet) { + positioner.screenRect.height() + } else { + positioner.availableRect.height() + } + val offsetPercent = (desiredY / height).coerceIn(0f, 1f) + val allowableStackRegion = + positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent + } +} diff --git a/libs/WindowManager/Shell/multivalentTestsForDevice b/libs/WindowManager/Shell/multivalentTestsForDevice new file mode 120000 index 000000000000..20ee34ada103 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTestsForDevice @@ -0,0 +1 @@ +multivalentTests
\ No newline at end of file diff --git a/libs/WindowManager/Shell/multivalentTestsForDeviceless b/libs/WindowManager/Shell/multivalentTestsForDeviceless new file mode 120000 index 000000000000..20ee34ada103 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTestsForDeviceless @@ -0,0 +1 @@ +multivalentTests
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 81d963877e4c..bb433dbbd2ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -176,6 +176,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private StatusBarCustomizer mCustomizer; private boolean mTrackingLatency; + // Keep previous navigation type before remove mBackNavigationInfo. + @BackNavigationInfo.BackTargetType + private int mPreviousNavigationType; + public BackAnimationController( @NonNull ShellInit shellInit, @NonNull ShellController shellController, @@ -871,6 +875,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellBackAnimationRegistry.resetDefaultCrossActivity(); cancelLatencyTracking(); if (mBackNavigationInfo != null) { + mPreviousNavigationType = mBackNavigationInfo.getType(); mBackNavigationInfo.onBackNavigationFinished(triggerBack); mBackNavigationInfo = null; } @@ -983,7 +988,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellExecutor.execute( () -> { if (!mShellBackAnimationRegistry.cancel( - mBackNavigationInfo.getType())) { + mBackNavigationInfo != null + ? mBackNavigationInfo.getType() + : mPreviousNavigationType)) { return; } if (!mBackGestureStarted) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index 80fc3a867d48..ac2a1c867462 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -136,6 +136,9 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { mStartTaskRect.set(mClosingTarget.windowConfiguration.getBounds()); mStartTaskRect.offsetTo(0, 0); + // inset bottom in case of pinned taskbar being present + mStartTaskRect.inset(0, 0, 0, mClosingTarget.contentInsets.bottom); + // Draw background. mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(), BACKGROUNDCOLOR, mTransaction); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 8c861c63a70d..bf783e6af36f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -197,60 +197,61 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { - return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, - finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler); - } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - return animateEnterPipFromActivityEmbedding( - info, startTransaction, finishTransaction, finishCallback); - } else if (mType == TYPE_DISPLAY_AND_SPLIT_CHANGE) { - return false; - } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - final boolean handledToPip = animateOpenIntentWithRemoteAndPip( - info, startTransaction, finishTransaction, finishCallback); - // Consume the transition on remote handler if the leftover handler already handle - // this transition. And if it cannot, the transition will be handled by remote - // handler, so don't consume here. - // Need to check leftOverHandler as it may change in - // #animateOpenIntentWithRemoteAndPip - if (handledToPip && mHasRequestToRemote - && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { - mPlayer.getRemoteTransitionHandler().onTransitionConsumed( - transition, false, null); - } - return handledToPip; - } else if (mType == TYPE_RECENTS_DURING_SPLIT) { - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - // Pip auto-entering info might be appended to recent transition like pressing - // home-key in 3-button navigation. This offers split handler the opportunity to - // handle split to pip animation. - if (mPipHandler.isEnteringPip(change, info.getType()) - && mSplitHandler.getSplitItemPosition(change.getLastParent()) - != SPLIT_POSITION_UNDEFINED) { - return animateEnterPipFromSplit( - this, info, startTransaction, finishTransaction, finishCallback, - mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + switch (mType) { + case TYPE_ENTER_PIP_FROM_SPLIT: + return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, + finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: + return animateEnterPipFromActivityEmbedding( + info, startTransaction, finishTransaction, finishCallback); + case TYPE_DISPLAY_AND_SPLIT_CHANGE: + return false; + case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + final boolean handledToPip = animateOpenIntentWithRemoteAndPip( + info, startTransaction, finishTransaction, finishCallback); + // Consume the transition on remote handler if the leftover handler already + // handle this transition. And if it cannot, the transition will be handled by + // remote handler, so don't consume here. + // Need to check leftOverHandler as it may change in + // #animateOpenIntentWithRemoteAndPip + if (handledToPip && mHasRequestToRemote + && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { + mPlayer.getRemoteTransitionHandler().onTransitionConsumed( + transition, false, null); + } + return handledToPip; + case TYPE_RECENTS_DURING_SPLIT: + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + // Pip auto-entering info might be appended to recent transition like + // pressing home-key in 3-button navigation. This offers split handler the + // opportunity to handle split to pip animation. + if (mPipHandler.isEnteringPip(change, info.getType()) + && mSplitHandler.getSplitItemPosition(change.getLastParent()) + != SPLIT_POSITION_UNDEFINED) { + return animateEnterPipFromSplit( + this, info, startTransaction, finishTransaction, finishCallback, + mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + } } - } - return animateRecentsDuringSplit( - info, startTransaction, finishTransaction, finishCallback); - } else if (mType == TYPE_KEYGUARD) { - return animateKeyguard(this, info, startTransaction, finishTransaction, - finishCallback, mKeyguardHandler, mPipHandler); - } else if (mType == TYPE_RECENTS_DURING_KEYGUARD) { - return animateRecentsDuringKeyguard( - info, startTransaction, finishTransaction, finishCallback); - } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { - return animateRecentsDuringDesktop( - info, startTransaction, finishTransaction, finishCallback); - } else if (mType == TYPE_UNFOLD) { - return animateUnfold( - info, startTransaction, finishTransaction, finishCallback); - } else { - throw new IllegalStateException( - "Starting mixed animation without a known mixed type? " + mType); + return animateRecentsDuringSplit( + info, startTransaction, finishTransaction, finishCallback); + case TYPE_KEYGUARD: + return animateKeyguard(this, info, startTransaction, finishTransaction, + finishCallback, mKeyguardHandler, mPipHandler); + case TYPE_RECENTS_DURING_KEYGUARD: + return animateRecentsDuringKeyguard( + info, startTransaction, finishTransaction, finishCallback); + case TYPE_RECENTS_DURING_DESKTOP: + return animateRecentsDuringDesktop( + info, startTransaction, finishTransaction, finishCallback); + case TYPE_UNFOLD: + return animateUnfold( + info, startTransaction, finishTransaction, finishCallback); + default: + throw new IllegalStateException( + "Starting mixed animation without a known mixed type? " + mType); } } @@ -457,79 +458,100 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (mType == TYPE_DISPLAY_AND_SPLIT_CHANGE) { - // queue since no actual animation. - } else if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { - if (mAnimType == ANIM_TYPE_GOING_HOME) { - boolean ended = mSplitHandler.end(); - // If split couldn't end (because it is remote), then don't end everything else - // since we have to play out the animation anyways. - if (!ended) return; - mPipHandler.end(); - if (mLeftoversHandler != null) { - mLeftoversHandler.mergeAnimation( - transition, info, t, mergeTarget, finishCallback); + switch (mType) { + case TYPE_DISPLAY_AND_SPLIT_CHANGE: + // queue since no actual animation. + break; + case TYPE_ENTER_PIP_FROM_SPLIT: + if (mAnimType == ANIM_TYPE_GOING_HOME) { + boolean ended = mSplitHandler.end(); + // If split couldn't end (because it is remote), then don't end everything + // else since we have to play out the animation anyways. + if (!ended) return; + mPipHandler.end(); + if (mLeftoversHandler != null) { + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + } + } else { + mPipHandler.end(); } - } else { + break; + case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: mPipHandler.end(); - } - } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - mPipHandler.end(); - mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - mPipHandler.end(); - if (mLeftoversHandler != null) { - mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, + mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } - } else if (mType == TYPE_RECENTS_DURING_SPLIT) { - if (mSplitHandler.isPendingEnter(transition)) { - // Recents -> enter-split means that we are switching from one pair to - // another pair. - mAnimType = ANIM_TYPE_PAIR_TO_PAIR; - } - mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mType == TYPE_KEYGUARD) { - mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mType == TYPE_RECENTS_DURING_KEYGUARD) { - if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { - DefaultMixedHandler.handoverTransitionLeashes(mInfo, info, t, mFinishT); - if (animateKeyguard( - this, info, t, mFinishT, mFinishCB, mKeyguardHandler, mPipHandler)) { - finishCallback.onTransitionFinished(null); + break; + case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + mPipHandler.end(); + if (mLeftoversHandler != null) { + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, + finishCallback); } - } - mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { - mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mType == TYPE_UNFOLD) { - mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else { - throw new IllegalStateException( - "Playing a mixed transition with unknown type? " + mType); + break; + case TYPE_RECENTS_DURING_SPLIT: + if (mSplitHandler.isPendingEnter(transition)) { + // Recents -> enter-split means that we are switching from one pair to + // another pair. + mAnimType = ANIM_TYPE_PAIR_TO_PAIR; + } + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + break; + case TYPE_KEYGUARD: + mKeyguardHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + break; + case TYPE_RECENTS_DURING_KEYGUARD: + if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { + DefaultMixedHandler.handoverTransitionLeashes(mInfo, info, t, mFinishT); + if (animateKeyguard(this, info, t, mFinishT, mFinishCB, mKeyguardHandler, + mPipHandler)) { + finishCallback.onTransitionFinished(null); + } + } + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + break; + case TYPE_RECENTS_DURING_DESKTOP: + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + break; + case TYPE_UNFOLD: + mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + break; + default: + throw new IllegalStateException( + "Playing a mixed transition with unknown type? " + mType); } } void onTransitionConsumed( @NonNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishT) { - if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { - mPipHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - mPipHandler.onTransitionConsumed(transition, aborted, finishT); - mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_RECENTS_DURING_SPLIT) { - mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_KEYGUARD) { - mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { - mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_UNFOLD) { - mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); + switch (mType) { + case TYPE_ENTER_PIP_FROM_SPLIT: + mPipHandler.onTransitionConsumed(transition, aborted, finishT); + break; + case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: + mPipHandler.onTransitionConsumed(transition, aborted, finishT); + mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT); + break; + case TYPE_RECENTS_DURING_SPLIT: + case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + case TYPE_RECENTS_DURING_DESKTOP: + mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); + break; + case TYPE_KEYGUARD: + mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); + break; + case TYPE_UNFOLD: + mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); + break; + default: + break; } + if (mHasRequestToRemote) { mPlayer.getRemoteTransitionHandler().onTransitionConsumed( transition, aborted, finishT); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java deleted file mode 100644 index 6ebee730756e..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java +++ /dev/null @@ -1,602 +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.wm.shell.bubbles; - -import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; - -import static org.mockito.Mockito.mock; - -import android.content.Intent; -import android.content.pm.ShortcutInfo; -import android.graphics.Insets; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; -import android.os.UserHandle; -import android.testing.AndroidTestingRunner; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests operations and the resulting state managed by {@link BubblePositioner}. - */ -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class BubblePositionerTest extends ShellTestCase { - - private BubblePositioner mPositioner; - - @Before - public void setUp() { - WindowManager windowManager = mContext.getSystemService(WindowManager.class); - mPositioner = new BubblePositioner(mContext, windowManager); - } - - @Test - public void testUpdate() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1000, 1200); - Rect availableRect = new Rect(screenBounds); - availableRect.inset(insets); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.getAvailableRect()).isEqualTo(availableRect); - assertThat(mPositioner.isLandscape()).isFalse(); - assertThat(mPositioner.isLargeScreen()).isFalse(); - assertThat(mPositioner.getInsets()).isEqualTo(insets); - } - - @Test - public void testShowBubblesVertically_phonePortrait() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.showBubblesVertically()).isFalse(); - } - - @Test - public void testShowBubblesVertically_phoneLandscape() { - DeviceConfig deviceConfig = new ConfigBuilder().setLandscape().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.isLandscape()).isTrue(); - assertThat(mPositioner.showBubblesVertically()).isTrue(); - } - - @Test - public void testShowBubblesVertically_tablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.showBubblesVertically()).isTrue(); - } - - /** If a resting position hasn't been set, calling it will return the default position. */ - @Test - public void testGetRestingPosition_returnsDefaultPosition() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - PointF restingPosition = mPositioner.getRestingPosition(); - PointF defaultPosition = mPositioner.getDefaultStartPosition(); - - assertThat(restingPosition).isEqualTo(defaultPosition); - } - - /** If a resting position has been set, it'll return that instead of the default position. */ - @Test - public void testGetRestingPosition_returnsRestingPosition() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - PointF restingPosition = new PointF(100, 100); - mPositioner.setRestingPosition(restingPosition); - - assertThat(mPositioner.getRestingPosition()).isEqualTo(restingPosition); - } - - /** Test that the default resting position on phone is in upper left. */ - @Test - public void testGetRestingPosition_bubble_onPhone() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_bubble_onPhone_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - /** Test that the default resting position on tablet is middle left. */ - @Test - public void testGetRestingPosition_chatBubble_onTablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_chatBubble_onTablet_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - /** Test that the default resting position on tablet is middle right. */ - @Test - public void testGetDefaultPosition_appBubble_onTablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */); - - assertThat(startPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(startPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_appBubble_onTablet_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */); - - assertThat(startPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(startPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testHasUserModifiedDefaultPosition_false() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - - mPositioner.setRestingPosition(mPositioner.getDefaultStartPosition()); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - } - - @Test - public void testHasUserModifiedDefaultPosition_true() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - - mPositioner.setRestingPosition(new PointF(0, 100)); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue(); - } - - @Test - public void testGetExpandedViewHeight_max() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT); - } - - @Test - public void testGetExpandedViewHeight_customHeight_valid() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - final int minHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_expanded_default_height); - Bubble bubble = new Bubble("key", - mock(ShortcutInfo.class), - minHeight + 100 /* desiredHeight */, - 0 /* desiredHeightResId */, - "title", - 0 /* taskId */, - null /* locus */, - true /* isDismissable */, - directExecutor(), - mock(Bubbles.BubbleMetadataFlagListener.class)); - - // Ensure the height is the same as the desired value - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo( - bubble.getDesiredHeight(mContext)); - } - - - @Test - public void testGetExpandedViewHeight_customHeight_tooSmall() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Bubble bubble = new Bubble("key", - mock(ShortcutInfo.class), - 10 /* desiredHeight */, - 0 /* desiredHeightResId */, - "title", - 0 /* taskId */, - null /* locus */, - true /* isDismissable */, - directExecutor(), - mock(Bubbles.BubbleMetadataFlagListener.class)); - - // Ensure the height is the same as the minimum value - final int minHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_expanded_default_height); - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight); - } - - @Test - public void testGetMaxExpandedViewHeight_onLargeTablet() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - int manageButtonHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); - int pointerWidth = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_pointer_width); - int expandedViewPadding = mContext.getResources().getDimensionPixelSize(R - .dimen.bubble_expanded_view_padding); - float expectedHeight = 1800 - 2 * 20 - manageButtonHeight - pointerWidth - - expandedViewPadding * 2; - assertThat(((float) mPositioner.getMaxExpandedViewHeight(false /* isOverflow */))) - .isWithin(0.1f).of(expectedHeight); - } - - @Test - public void testAreBubblesBottomAligned_largeScreen_true() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isTrue(); - } - - @Test - public void testAreBubblesBottomAligned_largeScreen_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testAreBubblesBottomAligned_smallTablet_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setSmallTablet() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testAreBubblesBottomAligned_phone_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testExpandedViewY_phoneLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height so it'll always be top aligned - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_phonePortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // Always top aligned in phone portrait - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_smallTabletLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setSmallTablet() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on small tablets - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_smallTabletPortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setSmallTablet() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on small tablets - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_largeScreenLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on landscape, large tablet - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_largeScreenPortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - int manageButtonHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); - int manageButtonPlusMargin = manageButtonHeight + 2 - * mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_manage_button_margin); - int pointerWidth = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_pointer_width); - - final float expectedExpandedViewY = mPositioner.getAvailableRect().bottom - - manageButtonPlusMargin - - mPositioner.getExpandedViewHeightForLargeScreen() - - pointerWidth; - - // Bubbles are bottom aligned on portrait, large tablet - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(expectedExpandedViewY); - } - - /** - * Calculates the Y position bubbles should be placed based on the config. Based on - * the calculations in {@link BubblePositioner#getDefaultStartPosition()} and - * {@link BubbleStackView.RelativeStackPosition}. - */ - private float getDefaultYPosition() { - final boolean isTablet = mPositioner.isLargeScreen(); - - // On tablet the position is centered, on phone it is an offset from the top. - final float desiredY = isTablet - ? mPositioner.getScreenRect().height() / 2f - (mPositioner.getBubbleSize() / 2f) - : mContext.getResources().getDimensionPixelOffset( - R.dimen.bubble_stack_starting_offset_y); - // Since we're visually centering the bubbles on tablet, use total screen height rather - // than the available height. - final float height = isTablet - ? mPositioner.getScreenRect().height() - : mPositioner.getAvailableRect().height(); - float offsetPercent = desiredY / height; - offsetPercent = Math.max(0f, Math.min(1f, offsetPercent)); - final RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent; - } - - /** - * Sets up window manager to return config values based on what you need for the test. - * By default it sets up a portrait phone without any insets. - */ - private static class ConfigBuilder { - private Rect mScreenBounds = new Rect(0, 0, 1000, 2000); - private boolean mIsLargeScreen = false; - private boolean mIsSmallTablet = false; - private boolean mIsLandscape = false; - private boolean mIsRtl = false; - private Insets mInsets = Insets.of(0, 0, 0, 0); - - public ConfigBuilder setScreenBounds(Rect screenBounds) { - mScreenBounds = screenBounds; - return this; - } - - public ConfigBuilder setLargeScreen() { - mIsLargeScreen = true; - return this; - } - - public ConfigBuilder setSmallTablet() { - mIsSmallTablet = true; - return this; - } - - public ConfigBuilder setLandscape() { - mIsLandscape = true; - return this; - } - - public ConfigBuilder setRtl() { - mIsRtl = true; - return this; - } - - public ConfigBuilder setInsets(Insets insets) { - mInsets = insets; - return this; - } - - private DeviceConfig build() { - return new DeviceConfig(mIsLargeScreen, mIsSmallTablet, mIsLandscape, mIsRtl, - mScreenBounds, mInsets); - } - } -} diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 6c3172a26751..d58c872dbc56 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -56,6 +56,7 @@ std::optional<std::int32_t> render_ahead() { bool Properties::debugLayersUpdates = false; bool Properties::debugOverdraw = false; +bool Properties::debugTraceGpuResourceCategories = false; bool Properties::showDirtyRegions = false; bool Properties::skipEmptyFrames = true; bool Properties::useBufferAge = true; @@ -151,10 +152,12 @@ bool Properties::load() { skpCaptureEnabled = debuggingEnabled && base::GetBoolProperty(PROPERTY_CAPTURE_SKP_ENABLED, false); - SkAndroidFrameworkTraceUtil::setEnableTracing( - base::GetBoolProperty(PROPERTY_SKIA_TRACING_ENABLED, false)); + bool skiaBroadTracing = base::GetBoolProperty(PROPERTY_SKIA_TRACING_ENABLED, false); + SkAndroidFrameworkTraceUtil::setEnableTracing(skiaBroadTracing); SkAndroidFrameworkTraceUtil::setUsePerfettoTrackEvents( base::GetBoolProperty(PROPERTY_SKIA_USE_PERFETTO_TRACK_EVENTS, false)); + debugTraceGpuResourceCategories = + base::GetBoolProperty(PROPERTY_TRACE_GPU_RESOURCES, skiaBroadTracing); runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index bca57e9e4678..b956facf6f90 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -143,6 +143,15 @@ enum DebugLevel { #define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled" /** + * Might split Skia's GPU resource utilization into separate tracing tracks (slow). + * + * Aggregate total and purgeable numbers will still be reported under a "misc" track when this is + * disabled, they just won't be split into distinct categories. Results may vary depending on GPU + * backend/API, and the category mappings defined in ATraceMemoryDump's hardcoded sResourceMap. + */ +#define PROPERTY_TRACE_GPU_RESOURCES "debug.hwui.trace_gpu_resources" + +/** * Allows broad recording of Skia drawing commands. * * If disabled, a very minimal set of trace events *may* be recorded. @@ -254,6 +263,7 @@ public: static bool debugLayersUpdates; static bool debugOverdraw; + static bool debugTraceGpuResourceCategories; static bool showDirtyRegions; // TODO: Remove after stabilization period static bool skipEmptyFrames; diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp index 234f42d79cb7..756b937f7de3 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -20,6 +20,8 @@ #include <cstring> +#include "GrDirectContext.h" + namespace android { namespace uirenderer { namespace skiapipeline { @@ -114,8 +116,16 @@ void ATraceMemoryDump::startFrame() { /** * logTraces reads from mCurrentValues and logs the counters with ATRACE. + * + * gpuMemoryIsAlreadyInDump must be true if GrDirectContext::dumpMemoryStatistics(...) was called + * with this tracer, false otherwise. Leaving this false allows this function to quickly query total + * and purgable GPU memory without the caller having to spend time in + * GrDirectContext::dumpMemoryStatistics(...) first, which iterates over every resource in the GPU + * cache. This can save significant time, but buckets all GPU memory into a single "misc" track, + * which may be a loss of granularity depending on the GPU backend and the categories defined in + * sResourceMap. */ -void ATraceMemoryDump::logTraces() { +void ATraceMemoryDump::logTraces(bool gpuMemoryIsAlreadyInDump, GrDirectContext* grContext) { // Accumulate data from last dumpName recordAndResetCountersIfNeeded(""); uint64_t hwui_all_frame_memory = 0; @@ -126,6 +136,20 @@ void ATraceMemoryDump::logTraces() { ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory); } } + + if (!gpuMemoryIsAlreadyInDump && grContext) { + // Total GPU memory + int gpuResourceCount; + size_t gpuResourceBytes; + grContext->getResourceCacheUsage(&gpuResourceCount, &gpuResourceBytes); + hwui_all_frame_memory += (uint64_t)gpuResourceBytes; + ATRACE_INT64("HWUI Misc Memory", gpuResourceBytes); + + // Purgable subset of GPU memory + size_t purgeableGpuResourceBytes = grContext->getResourceCachePurgeableBytes(); + ATRACE_INT64("Purgeable HWUI Misc Memory", purgeableGpuResourceBytes); + } + ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory); } diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h index 4592711dd5b5..777d1a2ddb5b 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.h +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h @@ -16,6 +16,7 @@ #pragma once +#include <GrDirectContext.h> #include <SkString.h> #include <SkTraceMemoryDump.h> @@ -50,7 +51,7 @@ public: void startFrame(); - void logTraces(); + void logTraces(bool gpuMemoryIsAlreadyInDump, GrDirectContext* grContext); private: std::string mLastDumpName; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 30d461271c89..eb4d4948dc53 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -269,13 +269,14 @@ void CacheManager::onFrameCompleted() { cancelDestroyContext(); mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC); if (ATRACE_ENABLED()) { + ATRACE_NAME("dumpingMemoryStatistics"); static skiapipeline::ATraceMemoryDump tracer; tracer.startFrame(); SkGraphics::DumpMemoryStatistics(&tracer); - if (mGrContext) { + if (mGrContext && Properties::debugTraceGpuResourceCategories) { mGrContext->dumpMemoryStatistics(&tracer); } - tracer.logTraces(); + tracer.logTraces(Properties::debugTraceGpuResourceCategories, mGrContext.get()); } } diff --git a/lint-baseline.xml b/lint-baseline.xml index 79b21551a76e..660884a18010 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -433,7 +433,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;" + errorLine1=" boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -444,7 +444,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;" + errorLine1=" boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -455,7 +455,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) > 0;" + errorLine1=" boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -466,7 +466,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;" + errorLine1=" boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -562,4 +562,4 @@ column="74"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 89792c7638cb..9616b5d44540 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -48,6 +48,7 @@ import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -141,7 +142,9 @@ public final class MediaRouter2 { * dispatch. This is only used to determine what callback a route should be assigned to (added, * removed, changed) in {@link #dispatchFilteredRoutesUpdatedOnHandler(List)}. */ - private volatile ArrayMap<String, MediaRoute2Info> mPreviousRoutes = new ArrayMap<>(); + private volatile ArrayMap<String, MediaRoute2Info> mPreviousFilteredRoutes = new ArrayMap<>(); + + private final Map<String, MediaRoute2Info> mPreviousUnfilteredRoutes = new ArrayMap<>(); /** * Stores the latest copy of exposed routes after filtering, sorting, and deduplication. Can be @@ -282,6 +285,8 @@ public final class MediaRouter2 { MediaRouter2 instance = sAppToProxyRouterMap.get(key); if (instance == null) { instance = new MediaRouter2(context, looper, clientPackageName, user); + // Register proxy router after instantiation to avoid race condition. + ((ProxyMediaRouter2Impl) instance.mImpl).registerProxyRouter(); sAppToProxyRouterMap.put(key, instance); } return instance; @@ -368,6 +373,7 @@ public final class MediaRouter2 { new SystemRoutingController( ProxyMediaRouter2Impl.getSystemSessionInfoImpl( mMediaRouterService, clientPackageName)); + mImpl = new ProxyMediaRouter2Impl(context, clientPackageName, user); } @@ -713,7 +719,7 @@ public final class MediaRouter2 { mImpl.transfer( controller.getRoutingSessionInfo(), route, - android.os.Process.myUserHandle(), + Process.myUserHandle(), mContext.getPackageName()); } @@ -883,7 +889,7 @@ public final class MediaRouter2 { newRoutes.stream().map(MediaRoute2Info::getId).collect(Collectors.toSet()); for (MediaRoute2Info route : newRoutes) { - MediaRoute2Info prevRoute = mPreviousRoutes.get(route.getId()); + MediaRoute2Info prevRoute = mPreviousFilteredRoutes.get(route.getId()); if (prevRoute == null) { addedRoutes.add(route); } else if (!prevRoute.equals(route)) { @@ -891,21 +897,21 @@ public final class MediaRouter2 { } } - for (int i = 0; i < mPreviousRoutes.size(); i++) { - if (!newRouteIds.contains(mPreviousRoutes.keyAt(i))) { - removedRoutes.add(mPreviousRoutes.valueAt(i)); + for (int i = 0; i < mPreviousFilteredRoutes.size(); i++) { + if (!newRouteIds.contains(mPreviousFilteredRoutes.keyAt(i))) { + removedRoutes.add(mPreviousFilteredRoutes.valueAt(i)); } } // update previous routes for (MediaRoute2Info route : removedRoutes) { - mPreviousRoutes.remove(route.getId()); + mPreviousFilteredRoutes.remove(route.getId()); } for (MediaRoute2Info route : addedRoutes) { - mPreviousRoutes.put(route.getId(), route); + mPreviousFilteredRoutes.put(route.getId(), route); } for (MediaRoute2Info route : changedRoutes) { - mPreviousRoutes.put(route.getId(), route); + mPreviousFilteredRoutes.put(route.getId(), route); } if (!addedRoutes.isEmpty()) { @@ -924,6 +930,27 @@ public final class MediaRouter2 { } } + void dispatchControllerUpdatedIfNeededOnHandler(Map<String, MediaRoute2Info> routesMap) { + List<RoutingController> controllers = getControllers(); + for (RoutingController controller : controllers) { + + for (String selectedRoute : controller.getRoutingSessionInfo().getSelectedRoutes()) { + if (routesMap.containsKey(selectedRoute) + && mPreviousUnfilteredRoutes.containsKey(selectedRoute)) { + MediaRoute2Info currentRoute = routesMap.get(selectedRoute); + MediaRoute2Info oldRoute = mPreviousUnfilteredRoutes.get(selectedRoute); + if (!currentRoute.equals(oldRoute)) { + notifyControllerUpdated(controller); + break; + } + } + } + } + + mPreviousUnfilteredRoutes.clear(); + mPreviousUnfilteredRoutes.putAll(routesMap); + } + void updateRoutesOnHandler(List<MediaRoute2Info> newRoutes) { synchronized (mLock) { mRoutes.clear(); @@ -945,6 +972,11 @@ public final class MediaRouter2 { MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler, this, mFilteredRoutes)); + mHandler.sendMessage( + obtainMessage( + MediaRouter2::dispatchControllerUpdatedIfNeededOnHandler, + this, + new HashMap<>(mRoutes))); } /** @@ -1528,7 +1560,7 @@ public final class MediaRouter2 { UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle(); String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName(); - return Objects.equals(android.os.Process.myUserHandle(), transferInitiatorUserHandle) + return Objects.equals(Process.myUserHandle(), transferInitiatorUserHandle) && Objects.equals(mContext.getPackageName(), transferInitiatorPackageName); } @@ -2153,18 +2185,19 @@ public final class MediaRouter2 { mClientUser = user; mClientPackageName = clientPackageName; mClient = new Client(); + mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; + } + public void registerProxyRouter() { try { mMediaRouterService.registerProxyRouter( mClient, - context.getApplicationContext().getPackageName(), - clientPackageName, - user); + mContext.getApplicationContext().getPackageName(), + mClientPackageName, + mClientUser); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } - - mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; } @Override @@ -2294,11 +2327,7 @@ public final class MediaRouter2 { List<RoutingSessionInfo> sessionInfos = getRoutingSessions(); RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1); - transfer( - targetSession, - route, - android.os.Process.myUserHandle(), - mContext.getPackageName()); + transfer(targetSession, route, Process.myUserHandle(), mContext.getPackageName()); } @Override @@ -3165,8 +3194,12 @@ public final class MediaRouter2 { return; } - requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE, - android.os.Process.myUserHandle(), mContext.getPackageName()); + requestCreateController( + controller, + route, + MANAGER_REQUEST_ID_NONE, + Process.myUserHandle(), + mContext.getPackageName()); } @Override diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl index a7ec6c692416..8ce1b6d5264d 100644 --- a/media/java/android/media/projection/IMediaProjectionManager.aidl +++ b/media/java/android/media/projection/IMediaProjectionManager.aidl @@ -37,31 +37,41 @@ interface IMediaProjectionManager { const String EXTRA_PACKAGE_REUSING_GRANTED_CONSENT = "extra_media_projection_package_reusing_consent"; + /** + * Returns whether a combination of process UID and package has the projection permission. + * + * @param processUid the process UID as returned by {@link android.os.Process.myUid()}. + */ @UnsupportedAppUsage - boolean hasProjectionPermission(int uid, String packageName); + boolean hasProjectionPermission(int processUid, String packageName); /** * Returns a new {@link IMediaProjection} instance associated with the given package. + * + * @param processUid the process UID as returned by {@link android.os.Process.myUid()}. */ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - IMediaProjection createProjection(int uid, String packageName, int type, + IMediaProjection createProjection(int processUid, String packageName, int type, boolean permanentGrant); /** * Returns the current {@link IMediaProjection} instance associated with the given - * package, or {@code null} if it is not possible to re-use the current projection. + * package and process UID, or {@code null} if it is not possible to re-use the current + * projection. * * <p>Should only be invoked when the user has reviewed consent for a re-used projection token. * Requires that there is a prior session waiting for the user to review consent, and the given * package details match those on the current projection. * * @see {@link #isCurrentProjection} + * + * @param processUid the process UID as returned by {@link android.os.Process.myUid()}. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - IMediaProjection getProjection(int uid, String packageName); + IMediaProjection getProjection(int processUid, String packageName); /** * Returns {@code true} if the given {@link IMediaProjection} corresponds to the current @@ -162,8 +172,8 @@ interface IMediaProjectionManager { * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an + * app or SystemUI. * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED. * Indicates the entry point for requesting the permission. Must be * a valid state defined @@ -172,49 +182,49 @@ interface IMediaProjectionManager { @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource); + oneway void notifyPermissionRequestInitiated(int hostProcessUid, int sessionCreationSource); /** * Notifies system server that the permission request was displayed. * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or + * SystemUI. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyPermissionRequestDisplayed(int hostUid); + oneway void notifyPermissionRequestDisplayed(int hostProcessUid); /** * Notifies system server that the permission request was cancelled. * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or + * SystemUI. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyPermissionRequestCancelled(int hostUid); + oneway void notifyPermissionRequestCancelled(int hostProcessUid); /** * Notifies system server that the app selector was displayed. * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or + * SystemUI. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyAppSelectorDisplayed(int hostUid); + oneway void notifyAppSelectorDisplayed(int hostProcessUid); @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode); + void notifyWindowingModeChanged(int contentToRecord, int targetProcessUid, int windowingMode); } diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java index 262f5f198c22..096e8adf2f68 100644 --- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java +++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java @@ -50,8 +50,16 @@ public final class BluetoothMidiDevice { private static final String TAG = "BluetoothMidiDevice"; private static final boolean DEBUG = false; - private static final int DEFAULT_PACKET_SIZE = 20; - private static final int MAX_PACKET_SIZE = 512; + // Bluetooth services should subtract 5 bytes from the MTU for headers. + private static final int HEADER_SIZE = 5; + // Min MTU size for BLE + private static final int MIN_L2CAP_MTU = 23; + // 23 (min L2CAP MTU) - 5 (header size) + private static final int DEFAULT_PACKET_SIZE = MIN_L2CAP_MTU - HEADER_SIZE; + // Max MTU size on Android + private static final int MAX_ANDROID_MTU = 517; + // 517 (max Android MTU) - 5 (header size) + private static final int MAX_PACKET_SIZE = MAX_ANDROID_MTU - HEADER_SIZE; // Bluetooth MIDI Gatt service UUID private static final UUID MIDI_SERVICE = UUID.fromString( @@ -135,8 +143,8 @@ public final class BluetoothMidiDevice { // switch to receiving notifications mBluetoothGatt.readCharacteristic(characteristic); - // Request higher MTU size - if (!gatt.requestMtu(MAX_PACKET_SIZE)) { + // Request max MTU size + if (!gatt.requestMtu(MAX_ANDROID_MTU)) { Log.e(TAG, "request mtu failed"); mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); @@ -204,8 +212,15 @@ public final class BluetoothMidiDevice { public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { Log.d(TAG, "onMtuChanged callback received. mtu: " + mtu + ", status: " + status); if (status == BluetoothGatt.GATT_SUCCESS) { - mPacketEncoder.setMaxPacketSize(Math.min(mtu, MAX_PACKET_SIZE)); - mPacketDecoder.setMaxPacketSize(Math.min(mtu, MAX_PACKET_SIZE)); + int packetSize = Math.min(mtu - HEADER_SIZE, MAX_PACKET_SIZE); + if (packetSize <= 0) { + Log.e(TAG, "onMtuChanged non-positive packet size: " + packetSize); + packetSize = DEFAULT_PACKET_SIZE; + } else if (packetSize < DEFAULT_PACKET_SIZE) { + Log.w(TAG, "onMtuChanged small packet size: " + packetSize); + } + mPacketEncoder.setMaxPacketSize(packetSize); + mPacketDecoder.setMaxPacketSize(packetSize); } else { mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); diff --git a/nfc-extras/Android.bp b/nfc-extras/Android.bp index cb9ac6fd1d33..1f187e8eab74 100644 --- a/nfc-extras/Android.bp +++ b/nfc-extras/Android.bp @@ -23,9 +23,13 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +// TODO(b/303286040): Deprecate this API surface since this is no longer supported (see ag/443092) java_sdk_library { name: "com.android.nfc_extras", srcs: ["java/**/*.java"], + libs: [ + "framework-nfc.impl" + ], api_packages: ["com.android.nfc_extras"], dist_group: "android", } diff --git a/nfc/Android.bp b/nfc/Android.bp index bf9f47ceb0a7..5d1404a56a9e 100644 --- a/nfc/Android.bp +++ b/nfc/Android.bp @@ -10,7 +10,15 @@ package { filegroup { name: "framework-nfc-non-updatable-sources", path: "java", - srcs: [], + srcs: [ + "java/android/nfc/NfcServiceManager.java", + "java/android/nfc/cardemulation/ApduServiceInfo.aidl", + "java/android/nfc/cardemulation/ApduServiceInfo.java", + "java/android/nfc/cardemulation/NfcFServiceInfo.aidl", + "java/android/nfc/cardemulation/NfcFServiceInfo.java", + "java/android/nfc/cardemulation/AidGroup.aidl", + "java/android/nfc/cardemulation/AidGroup.java", + ], } filegroup { @@ -30,10 +38,22 @@ java_sdk_library { libs: [ "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage ], + static_libs: [ + "android.nfc.flags-aconfig-java", + "android.permission.flags-aconfig-java", + ], srcs: [ ":framework-nfc-updatable-sources", + ":framework-nfc-javastream-protos", ], - defaults: ["framework-non-updatable-unbundled-defaults"], + defaults: ["framework-module-defaults"], + sdk_version: "module_current", + min_sdk_version: "34", // should be 35 (making it 34 for compiling for `-next`) + installable: true, + optimize: { + enabled: false, + }, + hostdex: true, // for hiddenapi check permitted_packages: [ "android.nfc", "com.android.nfc", @@ -41,11 +61,18 @@ java_sdk_library { hidden_api_packages: [ "com.android.nfc", ], - aidl: { - include_dirs: [ - // TODO (b/303286040): Remove these when we change to |framework-module-defaults| - "frameworks/base/nfc/java", - "frameworks/base/core/java", - ], + impl_library_visibility: [ + "//frameworks/base:__subpackages__", + "//cts/tests/tests/nfc", + "//packages/apps/Nfc:__subpackages__", + ], + jarjar_rules: ":nfc-jarjar-rules", + lint: { + strict_updatability_linting: true, }, } + +filegroup { + name: "nfc-jarjar-rules", + srcs: ["jarjar-rules.txt"], +} diff --git a/nfc/api/current.txt b/nfc/api/current.txt index d802177e249b..24c145f890c4 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -1 +1,455 @@ // Signature format: 2.0 +package android.nfc { + + public final class AvailableNfcAntenna implements android.os.Parcelable { + ctor public AvailableNfcAntenna(int, int); + method public int describeContents(); + method public int getLocationX(); + method public int getLocationY(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.AvailableNfcAntenna> CREATOR; + } + + public class FormatException extends java.lang.Exception { + ctor public FormatException(); + ctor public FormatException(String); + ctor public FormatException(String, Throwable); + } + + public final class NdefMessage implements android.os.Parcelable { + ctor public NdefMessage(byte[]) throws android.nfc.FormatException; + ctor public NdefMessage(android.nfc.NdefRecord, android.nfc.NdefRecord...); + ctor public NdefMessage(android.nfc.NdefRecord[]); + method public int describeContents(); + method public int getByteArrayLength(); + method public android.nfc.NdefRecord[] getRecords(); + method public byte[] toByteArray(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NdefMessage> CREATOR; + } + + public final class NdefRecord implements android.os.Parcelable { + ctor public NdefRecord(short, byte[], byte[], byte[]); + ctor @Deprecated public NdefRecord(byte[]) throws android.nfc.FormatException; + method public static android.nfc.NdefRecord createApplicationRecord(String); + method public static android.nfc.NdefRecord createExternal(String, String, byte[]); + method public static android.nfc.NdefRecord createMime(String, byte[]); + method public static android.nfc.NdefRecord createTextRecord(String, String); + method public static android.nfc.NdefRecord createUri(android.net.Uri); + method public static android.nfc.NdefRecord createUri(String); + method public int describeContents(); + method public byte[] getId(); + method public byte[] getPayload(); + method public short getTnf(); + method public byte[] getType(); + method @Deprecated public byte[] toByteArray(); + method public String toMimeType(); + method public android.net.Uri toUri(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NdefRecord> CREATOR; + field public static final byte[] RTD_ALTERNATIVE_CARRIER; + field public static final byte[] RTD_HANDOVER_CARRIER; + field public static final byte[] RTD_HANDOVER_REQUEST; + field public static final byte[] RTD_HANDOVER_SELECT; + field public static final byte[] RTD_SMART_POSTER; + field public static final byte[] RTD_TEXT; + field public static final byte[] RTD_URI; + field public static final short TNF_ABSOLUTE_URI = 3; // 0x3 + field public static final short TNF_EMPTY = 0; // 0x0 + field public static final short TNF_EXTERNAL_TYPE = 4; // 0x4 + field public static final short TNF_MIME_MEDIA = 2; // 0x2 + field public static final short TNF_UNCHANGED = 6; // 0x6 + field public static final short TNF_UNKNOWN = 5; // 0x5 + field public static final short TNF_WELL_KNOWN = 1; // 0x1 + } + + public final class NfcAdapter { + method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean allowTransaction(); + method public void disableForegroundDispatch(android.app.Activity); + method public void disableReaderMode(android.app.Activity); + method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean disallowTransaction(); + method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]); + method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle); + method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context); + method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo(); + method @FlaggedApi("android.nfc.enable_nfc_charging") @Nullable public android.nfc.WlcLDeviceInfo getWlcLDeviceInfo(); + method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler); + method public boolean isEnabled(); + method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported(); + method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled(); + method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported(); + 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"; + field public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; + field public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; + field @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT) public static final String ACTION_TRANSACTION_DETECTED = "android.nfc.action.TRANSACTION_DETECTED"; + field public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE"; + field public static final String EXTRA_AID = "android.nfc.extra.AID"; + field public static final String EXTRA_DATA = "android.nfc.extra.DATA"; + field public static final String EXTRA_ID = "android.nfc.extra.ID"; + field public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; + field public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON = "android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON"; + 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 + field public static final int FLAG_READER_NFC_F = 4; // 0x4 + field public static final int FLAG_READER_NFC_V = 8; // 0x8 + field public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 256; // 0x100 + field public static final int FLAG_READER_SKIP_NDEF_CHECK = 128; // 0x80 + field public static final int PREFERRED_PAYMENT_CHANGED = 2; // 0x2 + field public static final int PREFERRED_PAYMENT_LOADED = 1; // 0x1 + field public static final int PREFERRED_PAYMENT_UPDATED = 3; // 0x3 + field public static final int STATE_OFF = 1; // 0x1 + field public static final int STATE_ON = 3; // 0x3 + field public static final int STATE_TURNING_OFF = 4; // 0x4 + field public static final int STATE_TURNING_ON = 2; // 0x2 + } + + @Deprecated public static interface NfcAdapter.CreateBeamUrisCallback { + method @Deprecated public android.net.Uri[] createBeamUris(android.nfc.NfcEvent); + } + + @Deprecated public static interface NfcAdapter.CreateNdefMessageCallback { + method @Deprecated public android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent); + } + + @Deprecated public static interface NfcAdapter.OnNdefPushCompleteCallback { + method @Deprecated public void onNdefPushComplete(android.nfc.NfcEvent); + } + + public static interface NfcAdapter.OnTagRemovedListener { + method public void onTagRemoved(); + } + + public static interface NfcAdapter.ReaderCallback { + method public void onTagDiscovered(android.nfc.Tag); + } + + public final class NfcAntennaInfo implements android.os.Parcelable { + ctor public NfcAntennaInfo(int, int, boolean, @NonNull java.util.List<android.nfc.AvailableNfcAntenna>); + method public int describeContents(); + method @NonNull public java.util.List<android.nfc.AvailableNfcAntenna> getAvailableNfcAntennas(); + method public int getDeviceHeight(); + method public int getDeviceWidth(); + method public boolean isDeviceFoldable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NfcAntennaInfo> CREATOR; + } + + public final class NfcEvent { + field public final android.nfc.NfcAdapter nfcAdapter; + field public final int peerLlcpMajorVersion; + field public final int peerLlcpMinorVersion; + } + + public final class NfcManager { + method public android.nfc.NfcAdapter getDefaultAdapter(); + } + + public final class Tag implements android.os.Parcelable { + method public int describeContents(); + method public byte[] getId(); + method public String[] getTechList(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.Tag> CREATOR; + } + + public class TagLostException extends java.io.IOException { + ctor public TagLostException(); + ctor public TagLostException(String); + } + + @FlaggedApi("android.nfc.enable_nfc_charging") public final class WlcLDeviceInfo implements android.os.Parcelable { + ctor public WlcLDeviceInfo(double, double, double, int); + method public int describeContents(); + method public double getBatteryLevel(); + method public double getProductId(); + method public int getState(); + method public double getTemperature(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CONNECTED_CHARGING = 2; // 0x2 + field public static final int CONNECTED_DISCHARGING = 3; // 0x3 + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.WlcLDeviceInfo> CREATOR; + field public static final int DISCONNECTED = 1; // 0x1 + } + +} + +package android.nfc.cardemulation { + + public final class CardEmulation { + method public boolean categoryAllowsForegroundPreference(String); + method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public java.util.List<java.lang.String> getAidsForPreferredPaymentService(); + method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, String); + method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public CharSequence getDescriptionForPreferredPaymentService(); + method public static android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter); + method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getRouteDestinationForPreferredPaymentService(); + method public int getSelectionModeForCategory(String); + method public boolean isDefaultServiceForAid(android.content.ComponentName, String); + method public boolean isDefaultServiceForCategory(android.content.ComponentName, String); + method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>); + method public boolean removeAidsForService(android.content.ComponentName, String); + method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String); + method public boolean setPreferredService(android.app.Activity, android.content.ComponentName); + method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setServiceObserveModeDefault(@NonNull android.content.ComponentName, boolean); + method public boolean supportsAidPrefixRegistration(); + method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName); + method public boolean unsetPreferredService(android.app.Activity); + field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; + field public static final String CATEGORY_OTHER = "other"; + field public static final String CATEGORY_PAYMENT = "payment"; + field public static final String EXTRA_CATEGORY = "category"; + field public static final String EXTRA_SERVICE_COMPONENT = "component"; + field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1 + field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2 + field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0 + } + + public abstract class HostApduService extends android.app.Service { + ctor public HostApduService(); + method public final void notifyUnhandled(); + method public final android.os.IBinder onBind(android.content.Intent); + method public abstract void onDeactivated(int); + method public abstract byte[] processCommandApdu(byte[], android.os.Bundle); + method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.os.Bundle>); + method public final void sendResponseApdu(byte[]); + field public static final int DEACTIVATION_DESELECTED = 1; // 0x1 + field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0 + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA"; + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN"; + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP"; + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_A = 65; // 0x0041 'A' + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_B = 66; // 0x0042 'B' + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_F = 70; // 0x0046 'F' + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE"; + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_OFF = 88; // 0x0058 'X' + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_ON = 79; // 0x004f 'O' + field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x0055 'U' + field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE"; + field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service"; + } + + public abstract class HostNfcFService extends android.app.Service { + ctor public HostNfcFService(); + method public final android.os.IBinder onBind(android.content.Intent); + method public abstract void onDeactivated(int); + method public abstract byte[] processNfcFPacket(byte[], android.os.Bundle); + method public final void sendResponsePacket(byte[]); + field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0 + field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_NFCF_SERVICE"; + field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_nfcf_service"; + } + + public final class NfcFCardEmulation { + method public boolean disableService(android.app.Activity) throws java.lang.RuntimeException; + method public boolean enableService(android.app.Activity, android.content.ComponentName) throws java.lang.RuntimeException; + method public static android.nfc.cardemulation.NfcFCardEmulation getInstance(android.nfc.NfcAdapter); + method public String getNfcid2ForService(android.content.ComponentName) throws java.lang.RuntimeException; + method public String getSystemCodeForService(android.content.ComponentName) throws java.lang.RuntimeException; + method public boolean registerSystemCodeForService(android.content.ComponentName, String) throws java.lang.RuntimeException; + method public boolean setNfcid2ForService(android.content.ComponentName, String) throws java.lang.RuntimeException; + method public boolean unregisterSystemCodeForService(android.content.ComponentName) throws java.lang.RuntimeException; + } + + public abstract class OffHostApduService extends android.app.Service { + ctor public OffHostApduService(); + field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"; + field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service"; + } + +} + +package android.nfc.tech { + + public final class IsoDep implements android.nfc.tech.TagTechnology { + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public static android.nfc.tech.IsoDep get(android.nfc.Tag); + method public byte[] getHiLayerResponse(); + method public byte[] getHistoricalBytes(); + method public int getMaxTransceiveLength(); + method public android.nfc.Tag getTag(); + method public int getTimeout(); + method public boolean isConnected(); + method public boolean isExtendedLengthApduSupported(); + method public void setTimeout(int); + method public byte[] transceive(byte[]) throws java.io.IOException; + } + + public final class MifareClassic implements android.nfc.tech.TagTechnology { + method public boolean authenticateSectorWithKeyA(int, byte[]) throws java.io.IOException; + method public boolean authenticateSectorWithKeyB(int, byte[]) throws java.io.IOException; + method public int blockToSector(int); + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public void decrement(int, int) throws java.io.IOException; + method public static android.nfc.tech.MifareClassic get(android.nfc.Tag); + method public int getBlockCount(); + method public int getBlockCountInSector(int); + method public int getMaxTransceiveLength(); + method public int getSectorCount(); + method public int getSize(); + method public android.nfc.Tag getTag(); + method public int getTimeout(); + method public int getType(); + method public void increment(int, int) throws java.io.IOException; + method public boolean isConnected(); + method public byte[] readBlock(int) throws java.io.IOException; + method public void restore(int) throws java.io.IOException; + method public int sectorToBlock(int); + method public void setTimeout(int); + method public byte[] transceive(byte[]) throws java.io.IOException; + method public void transfer(int) throws java.io.IOException; + method public void writeBlock(int, byte[]) throws java.io.IOException; + field public static final int BLOCK_SIZE = 16; // 0x10 + field public static final byte[] KEY_DEFAULT; + field public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY; + field public static final byte[] KEY_NFC_FORUM; + field public static final int SIZE_1K = 1024; // 0x400 + field public static final int SIZE_2K = 2048; // 0x800 + field public static final int SIZE_4K = 4096; // 0x1000 + field public static final int SIZE_MINI = 320; // 0x140 + field public static final int TYPE_CLASSIC = 0; // 0x0 + field public static final int TYPE_PLUS = 1; // 0x1 + field public static final int TYPE_PRO = 2; // 0x2 + field public static final int TYPE_UNKNOWN = -1; // 0xffffffff + } + + public final class MifareUltralight implements android.nfc.tech.TagTechnology { + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public static android.nfc.tech.MifareUltralight get(android.nfc.Tag); + method public int getMaxTransceiveLength(); + method public android.nfc.Tag getTag(); + method public int getTimeout(); + method public int getType(); + method public boolean isConnected(); + method public byte[] readPages(int) throws java.io.IOException; + method public void setTimeout(int); + method public byte[] transceive(byte[]) throws java.io.IOException; + method public void writePage(int, byte[]) throws java.io.IOException; + field public static final int PAGE_SIZE = 4; // 0x4 + field public static final int TYPE_ULTRALIGHT = 1; // 0x1 + field public static final int TYPE_ULTRALIGHT_C = 2; // 0x2 + field public static final int TYPE_UNKNOWN = -1; // 0xffffffff + } + + public final class Ndef implements android.nfc.tech.TagTechnology { + method public boolean canMakeReadOnly(); + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public static android.nfc.tech.Ndef get(android.nfc.Tag); + method public android.nfc.NdefMessage getCachedNdefMessage(); + method public int getMaxSize(); + method public android.nfc.NdefMessage getNdefMessage() throws android.nfc.FormatException, java.io.IOException; + method public android.nfc.Tag getTag(); + method public String getType(); + method public boolean isConnected(); + method public boolean isWritable(); + method public boolean makeReadOnly() throws java.io.IOException; + method public void writeNdefMessage(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException; + field public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic"; + field public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1"; + field public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2"; + field public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3"; + field public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4"; + } + + public final class NdefFormatable implements android.nfc.tech.TagTechnology { + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public void format(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException; + method public void formatReadOnly(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException; + method public static android.nfc.tech.NdefFormatable get(android.nfc.Tag); + method public android.nfc.Tag getTag(); + method public boolean isConnected(); + } + + public final class NfcA implements android.nfc.tech.TagTechnology { + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public static android.nfc.tech.NfcA get(android.nfc.Tag); + method public byte[] getAtqa(); + method public int getMaxTransceiveLength(); + method public short getSak(); + method public android.nfc.Tag getTag(); + method public int getTimeout(); + method public boolean isConnected(); + method public void setTimeout(int); + method public byte[] transceive(byte[]) throws java.io.IOException; + } + + public final class NfcB implements android.nfc.tech.TagTechnology { + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public static android.nfc.tech.NfcB get(android.nfc.Tag); + method public byte[] getApplicationData(); + method public int getMaxTransceiveLength(); + method public byte[] getProtocolInfo(); + method public android.nfc.Tag getTag(); + method public boolean isConnected(); + method public byte[] transceive(byte[]) throws java.io.IOException; + } + + public final class NfcBarcode implements android.nfc.tech.TagTechnology { + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public static android.nfc.tech.NfcBarcode get(android.nfc.Tag); + method public byte[] getBarcode(); + method public android.nfc.Tag getTag(); + method public int getType(); + method public boolean isConnected(); + field public static final int TYPE_KOVIO = 1; // 0x1 + field public static final int TYPE_UNKNOWN = -1; // 0xffffffff + } + + public final class NfcF implements android.nfc.tech.TagTechnology { + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public static android.nfc.tech.NfcF get(android.nfc.Tag); + method public byte[] getManufacturer(); + method public int getMaxTransceiveLength(); + method public byte[] getSystemCode(); + method public android.nfc.Tag getTag(); + method public int getTimeout(); + method public boolean isConnected(); + method public void setTimeout(int); + method public byte[] transceive(byte[]) throws java.io.IOException; + } + + public final class NfcV implements android.nfc.tech.TagTechnology { + method public void close() throws java.io.IOException; + method public void connect() throws java.io.IOException; + method public static android.nfc.tech.NfcV get(android.nfc.Tag); + method public byte getDsfId(); + method public int getMaxTransceiveLength(); + method public byte getResponseFlags(); + method public android.nfc.Tag getTag(); + method public boolean isConnected(); + method public byte[] transceive(byte[]) throws java.io.IOException; + } + + public interface TagTechnology extends java.io.Closeable { + method public void connect() throws java.io.IOException; + method public android.nfc.Tag getTag(); + method public boolean isConnected(); + } + +} + diff --git a/nfc/api/lint-baseline.txt b/nfc/api/lint-baseline.txt new file mode 100644 index 000000000000..ef9aab6e7641 --- /dev/null +++ b/nfc/api/lint-baseline.txt @@ -0,0 +1,95 @@ +// Baseline format: 1.0 +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED: + Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED: + Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED: + Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior + + +MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent): + Missing nullability on method `onBind` return +MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent) parameter #0: + Missing nullability on parameter `intent` in method `onBind` + + +RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity): + Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]): + Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String): + Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String): + Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String): + Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.nfc.tech.IsoDep#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]): + Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]): + Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int): + Method 'decrement' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int): + Method 'increment' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int): + Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#restore(int): + Method 'restore' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#transfer(int): + Method 'transfer' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]): + Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int): + Method 'readPages' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]): + Method 'writePage' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#getNdefMessage(): + Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#isWritable(): + Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#makeReadOnly(): + Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage): + Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage): + Method 'format' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage): + Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcA#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcA#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcF#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcF#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.TagTechnology#close(): + Method 'close' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.TagTechnology#connect(): + Method 'connect' documentation mentions permissions without declaring @RequiresPermission diff --git a/nfc/api/module-lib-current.txt b/nfc/api/module-lib-current.txt index d802177e249b..5ebe91111ec0 100644 --- a/nfc/api/module-lib-current.txt +++ b/nfc/api/module-lib-current.txt @@ -1 +1,10 @@ // Signature format: 2.0 +package android.nfc { + + public class NfcFrameworkInitializer { + method public static void registerServiceWrappers(); + method public static void setNfcServiceManager(@NonNull android.nfc.NfcServiceManager); + } + +} + diff --git a/nfc/api/module-lib-lint-baseline.txt b/nfc/api/module-lib-lint-baseline.txt new file mode 100644 index 000000000000..f7f8ee3ddda5 --- /dev/null +++ b/nfc/api/module-lib-lint-baseline.txt @@ -0,0 +1,93 @@ +// Baseline format: 1.0 +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED: + Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED: + Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: + Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @BroadcastBehavior +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED: + Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior + +RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity): + Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]): + Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String): + Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String): + Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String): + Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.nfc.tech.IsoDep#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]): + Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]): + Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int): + Method 'decrement' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int): + Method 'increment' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int): + Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#restore(int): + Method 'restore' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#transfer(int): + Method 'transfer' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]): + Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int): + Method 'readPages' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]): + Method 'writePage' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#getNdefMessage(): + Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#isWritable(): + Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#makeReadOnly(): + Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage): + Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage): + Method 'format' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage): + Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcA#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcA#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcF#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcF#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.TagTechnology#close(): + Method 'close' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.TagTechnology#connect(): + Method 'connect' documentation mentions permissions without declaring @RequiresPermission + +SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: + Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/nfc/api/removed.txt b/nfc/api/removed.txt index d802177e249b..fb82b5ddbb21 100644 --- a/nfc/api/removed.txt +++ b/nfc/api/removed.txt @@ -1 +1,17 @@ // Signature format: 2.0 +package android.nfc { + + public final class NfcAdapter { + method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void disableForegroundNdefPush(android.app.Activity); + method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage); + method @Deprecated @android.compat.annotation.UnsupportedAppUsage public boolean invokeBeam(android.app.Activity); + method @Deprecated @android.compat.annotation.UnsupportedAppUsage public boolean isNdefPushEnabled(); + method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setBeamPushUris(android.net.Uri[], android.app.Activity); + method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity); + method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...); + method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...); + method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...); + } + +} + diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index d802177e249b..40672a1adc15 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -1 +1,53 @@ // Signature format: 2.0 +package android.nfc { + + public final class NfcAdapter { + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler, String[]); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable(); + method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean); + method @FlaggedApi("android.nfc.enable_nfc_charging") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableWlc(boolean); + method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState(); + method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int); + method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn(); + method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported(); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagIntentAppPreferenceSupported(); + method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener); + method @FlaggedApi("android.nfc.enable_nfc_charging") public void registerWlcStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.WlcStateListener); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler); + method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean); + method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderMode(boolean); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean); + method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener); + method @FlaggedApi("android.nfc.enable_nfc_charging") public void unregisterWlcStateListener(@NonNull android.nfc.NfcAdapter.WlcStateListener); + field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC"; + field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff + field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0 + field public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; // 0xfffffffe + } + + public static interface NfcAdapter.ControllerAlwaysOnListener { + method public void onControllerAlwaysOnChanged(boolean); + } + + public static interface NfcAdapter.NfcUnlockHandler { + method public boolean onUnlockAttempted(android.nfc.Tag); + } + + @FlaggedApi("android.nfc.enable_nfc_charging") public static interface NfcAdapter.WlcStateListener { + method public void onWlcStateChanged(@NonNull android.nfc.WlcLDeviceInfo); + } + +} + +package android.nfc.cardemulation { + + public final class CardEmulation { + method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public android.nfc.cardemulation.ApduServiceInfo getPreferredPaymentService(); + method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int); + } + +} + diff --git a/nfc/api/system-lint-baseline.txt b/nfc/api/system-lint-baseline.txt new file mode 100644 index 000000000000..761c8e63df81 --- /dev/null +++ b/nfc/api/system-lint-baseline.txt @@ -0,0 +1,105 @@ +// Baseline format: 1.0 +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED: + Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED: + Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: + Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @BroadcastBehavior +BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED: + Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior + + +MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent): + Missing nullability on method `onBind` return +MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent) parameter #0: + Missing nullability on parameter `intent` in method `onBind` + + +RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity): + Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]): + Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String): + Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String): + Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String): + Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission +RequiresPermission: android.nfc.tech.IsoDep#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]): + Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]): + Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int): + Method 'decrement' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int): + Method 'increment' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int): + Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#restore(int): + Method 'restore' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#transfer(int): + Method 'transfer' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]): + Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int): + Method 'readPages' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]): + Method 'writePage' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#getNdefMessage(): + Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#isWritable(): + Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#makeReadOnly(): + Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage): + Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage): + Method 'format' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage): + Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcA#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcA#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcF#getTimeout(): + Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcF#setTimeout(int): + Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]): + Method 'transceive' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.TagTechnology#close(): + Method 'close' documentation mentions permissions without declaring @RequiresPermission +RequiresPermission: android.nfc.tech.TagTechnology#connect(): + Method 'connect' documentation mentions permissions without declaring @RequiresPermission + +SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle): + SAM-compatible parameters (such as parameter 2, "callback", in android.nfc.NfcAdapter.enableReaderMode) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler): + SAM-compatible parameters (such as parameter 3, "tagRemovedListener", in android.nfc.NfcAdapter.ignore) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions + +SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: + Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/nfc/api/system-removed.txt b/nfc/api/system-removed.txt index d802177e249b..c6eaa57b6b06 100644 --- a/nfc/api/system-removed.txt +++ b/nfc/api/system-removed.txt @@ -1 +1,12 @@ // Signature format: 2.0 +package android.nfc { + + public final class NfcAdapter { + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush(); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush(); + method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int); + field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1 + } + +} + diff --git a/nfc/jarjar-rules.txt b/nfc/jarjar-rules.txt new file mode 100644 index 000000000000..4cd652d6af7f --- /dev/null +++ b/nfc/jarjar-rules.txt @@ -0,0 +1,38 @@ +# Used by framework-nfc for proto debug dumping +rule android.app.PendingIntentProto* com.android.nfc.x.@0 +rule android.content.ComponentNameProto* com.android.nfc.x.@0 +rule android.content.IntentProto* com.android.nfc.x.@0 +rule android.content.IntentFilterProto* com.android.nfc.x.@0 +rule android.content.AuthorityEntryProto* com.android.nfc.x.@0 +rule android.nfc.cardemulation.AidGroupProto* com.android.nfc.x.@0 +rule android.nfc.cardemulation.ApduServiceInfoProto* com.android.nfc.x.@0 +rule android.nfc.cardemulation.NfcFServiceInfoProto* com.android.nfc.x.@0 +rule android.nfc.NdefMessageProto* com.android.nfc.x.@0 +rule android.nfc.NdefRecordProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.CardEmulationManagerProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.RegisteredServicesCacheProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.RegisteredNfcFServicesCacheProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.PreferredServicesProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.EnabledNfcFServicesProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.RegisteredAidCacheProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.AidRoutingManagerProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.RegisteredT3tIdentifiersCacheProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.SystemCodeRoutingManagerProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.HostEmulationManagerProto* com.android.nfc.x.@0 +rule com.android.nfc.cardemulation.HostNfcFEmulationManagerProto* com.android.nfc.x.@0 +rule com.android.nfc.NfcServiceDumpProto* com.android.nfc.x.@0 +rule com.android.nfc.DiscoveryParamsProto* com.android.nfc.x.@0 +rule com.android.nfc.NfcDispatcherProto* com.android.nfc.x.@0 +rule android.os.PersistableBundleProto* com.android.nfc.x.@0 + +# Used by framework-nfc for reading trunk stable flags +rule android.nfc.FakeFeatureFlagsImpl* com.android.nfc.x.@0 +rule android.nfc.FeatureFlags* com.android.nfc.x.@0 +rule android.nfc.Flags* com.android.nfc.x.@0 +rule android.permission.flags.** com.android.nfc.x.@0 + +# Used by framework-nfc for misc utilities +rule android.os.PatternMatcher* com.android.nfc.x.@0 + +rule com.android.incident.Privacy* com.android.nfc.x.@0 +rule com.android.incident.PrivacyFlags* com.android.nfc.x.@0 diff --git a/core/java/android/nfc/ApduList.aidl b/nfc/java/android/nfc/ApduList.aidl index f6236b2bfb3b..f6236b2bfb3b 100644 --- a/core/java/android/nfc/ApduList.aidl +++ b/nfc/java/android/nfc/ApduList.aidl diff --git a/core/java/android/nfc/ApduList.java b/nfc/java/android/nfc/ApduList.java index 027141d99c30..027141d99c30 100644 --- a/core/java/android/nfc/ApduList.java +++ b/nfc/java/android/nfc/ApduList.java diff --git a/core/java/android/nfc/AvailableNfcAntenna.aidl b/nfc/java/android/nfc/AvailableNfcAntenna.aidl index 9d06e2d7d5eb..9d06e2d7d5eb 100644 --- a/core/java/android/nfc/AvailableNfcAntenna.aidl +++ b/nfc/java/android/nfc/AvailableNfcAntenna.aidl diff --git a/core/java/android/nfc/AvailableNfcAntenna.java b/nfc/java/android/nfc/AvailableNfcAntenna.java index 6e6512a04971..6e6512a04971 100644 --- a/core/java/android/nfc/AvailableNfcAntenna.java +++ b/nfc/java/android/nfc/AvailableNfcAntenna.java diff --git a/core/java/android/nfc/Constants.java b/nfc/java/android/nfc/Constants.java index f76833063605..f76833063605 100644 --- a/core/java/android/nfc/Constants.java +++ b/nfc/java/android/nfc/Constants.java diff --git a/core/java/android/nfc/ErrorCodes.java b/nfc/java/android/nfc/ErrorCodes.java index d2c81cd27d90..d2c81cd27d90 100644 --- a/core/java/android/nfc/ErrorCodes.java +++ b/nfc/java/android/nfc/ErrorCodes.java diff --git a/core/java/android/nfc/FormatException.java b/nfc/java/android/nfc/FormatException.java index a57de1e0e21a..a57de1e0e21a 100644 --- a/core/java/android/nfc/FormatException.java +++ b/nfc/java/android/nfc/FormatException.java diff --git a/core/java/android/nfc/IAppCallback.aidl b/nfc/java/android/nfc/IAppCallback.aidl index b06bf06d5197..b06bf06d5197 100644 --- a/core/java/android/nfc/IAppCallback.aidl +++ b/nfc/java/android/nfc/IAppCallback.aidl diff --git a/core/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl index 286cf2890eea..286cf2890eea 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/nfc/java/android/nfc/INfcAdapter.aidl diff --git a/core/java/android/nfc/INfcAdapterExtras.aidl b/nfc/java/android/nfc/INfcAdapterExtras.aidl index cde57c58ca1f..cde57c58ca1f 100644 --- a/core/java/android/nfc/INfcAdapterExtras.aidl +++ b/nfc/java/android/nfc/INfcAdapterExtras.aidl diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl index f4b46046bc3e..f4b46046bc3e 100644 --- a/core/java/android/nfc/INfcCardEmulation.aidl +++ b/nfc/java/android/nfc/INfcCardEmulation.aidl diff --git a/core/java/android/nfc/INfcControllerAlwaysOnListener.aidl b/nfc/java/android/nfc/INfcControllerAlwaysOnListener.aidl index 1bb7680d2fed..1bb7680d2fed 100644 --- a/core/java/android/nfc/INfcControllerAlwaysOnListener.aidl +++ b/nfc/java/android/nfc/INfcControllerAlwaysOnListener.aidl diff --git a/core/java/android/nfc/INfcDta.aidl b/nfc/java/android/nfc/INfcDta.aidl index 4cc59271362b..4cc59271362b 100644 --- a/core/java/android/nfc/INfcDta.aidl +++ b/nfc/java/android/nfc/INfcDta.aidl diff --git a/core/java/android/nfc/INfcFCardEmulation.aidl b/nfc/java/android/nfc/INfcFCardEmulation.aidl index 124bfac4f0d0..124bfac4f0d0 100644 --- a/core/java/android/nfc/INfcFCardEmulation.aidl +++ b/nfc/java/android/nfc/INfcFCardEmulation.aidl diff --git a/core/java/android/nfc/INfcTag.aidl b/nfc/java/android/nfc/INfcTag.aidl index 170df71385bb..170df71385bb 100644 --- a/core/java/android/nfc/INfcTag.aidl +++ b/nfc/java/android/nfc/INfcTag.aidl diff --git a/core/java/android/nfc/INfcUnlockHandler.aidl b/nfc/java/android/nfc/INfcUnlockHandler.aidl index e1cace987dc3..e1cace987dc3 100644 --- a/core/java/android/nfc/INfcUnlockHandler.aidl +++ b/nfc/java/android/nfc/INfcUnlockHandler.aidl diff --git a/core/java/android/nfc/INfcWlcStateListener.aidl b/nfc/java/android/nfc/INfcWlcStateListener.aidl index c2b7075bc6e4..c2b7075bc6e4 100644 --- a/core/java/android/nfc/INfcWlcStateListener.aidl +++ b/nfc/java/android/nfc/INfcWlcStateListener.aidl diff --git a/core/java/android/nfc/ITagRemovedCallback.aidl b/nfc/java/android/nfc/ITagRemovedCallback.aidl index 2a06ff314b22..2a06ff314b22 100644 --- a/core/java/android/nfc/ITagRemovedCallback.aidl +++ b/nfc/java/android/nfc/ITagRemovedCallback.aidl diff --git a/core/java/android/nfc/NdefMessage.aidl b/nfc/java/android/nfc/NdefMessage.aidl index 378b9d05b385..378b9d05b385 100644 --- a/core/java/android/nfc/NdefMessage.aidl +++ b/nfc/java/android/nfc/NdefMessage.aidl diff --git a/core/java/android/nfc/NdefMessage.java b/nfc/java/android/nfc/NdefMessage.java index 553f6c01b016..553f6c01b016 100644 --- a/core/java/android/nfc/NdefMessage.java +++ b/nfc/java/android/nfc/NdefMessage.java diff --git a/core/java/android/nfc/NdefRecord.aidl b/nfc/java/android/nfc/NdefRecord.aidl index 10f89d0936e4..10f89d0936e4 100644 --- a/core/java/android/nfc/NdefRecord.aidl +++ b/nfc/java/android/nfc/NdefRecord.aidl diff --git a/core/java/android/nfc/NdefRecord.java b/nfc/java/android/nfc/NdefRecord.java index 7bf4355d5b35..7bf4355d5b35 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/nfc/java/android/nfc/NdefRecord.java diff --git a/core/java/android/nfc/NfcActivityManager.java b/nfc/java/android/nfc/NfcActivityManager.java index f03fc0af86b3..f03fc0af86b3 100644 --- a/core/java/android/nfc/NfcActivityManager.java +++ b/nfc/java/android/nfc/NfcActivityManager.java diff --git a/core/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java index 75f5491a416f..75f5491a416f 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/nfc/java/android/nfc/NfcAdapter.java diff --git a/core/java/android/nfc/NfcAntennaInfo.aidl b/nfc/java/android/nfc/NfcAntennaInfo.aidl index d5e79fc37282..d5e79fc37282 100644 --- a/core/java/android/nfc/NfcAntennaInfo.aidl +++ b/nfc/java/android/nfc/NfcAntennaInfo.aidl diff --git a/core/java/android/nfc/NfcAntennaInfo.java b/nfc/java/android/nfc/NfcAntennaInfo.java index b002ca21e8e3..b002ca21e8e3 100644 --- a/core/java/android/nfc/NfcAntennaInfo.java +++ b/nfc/java/android/nfc/NfcAntennaInfo.java diff --git a/core/java/android/nfc/NfcControllerAlwaysOnListener.java b/nfc/java/android/nfc/NfcControllerAlwaysOnListener.java index 6ae58fd38cbe..6ae58fd38cbe 100644 --- a/core/java/android/nfc/NfcControllerAlwaysOnListener.java +++ b/nfc/java/android/nfc/NfcControllerAlwaysOnListener.java diff --git a/core/java/android/nfc/NfcEvent.java b/nfc/java/android/nfc/NfcEvent.java index aff4f52f2bab..aff4f52f2bab 100644 --- a/core/java/android/nfc/NfcEvent.java +++ b/nfc/java/android/nfc/NfcEvent.java diff --git a/core/java/android/nfc/NfcFrameworkInitializer.java b/nfc/java/android/nfc/NfcFrameworkInitializer.java index 1ab8a1ebd72c..1ab8a1ebd72c 100644 --- a/core/java/android/nfc/NfcFrameworkInitializer.java +++ b/nfc/java/android/nfc/NfcFrameworkInitializer.java diff --git a/core/java/android/nfc/NfcManager.java b/nfc/java/android/nfc/NfcManager.java index 644e3122774b..644e3122774b 100644 --- a/core/java/android/nfc/NfcManager.java +++ b/nfc/java/android/nfc/NfcManager.java diff --git a/core/java/android/nfc/NfcServiceManager.java b/nfc/java/android/nfc/NfcServiceManager.java index 5582f1154cad..5582f1154cad 100644 --- a/core/java/android/nfc/NfcServiceManager.java +++ b/nfc/java/android/nfc/NfcServiceManager.java diff --git a/core/java/android/nfc/NfcWlcStateListener.java b/nfc/java/android/nfc/NfcWlcStateListener.java index 8d793101f41f..8d793101f41f 100644 --- a/core/java/android/nfc/NfcWlcStateListener.java +++ b/nfc/java/android/nfc/NfcWlcStateListener.java diff --git a/core/java/android/nfc/Tag.aidl b/nfc/java/android/nfc/Tag.aidl index 312261ebe76d..312261ebe76d 100644 --- a/core/java/android/nfc/Tag.aidl +++ b/nfc/java/android/nfc/Tag.aidl diff --git a/core/java/android/nfc/Tag.java b/nfc/java/android/nfc/Tag.java index 500038f14d9d..500038f14d9d 100644 --- a/core/java/android/nfc/Tag.java +++ b/nfc/java/android/nfc/Tag.java diff --git a/core/java/android/nfc/TagLostException.java b/nfc/java/android/nfc/TagLostException.java index 1981d7c0c6e0..1981d7c0c6e0 100644 --- a/core/java/android/nfc/TagLostException.java +++ b/nfc/java/android/nfc/TagLostException.java diff --git a/core/java/android/nfc/TechListParcel.aidl b/nfc/java/android/nfc/TechListParcel.aidl index 92e646f220f0..92e646f220f0 100644 --- a/core/java/android/nfc/TechListParcel.aidl +++ b/nfc/java/android/nfc/TechListParcel.aidl diff --git a/core/java/android/nfc/TechListParcel.java b/nfc/java/android/nfc/TechListParcel.java index 9f01559bd344..9f01559bd344 100644 --- a/core/java/android/nfc/TechListParcel.java +++ b/nfc/java/android/nfc/TechListParcel.java diff --git a/core/java/android/nfc/TransceiveResult.aidl b/nfc/java/android/nfc/TransceiveResult.aidl index 98f92ee03eef..98f92ee03eef 100644 --- a/core/java/android/nfc/TransceiveResult.aidl +++ b/nfc/java/android/nfc/TransceiveResult.aidl diff --git a/core/java/android/nfc/TransceiveResult.java b/nfc/java/android/nfc/TransceiveResult.java index 7992094e6ba1..7992094e6ba1 100644 --- a/core/java/android/nfc/TransceiveResult.java +++ b/nfc/java/android/nfc/TransceiveResult.java diff --git a/core/java/android/nfc/WlcLDeviceInfo.aidl b/nfc/java/android/nfc/WlcLDeviceInfo.aidl index 33143fe81162..33143fe81162 100644 --- a/core/java/android/nfc/WlcLDeviceInfo.aidl +++ b/nfc/java/android/nfc/WlcLDeviceInfo.aidl diff --git a/core/java/android/nfc/WlcLDeviceInfo.java b/nfc/java/android/nfc/WlcLDeviceInfo.java index 016431e90d8e..016431e90d8e 100644 --- a/core/java/android/nfc/WlcLDeviceInfo.java +++ b/nfc/java/android/nfc/WlcLDeviceInfo.java diff --git a/core/java/android/nfc/cardemulation/AidGroup.aidl b/nfc/java/android/nfc/cardemulation/AidGroup.aidl index 56d6fa559677..56d6fa559677 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.aidl +++ b/nfc/java/android/nfc/cardemulation/AidGroup.aidl diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/nfc/java/android/nfc/cardemulation/AidGroup.java index ae3e333051d7..ae3e333051d7 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.java +++ b/nfc/java/android/nfc/cardemulation/AidGroup.java diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.aidl b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl index a62fdd6a6c5c..a62fdd6a6c5c 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.aidl +++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java index 41dee3ab035c..41dee3ab035c 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java index 81eab71fe080..81eab71fe080 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java index 7cd2533a7dbf..7cd2533a7dbf 100644 --- a/core/java/android/nfc/cardemulation/HostApduService.java +++ b/nfc/java/android/nfc/cardemulation/HostApduService.java diff --git a/core/java/android/nfc/cardemulation/HostNfcFService.java b/nfc/java/android/nfc/cardemulation/HostNfcFService.java index 65b5ca77de62..65b5ca77de62 100644 --- a/core/java/android/nfc/cardemulation/HostNfcFService.java +++ b/nfc/java/android/nfc/cardemulation/HostNfcFService.java diff --git a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java b/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java index 48bbf5b61052..48bbf5b61052 100644 --- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.aidl b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl index 56b98ebd90fa..56b98ebd90fa 100644 --- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.aidl +++ b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java index 33bc16978721..33bc16978721 100644 --- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java +++ b/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java diff --git a/core/java/android/nfc/OWNERS b/nfc/java/android/nfc/cardemulation/OWNERS index 35e9713f5715..35e9713f5715 100644 --- a/core/java/android/nfc/OWNERS +++ b/nfc/java/android/nfc/cardemulation/OWNERS diff --git a/core/java/android/nfc/cardemulation/OffHostApduService.java b/nfc/java/android/nfc/cardemulation/OffHostApduService.java index 2286e8476d94..2286e8476d94 100644 --- a/core/java/android/nfc/cardemulation/OffHostApduService.java +++ b/nfc/java/android/nfc/cardemulation/OffHostApduService.java diff --git a/core/java/android/nfc/cardemulation/Utils.java b/nfc/java/android/nfc/cardemulation/Utils.java index 202e1cfb48f6..202e1cfb48f6 100644 --- a/core/java/android/nfc/cardemulation/Utils.java +++ b/nfc/java/android/nfc/cardemulation/Utils.java diff --git a/core/java/android/nfc/dta/NfcDta.java b/nfc/java/android/nfc/dta/NfcDta.java index 88016623434d..88016623434d 100644 --- a/core/java/android/nfc/dta/NfcDta.java +++ b/nfc/java/android/nfc/dta/NfcDta.java diff --git a/core/java/android/nfc/cardemulation/OWNERS b/nfc/java/android/nfc/dta/OWNERS index 35e9713f5715..35e9713f5715 100644 --- a/core/java/android/nfc/cardemulation/OWNERS +++ b/nfc/java/android/nfc/dta/OWNERS diff --git a/core/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig index 01a45708fddf..01a45708fddf 100644 --- a/core/java/android/nfc/flags.aconfig +++ b/nfc/java/android/nfc/flags.aconfig diff --git a/core/java/android/nfc/package.html b/nfc/java/android/nfc/package.html index 55c1d1650aa9..55c1d1650aa9 100644 --- a/core/java/android/nfc/package.html +++ b/nfc/java/android/nfc/package.html diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/nfc/java/android/nfc/tech/BasicTagTechnology.java index ae468fead7a2..ae468fead7a2 100644 --- a/core/java/android/nfc/tech/BasicTagTechnology.java +++ b/nfc/java/android/nfc/tech/BasicTagTechnology.java diff --git a/core/java/android/nfc/tech/IsoDep.java b/nfc/java/android/nfc/tech/IsoDep.java index 0ba0c5a8d13b..0ba0c5a8d13b 100644 --- a/core/java/android/nfc/tech/IsoDep.java +++ b/nfc/java/android/nfc/tech/IsoDep.java diff --git a/core/java/android/nfc/tech/MifareClassic.java b/nfc/java/android/nfc/tech/MifareClassic.java index 26f54e692289..26f54e692289 100644 --- a/core/java/android/nfc/tech/MifareClassic.java +++ b/nfc/java/android/nfc/tech/MifareClassic.java diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/nfc/java/android/nfc/tech/MifareUltralight.java index c0416a39ba76..c0416a39ba76 100644 --- a/core/java/android/nfc/tech/MifareUltralight.java +++ b/nfc/java/android/nfc/tech/MifareUltralight.java diff --git a/core/java/android/nfc/tech/Ndef.java b/nfc/java/android/nfc/tech/Ndef.java index 7d83f157a314..7d83f157a314 100644 --- a/core/java/android/nfc/tech/Ndef.java +++ b/nfc/java/android/nfc/tech/Ndef.java diff --git a/core/java/android/nfc/tech/NdefFormatable.java b/nfc/java/android/nfc/tech/NdefFormatable.java index 2240fe7f7d3b..2240fe7f7d3b 100644 --- a/core/java/android/nfc/tech/NdefFormatable.java +++ b/nfc/java/android/nfc/tech/NdefFormatable.java diff --git a/core/java/android/nfc/tech/NfcA.java b/nfc/java/android/nfc/tech/NfcA.java index 7e6648361670..7e6648361670 100644 --- a/core/java/android/nfc/tech/NfcA.java +++ b/nfc/java/android/nfc/tech/NfcA.java diff --git a/core/java/android/nfc/tech/NfcB.java b/nfc/java/android/nfc/tech/NfcB.java index 3ebd47f610c0..3ebd47f610c0 100644 --- a/core/java/android/nfc/tech/NfcB.java +++ b/nfc/java/android/nfc/tech/NfcB.java diff --git a/core/java/android/nfc/tech/NfcBarcode.java b/nfc/java/android/nfc/tech/NfcBarcode.java index 421ba7827cb1..421ba7827cb1 100644 --- a/core/java/android/nfc/tech/NfcBarcode.java +++ b/nfc/java/android/nfc/tech/NfcBarcode.java diff --git a/core/java/android/nfc/tech/NfcF.java b/nfc/java/android/nfc/tech/NfcF.java index 2ccd38875f07..2ccd38875f07 100644 --- a/core/java/android/nfc/tech/NfcF.java +++ b/nfc/java/android/nfc/tech/NfcF.java diff --git a/core/java/android/nfc/tech/NfcV.java b/nfc/java/android/nfc/tech/NfcV.java index 186c63bf07fd..186c63bf07fd 100644 --- a/core/java/android/nfc/tech/NfcV.java +++ b/nfc/java/android/nfc/tech/NfcV.java diff --git a/core/java/android/nfc/dta/OWNERS b/nfc/java/android/nfc/tech/OWNERS index 35e9713f5715..35e9713f5715 100644 --- a/core/java/android/nfc/dta/OWNERS +++ b/nfc/java/android/nfc/tech/OWNERS diff --git a/core/java/android/nfc/tech/TagTechnology.java b/nfc/java/android/nfc/tech/TagTechnology.java index 839fe429b338..839fe429b338 100644 --- a/core/java/android/nfc/tech/TagTechnology.java +++ b/nfc/java/android/nfc/tech/TagTechnology.java diff --git a/core/java/android/nfc/tech/package.html b/nfc/java/android/nfc/tech/package.html index a99828f90c5b..a99828f90c5b 100644 --- a/core/java/android/nfc/tech/package.html +++ b/nfc/java/android/nfc/tech/package.html diff --git a/core/tests/nfctests/Android.bp b/nfc/tests/Android.bp index f81be494ad18..62566ee89fb8 100644 --- a/core/tests/nfctests/Android.bp +++ b/nfc/tests/Android.bp @@ -30,6 +30,7 @@ android_test { "truth", ], libs: [ + "framework-nfc.impl", "android.test.runner", ], srcs: ["src/**/*.java"], diff --git a/core/tests/nfctests/AndroidManifest.xml b/nfc/tests/AndroidManifest.xml index 99e2c34c656b..99e2c34c656b 100644 --- a/core/tests/nfctests/AndroidManifest.xml +++ b/nfc/tests/AndroidManifest.xml diff --git a/core/tests/nfctests/AndroidTest.xml b/nfc/tests/AndroidTest.xml index 490d6f5df197..490d6f5df197 100644 --- a/core/tests/nfctests/AndroidTest.xml +++ b/nfc/tests/AndroidTest.xml diff --git a/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java b/nfc/tests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java index 48f4288d401e..48f4288d401e 100644 --- a/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java +++ b/nfc/tests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java diff --git a/core/tests/nfctests/src/android/nfc/TechListParcelTest.java b/nfc/tests/src/android/nfc/TechListParcelTest.java index a12bbbc6884b..a12bbbc6884b 100644 --- a/core/tests/nfctests/src/android/nfc/TechListParcelTest.java +++ b/nfc/tests/src/android/nfc/TechListParcelTest.java diff --git a/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml b/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml deleted file mode 100644 index 9d16f32db932..000000000000 --- a/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- - ~ 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. - --> - -<!-- Copied from //frameworks/base/core/res/res/drawable/item_background_material.xml --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@color/autofill_light_colorControlHighlight"> - <item android:id="@android:id/mask"> - <color android:color="@android:color/white"/> - </item> -</ripple>
\ No newline at end of file diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml index 2f0c83b6556a..5becc86927d2 100644 --- a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml +++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ 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. @@ -23,8 +23,8 @@ android:shape="rectangle" android:top="1dp"> <shape> - <corners android:radius="28dp" /> - <solid android:color="@android:color/system_surface_container_high_light" /> + <corners android:radius="16dp" /> + <solid android:color="@color/dropdown_container" /> </shape> </item> </ripple>
\ No newline at end of file diff --git a/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml b/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml deleted file mode 100644 index e4e9f7ac85a9..000000000000 --- a/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml +++ /dev/null @@ -1,37 +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. - --> - -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@android:id/content" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/autofill.Dataset"> - <ImageView - android:id="@android:id/icon1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_alignParentStart="true" - android:background="@null"/> - <TextView - android:id="@android:id/text1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_toEndOf="@android:id/icon1" - style="@style/autofill.TextAppearance"/> - -</RelativeLayout> diff --git a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml new file mode 100644 index 000000000000..cb6c6b473244 --- /dev/null +++ b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml @@ -0,0 +1,45 @@ +<!-- + ~ 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. + --> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxWidth="@dimen/autofill_dropdown_layout_width" + android:elevation="3dp"> + + <ImageView + android:id="@android:id/icon1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_alignParentStart="true" + android:background="@null"/> + <TextView + android:id="@android:id/text1" + android:layout_width="@dimen/autofill_dropdown_text_width" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_toEndOf="@android:id/icon1" + style="@style/autofill.TextTitle"/> + <TextView + android:id="@android:id/text2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@android:id/text1" + android:layout_toEndOf="@android:id/icon1" + style="@style/autofill.TextSubtitle"/> + +</RelativeLayout> diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml index 63b9f24d9033..dcb7ef9c3ed8 100644 --- a/packages/CredentialManager/res/values/colors.xml +++ b/packages/CredentialManager/res/values/colors.xml @@ -16,23 +16,8 @@ <!-- Color palette --> <resources> - <color name="autofill_light_colorPrimary">@color/primary_material_light</color> - <color name="autofill_light_colorAccent">@color/accent_material_light</color> - <color name="autofill_light_colorControlHighlight">@color/ripple_material_light</color> - <color name="autofill_light_colorButtonNormal">@color/button_material_light</color> - - <!-- Text colors --> - <color name="autofill_light_textColorPrimary">@color/abc_primary_text_material_light</color> - <color name="autofill_light_textColorSecondary">@color/abc_secondary_text_material_light</color> - <color name="autofill_light_textColorHint">@color/abc_hint_foreground_material_light</color> - <color name="autofill_light_textColorHintInverse">@color/abc_hint_foreground_material_dark - </color> - <color name="autofill_light_textColorHighlight">@color/highlighted_text_material_light</color> - <color name="autofill_light_textColorLink">@color/autofill_light_colorAccent</color> - <!-- These colors are used for Remote Views. --> - <color name="background_dark_mode">#0E0C0B</color> - <color name="background">#F1F3F4</color> - <color name="text_primary_dark_mode">#DFDEDB</color> - <color name="text_primary">#202124</color> + <color name="text_primary">#1A1B20</color> + <color name="text_secondary">#44474F</color> + <color name="dropdown_container">#F3F3FA</color> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml index 67003a330974..2a4719d027e2 100644 --- a/packages/CredentialManager/res/values/dimens.xml +++ b/packages/CredentialManager/res/values/dimens.xml @@ -17,6 +17,12 @@ --> <resources> - <dimen name="autofill_view_padding">16dp</dimen> - <dimen name="autofill_icon_size">16dp</dimen> + <dimen name="autofill_view_top_padding">12dp</dimen> + <dimen name="autofill_view_right_padding">24dp</dimen> + <dimen name="autofill_view_bottom_padding">12dp</dimen> + <dimen name="autofill_view_left_padding">16dp</dimen> + <dimen name="autofill_view_icon_to_text_padding">10dp</dimen> + <dimen name="autofill_icon_size">24dp</dimen> + <dimen name="autofill_dropdown_layout_width">296dp</dimen> + <dimen name="autofill_dropdown_text_width">240dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/res/values/styles.xml b/packages/CredentialManager/res/values/styles.xml index 4a5761acdd83..7de941ebd4a5 100644 --- a/packages/CredentialManager/res/values/styles.xml +++ b/packages/CredentialManager/res/values/styles.xml @@ -15,24 +15,13 @@ --> <resources> - <style name="autofill.TextAppearance.Small" parent="@style/autofill.TextAppearance"> - <item name="android:textSize">12sp</item> - </style> - - - <style name="autofill.Dataset" parent=""> - <item name="android:background">@drawable/autofill_light_selectable_item_background</item> - </style> - - <style name="autofill.TextAppearance" parent=""> - <item name="android:textColor">@color/autofill_light_textColorPrimary</item> - <item name="android:textColorHint">@color/autofill_light_textColorHint</item> - <item name="android:textColorHighlight">@color/autofill_light_textColorHighlight</item> - <item name="android:textColorLink">@color/autofill_light_textColorLink</item> + <style name="autofill.TextTitle" parent=""> + <item name="android:fontFamily">google-sans-medium</item> <item name="android:textSize">14sp</item> </style> - <style name="autofill.TextAppearance.Primary"> - <item name="android:textColor">@color/autofill_light_textColorPrimary</item> + <style name="autofill.TextSubtitle" parent=""> + <item name="android:fontFamily">google-sans-text</item> + <item name="android:textSize">12sp</item> </style> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index b2c23a401117..58467afe43a4 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -57,6 +57,7 @@ import com.android.credentialmanager.common.ui.RemoteViewsFactory import com.android.credentialmanager.getflow.ProviderDisplayInfo import com.android.credentialmanager.getflow.toProviderDisplayInfo import com.android.credentialmanager.ktx.credentialEntry +import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.model.get.CredentialEntryInfo import com.android.credentialmanager.model.get.ProviderInfo import org.json.JSONException @@ -313,12 +314,14 @@ class CredentialAutofillService : AutofillService() { var i = 0 var datasetAdded = false - val duplicateDisplayNames: MutableMap<String, Boolean> = mutableMapOf() + val duplicateDisplayNamesForPasskeys: MutableMap<String, Boolean> = mutableMapOf() providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach { val credentialEntry = it.sortedCredentialEntryList.first() - credentialEntry.displayName?.let {displayName -> - val duplicateEntry = duplicateDisplayNames.contains(displayName) - duplicateDisplayNames[displayName] = duplicateEntry + if (credentialEntry.credentialType == CredentialType.PASSKEY) { + credentialEntry.displayName?.let { displayName -> + val duplicateEntry = duplicateDisplayNamesForPasskeys.contains(displayName) + duplicateDisplayNamesForPasskeys[displayName] = duplicateEntry + } } } providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{ @@ -355,12 +358,19 @@ class CredentialAutofillService : AutofillService() { } else { spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1] } - val displayName : String = primaryEntry.displayName ?: primaryEntry.userName + val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY + && primaryEntry.displayName != null) { + primaryEntry.displayName!! + } else { + primaryEntry.userName + } val sliceBuilder = InlineSuggestionUi .newContentBuilder(pendingIntent) .setTitle(displayName) sliceBuilder.setStartIcon(icon) - if (duplicateDisplayNames[displayName] == true) { + if (primaryEntry.credentialType == + CredentialType.PASSKEY && duplicateDisplayNamesForPasskeys[displayName] + == true) { sliceBuilder.setSubtitle(primaryEntry.userName) } inlinePresentation = InlinePresentation( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt index 4dc7f00c1550..e039dead043e 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt @@ -21,6 +21,7 @@ import android.content.res.Configuration import android.widget.RemoteViews import androidx.core.content.ContextCompat import com.android.credentialmanager.model.get.CredentialEntryInfo +import com.android.credentialmanager.model.CredentialType import android.graphics.drawable.Icon class RemoteViewsFactory { @@ -29,48 +30,87 @@ class RemoteViewsFactory { private const val setAdjustViewBoundsMethodName = "setAdjustViewBounds" private const val setMaxHeightMethodName = "setMaxHeight" private const val setBackgroundResourceMethodName = "setBackgroundResource" + private const val bulletPoint = "\u2022" + private const val passwordCharacterLength = 15 fun createDropdownPresentation( context: Context, icon: Icon, credentialEntryInfo: CredentialEntryInfo ): RemoteViews { - val padding = context.resources.getDimensionPixelSize(com.android - .credentialmanager.R.dimen.autofill_view_padding) var layoutId: Int = com.android.credentialmanager.R.layout - .autofill_dataset_left_with_item_tag_hint + .credman_dropdown_presentation_layout val remoteViews = RemoteViews(context.packageName, layoutId) - setRemoteViewsPaddings(remoteViews, padding) - val textColorPrimary = getTextColorPrimary(isDarkMode(context), context); - remoteViews.setTextColor(android.R.id.text1, textColorPrimary); - remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName) - + if (credentialEntryInfo.credentialType == CredentialType.UNKNOWN) { + return remoteViews + } + setRemoteViewsPaddings(remoteViews, context) + if (credentialEntryInfo.credentialType == CredentialType.PASSKEY) { + val displayName = credentialEntryInfo.displayName ?: credentialEntryInfo.userName + remoteViews.setTextViewText(android.R.id.text1, displayName) + val secondaryText = if (credentialEntryInfo.displayName != null) + (credentialEntryInfo.userName + " " + bulletPoint + " " + + credentialEntryInfo.credentialTypeDisplayName + + " " + bulletPoint + " " + credentialEntryInfo.providerDisplayName) + else (credentialEntryInfo.credentialTypeDisplayName + " " + bulletPoint + " " + + credentialEntryInfo.providerDisplayName) + remoteViews.setTextViewText(android.R.id.text2, secondaryText) + } else { + remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName) + remoteViews.setTextViewText(android.R.id.text2, + bulletPoint.repeat(passwordCharacterLength)) + } + val textColorPrimary = ContextCompat.getColor(context, + com.android.credentialmanager.R.color.text_primary) + remoteViews.setTextColor(android.R.id.text1, textColorPrimary) + val textColorSecondary = ContextCompat.getColor(context, com.android + .credentialmanager.R.color.text_secondary) + remoteViews.setTextColor(android.R.id.text2, textColorSecondary) remoteViews.setImageViewIcon(android.R.id.icon1, icon); remoteViews.setBoolean( android.R.id.icon1, setAdjustViewBoundsMethodName, true); remoteViews.setInt( android.R.id.icon1, - setMaxHeightMethodName, + setMaxHeightMethodName, context.resources.getDimensionPixelSize( com.android.credentialmanager.R.dimen.autofill_icon_size)); - val drawableId = if (isDarkMode(context)) - com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one_dark - else com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one + val drawableId = + com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one remoteViews.setInt( android.R.id.content, setBackgroundResourceMethodName, drawableId); return remoteViews } private fun setRemoteViewsPaddings( - remoteViews: RemoteViews, - padding: Int) { - val halfPadding = padding / 2 + remoteViews: RemoteViews, context: Context) { + val leftPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_left_padding) + val iconToTextPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_icon_to_text_padding) + val rightPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_right_padding) + val topPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_top_padding) + val bottomPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_bottom_padding) + remoteViews.setViewPadding( + android.R.id.icon1, + leftPadding, + /* top=*/0, + /* right=*/0, + /* bottom=*/0) remoteViews.setViewPadding( android.R.id.text1, - halfPadding, - halfPadding, - halfPadding, - halfPadding) + iconToTextPadding, + /* top=*/topPadding, + /* right=*/rightPadding, + /* bottom=*/0) + remoteViews.setViewPadding( + android.R.id.text2, + iconToTextPadding, + /* top=*/0, + /* right=*/rightPadding, + /* bottom=*/bottomPadding) } private fun isDarkMode(context: Context): Boolean { @@ -78,11 +118,5 @@ class RemoteViewsFactory { Configuration.UI_MODE_NIGHT_MASK return currentNightMode == Configuration.UI_MODE_NIGHT_YES } - - private fun getTextColorPrimary(darkMode: Boolean, context: Context): Int { - return if (darkMode) ContextCompat.getColor( - context, com.android.credentialmanager.R.color.text_primary_dark_mode) - else ContextCompat.getColor(context, com.android.credentialmanager.R.color.text_primary) - } } } diff --git a/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml b/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml index 7f1651721d84..8127e1a6d5a2 100644 --- a/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml +++ b/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" @@ -8,7 +8,7 @@ errorLine2=" ~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java" - line="78" + line="79" column="34"/> </issue> @@ -19,7 +19,7 @@ errorLine2=" ~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java" - line="90" + line="91" column="36"/> </issue> @@ -30,7 +30,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="43" + line="45" column="46"/> </issue> @@ -41,7 +41,7 @@ errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="65" + line="67" column="9"/> </issue> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="72" + line="74" column="9"/> </issue> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="80" + line="82" column="9"/> </issue> @@ -74,8 +74,8 @@ errorLine2=" ~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="105" + line="107" column="26"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 8b16d64bcfe7..5da4b9518a31 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -61,7 +61,6 @@ android_library { "src/**/*.kt", ], lint: { - baseline_filename: "lint-baseline.xml", extra_check_modules: ["SettingsLibLintChecker"], }, } diff --git a/packages/SettingsLib/EmergencyNumber/lint-baseline.xml b/packages/SettingsLib/EmergencyNumber/lint-baseline.xml index e9c687fd3227..13bf5f54ead3 100644 --- a/packages/SettingsLib/EmergencyNumber/lint-baseline.xml +++ b/packages/SettingsLib/EmergencyNumber/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" @@ -8,7 +8,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="77" + line="81" column="41"/> </issue> @@ -19,7 +19,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="78" + line="82" column="45"/> </issue> @@ -30,18 +30,18 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="78" + line="82" column="62"/> </issue> <issue id="NewApi" message="Call requires API level 29 (current min is 21): `android.telephony.TelephonyManager#getEmergencyNumberList`" - errorLine1=" Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(" + errorLine1=" Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(" errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="173" + line="177" column="74"/> </issue> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="196" + line="200" column="41"/> </issue> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="219" + line="223" column="69"/> </issue> @@ -74,7 +74,7 @@ errorLine2=" ~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="234" + line="238" column="41"/> </issue> @@ -85,8 +85,8 @@ errorLine2=" ~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="251" + line="255" column="52"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp index d9f74dadf281..010a6ce9d4d9 100644 --- a/packages/SettingsLib/MainSwitchPreference/Android.bp +++ b/packages/SettingsLib/MainSwitchPreference/Android.bp @@ -28,7 +28,4 @@ android_library { "com.android.extservices", "com.android.healthfitness", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } diff --git a/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml b/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml deleted file mode 100644 index cfa64a487407..000000000000 --- a/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> - - <issue - id="NewApi" - message="`@android:dimen/config_restrictedIconSize` requires API level 29 (current min is 28)" - errorLine1=' <dimen name="settingslib_restricted_icon_size">@android:dimen/config_restrictedIconSize</dimen>' - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml" - line="21" - column="52"/> - </issue> - -</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml index 26d05a621c22..45a07fe9eee3 100644 --- a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml +++ b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml @@ -1,26 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" - errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" - errorLine2=" ~~"> + message="Call requires API level 28 (current min is 23): `android.content.Context#createPackageContextAsUser`" + errorLine1=" userContext = context.createPackageContextAsUser(context.getPackageName(), 0, user);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="97" - column="56"/> + line="64" + column="35"/> </issue> <issue id="NewApi" - message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" - errorLine1=" return um.getUserProfiles().contains(UserHandle.of(userId));" - errorLine2=" ~~"> + message="Call requires API level 29 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerUser`" + errorLine1=" if (Objects.equals(dpm.getDeviceOwnerUser(), user)) {" + errorLine2=" ~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="140" - column="57"/> + line="74" + column="32"/> </issue> <issue @@ -36,35 +36,35 @@ <issue id="NewApi" - message="Call requires API level 28 (current min is 23): `android.content.Context#createPackageContextAsUser`" - errorLine1=" userContext = context.createPackageContextAsUser(context.getPackageName(), 0, user);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" + errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="64" - column="35"/> + line="97" + column="56"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerUser`" - errorLine1=" if (Objects.equals(dpm.getDeviceOwnerUser(), user)) {" - errorLine2=" ~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 29 (current min is 23): `android.content.Context#startActivityAsUser`" + errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" + errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="74" - column="32"/> + line="97" + column="17"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 23): `android.content.Context#startActivityAsUser`" - errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" + errorLine1=" return um.getUserProfiles().contains(UserHandle.of(userId));" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="97" - column="17"/> + line="120" + column="57"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/SchedulesProvider/lint-baseline.xml b/packages/SettingsLib/SchedulesProvider/lint-baseline.xml index 0744710e5224..db6a88210cd4 100644 --- a/packages/SettingsLib/SchedulesProvider/lint-baseline.xml +++ b/packages/SettingsLib/SchedulesProvider/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" diff --git a/packages/SettingsLib/SearchProvider/lint-baseline.xml b/packages/SettingsLib/SearchProvider/lint-baseline.xml index 53346e030be2..3cfca1d2cdcb 100644 --- a/packages/SettingsLib/SearchProvider/lint-baseline.xml +++ b/packages/SettingsLib/SearchProvider/lint-baseline.xml @@ -1,36 +1,47 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableResource`" - errorLine1=" super(" - errorLine2=" ~~~~~"> + message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexablesProvider`" + errorLine1="public abstract class SettingsXmlIndexProvider extends SearchIndexablesProvider {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="107" - column="13"/> + line="34" + column="56"/> </issue> <issue id="NewApi" - message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableResource`" - errorLine1=" public static final class SearchIndexableIntentResource extends SearchIndexableResource {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexablesContract#INDEXABLES_XML_RES_COLUMNS`" + errorLine1=" final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="97" - column="69"/> + line="45" + column="54"/> </issue> <issue id="NewApi" - message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexablesProvider`" - errorLine1="public abstract class SettingsXmlIndexProvider extends SearchIndexablesProvider {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#rank`" + errorLine1=" .add(XmlResource.COLUMN_RANK, indexableResource.rank)" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="34" + line="50" + column="51"/> + </issue> + + <issue + id="NewApi" + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableResource#xmlResId`" + errorLine1=" .add(XmlResource.COLUMN_XML_RESID, indexableResource.xmlResId)" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" + line="51" column="56"/> </issue> @@ -58,79 +69,68 @@ <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" - errorLine1=' this.intentAction = "android.intent.action.MAIN";' - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="113" - column="17"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" - errorLine1=" this.intentAction = intentAction;" - errorLine2=" ~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" + errorLine1=" indexableResource.intentTargetClass);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="115" - column="17"/> + line="56" + column="29"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" - errorLine1=" indexableResource.intentTargetClass);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableResource`" + errorLine1=" public static final class SearchIndexableIntentResource extends SearchIndexableResource {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="56" - column="29"/> + line="97" + column="69"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" - errorLine1=" this.intentTargetClass = className;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableResource`" + errorLine1=" super(" + errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="117" + line="107" column="13"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#rank`" - errorLine1=" .add(XmlResource.COLUMN_RANK, indexableResource.rank)" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" + errorLine1=' this.intentAction = "android.intent.action.MAIN";' + errorLine2=" ~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="50" - column="51"/> + line="113" + column="17"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableResource#xmlResId`" - errorLine1=" .add(XmlResource.COLUMN_XML_RESID, indexableResource.xmlResId)" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" + errorLine1=" this.intentAction = intentAction;" + errorLine2=" ~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="51" - column="56"/> + line="115" + column="17"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexablesContract#INDEXABLES_XML_RES_COLUMNS`" - errorLine1=" final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" + errorLine1=" this.intentTargetClass = className;" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="45" - column="54"/> + line="117" + column="13"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt index a9974dc7d389..514ad66919ee 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt @@ -39,6 +39,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import com.android.settingslib.spa.framework.theme.SettingsDimension import com.android.settingslib.spa.framework.theme.SettingsTheme @@ -68,6 +71,7 @@ fun Spinner(options: List<SpinnerOption>, selectedId: Int?, setId: (id: Int) -> ) { val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd) Button( + modifier = Modifier.semantics { role = Role.DropdownList }, onClick = { expanded = true }, colors = ButtonDefaults.buttonColors( containerColor = SettingsTheme.colorScheme.spinnerHeaderContainer, diff --git a/packages/SettingsLib/Tile/lint-baseline.xml b/packages/SettingsLib/Tile/lint-baseline.xml index 326ec0dbaa72..56b1bcafbae2 100644 --- a/packages/SettingsLib/Tile/lint-baseline.xml +++ b/packages/SettingsLib/Tile/lint-baseline.xml @@ -1,55 +1,59 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 24 (current min is 21): `java.lang.Iterable#forEach`" - errorLine1=" controllers.forEach(controller -> {" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java" - line="79" - column="21"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#createWithResource`"> + message="Call requires API level 29 (current min is 21): `android.os.Parcel#writeBoolean`" + errorLine1=" dest.writeBoolean(this instanceof ProviderTile);" + errorLine2=" ~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="312"/> + line="114" + column="14"/> </issue> <issue id="NewApi" - message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#setTint`"> + message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#createWithResource`" + errorLine1=" final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);" + errorLine2=" ~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="318"/> + line="326" + column="36"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.os.Parcel#readBoolean`"> + message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#setTint`" + errorLine1=" icon.setTint(tintColor);" + errorLine2=" ~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="373"/> + line="332" + column="22"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.os.Parcel#writeBoolean`"> + message="Call requires API level 29 (current min is 21): `android.os.Parcel#readBoolean`" + errorLine1=" final boolean isProviderTile = source.readBoolean();" + errorLine2=" ~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="108"/> + line="387" + column="51"/> </issue> <issue id="NewApi" - message="Call requires API level 31 (current min is 21): `android.content.Context#getAttributionSource`"> + message="Call requires API level 31 (current min is 21): `android.content.Context#getAttributionSource`" + errorLine1=" return provider.call(context.getAttributionSource()," + errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java" - line="565"/> + line="601" + column="42"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/Utils/lint-baseline.xml b/packages/SettingsLib/Utils/lint-baseline.xml index 3fcd56c557e8..2f6cc3ae8719 100644 --- a/packages/SettingsLib/Utils/lint-baseline.xml +++ b/packages/SettingsLib/Utils/lint-baseline.xml @@ -1,26 +1,15 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" - errorLine1=" mContext.getPackageName(), 0, UserHandle.of(managedUserId)" - errorLine2=" ~~"> - <location - file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="119" - column="70"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" - errorLine1=" intent, 0, UserHandle.of(managedUserId));" - errorLine2=" ~~"> + message="Call requires API level 24 (current min is 23): `android.os.UserManager#isManagedProfile`" + errorLine1=" return context.getSystemService(UserManager.class).isManagedProfile(userId)" + errorLine2=" ~~~~~~~~~~~~~~~~"> <location - file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="150" - column="47"/> + file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java" + line="62" + column="60"/> </issue> <issue @@ -36,68 +25,68 @@ <issue id="NewApi" - message="Call requires API level 24 (current min is 21): `android.os.UserManager#isManagedProfile`" - errorLine1=" if (mUserManager.isManagedProfile(id)) {" - errorLine2=" ~~~~~~~~~~~~~~~~"> + message="Call requires API level 29 (current min is 21): `android.content.Context#startActivityAsUser`" + errorLine1=" activityContext.startActivityAsUser(intent, UserHandle.of(userId));" + errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="173" - column="30"/> + line="80" + column="29"/> </issue> <issue id="NewApi" - message="Call requires API level 24 (current min is 23): `android.os.UserManager#isManagedProfile`" - errorLine1=" return context.getSystemService(UserManager.class).isManagedProfile(userId)" - errorLine2=" ~~~~~~~~~~~~~~~~"> + message="Call requires API level 28 (current min is 21): `android.content.Context#createPackageContextAsUser`" + errorLine1=" mContext.createPackageContextAsUser(" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location - file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java" - line="62" - column="60"/> + file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" + line="118" + column="30"/> </issue> <issue id="NewApi" - message="Call requires API level 26 (current min is 21): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`" - errorLine1=" return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" + errorLine1=" mContext.getPackageName(), 0, UserHandle.of(managedUserId)" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="163" - column="37"/> + line="119" + column="70"/> </issue> <issue id="NewApi" - message="Call requires API level 28 (current min is 21): `android.content.Context#createPackageContextAsUser`" - errorLine1=" mContext.createPackageContextAsUser(" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 29 (current min is 21): `android.content.pm.PackageManager#queryIntentActivitiesAsUser`" + errorLine1=" mPackageManager.queryIntentActivitiesAsUser(" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="118" - column="30"/> + line="149" + column="33"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.content.Context#startActivityAsUser`" - errorLine1=" activityContext.startActivityAsUser(intent, UserHandle.of(userId));" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" + errorLine1=" intent, 0, UserHandle.of(managedUserId));" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="80" - column="29"/> + line="150" + column="47"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.content.pm.PackageManager#queryIntentActivitiesAsUser`" - errorLine1=" mPackageManager.queryIntentActivitiesAsUser(" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 26 (current min is 21): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`" + errorLine1=" return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="149" - column="33"/> + line="163" + column="37"/> </issue> <issue @@ -111,4 +100,15 @@ column="53"/> </issue> + <issue + id="NewApi" + message="Call requires API level 24 (current min is 21): `android.os.UserManager#isManagedProfile`" + errorLine1=" if (mUserManager.isManagedProfile(id)) {" + errorLine2=" ~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" + line="173" + column="30"/> + </issue> + </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/lint-baseline.xml b/packages/SettingsLib/lint-baseline.xml deleted file mode 100644 index d6a23fd827d9..000000000000 --- a/packages/SettingsLib/lint-baseline.xml +++ /dev/null @@ -1,204 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.bluetooth.BluetoothDevice#setAlias`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java" - line="584"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiInfo#getSubscriptionId`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java" - line="248"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiInfo#getSubscriptionId`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java" - line="278"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiManager#registerSubsystemRestartTrackingCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="201"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiManager#unregisterSubsystemRestartTrackingCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="208"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.os.UserManager#isUserForeground`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java" - line="78"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/Utils.java" - line="498"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java" - line="225"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="215"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="86"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#unregisterTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="222"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#unregisterTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="88"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java" - line="66"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java" - line="49"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java" - line="33"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.net.wifi.WifiManager.SubsystemRestartTrackingCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="64"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="125"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.CarrierNetworkListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="124"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DataActivityListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="123"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DataConnectionStateListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="122"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DisplayInfoListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="126"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ServiceStateListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="120"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.SignalStrengthsListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="121"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="79"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="119"/> - </issue> - -</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp index 3b14712cc87e..390c9d2e98de 100644 --- a/packages/SettingsLib/search/Android.bp +++ b/packages/SettingsLib/search/Android.bp @@ -12,9 +12,6 @@ java_library { visibility: ["//visibility:private"], srcs: ["interface-src/**/*.java"], host_supported: true, - lint: { - baseline_filename: "lint-baseline.xml", - }, } android_library { diff --git a/packages/SettingsLib/search/lint-baseline.xml b/packages/SettingsLib/search/lint-baseline.xml index 7ec512b617d7..61cdb051feeb 100644 --- a/packages/SettingsLib/search/lint-baseline.xml +++ b/packages/SettingsLib/search/lint-baseline.xml @@ -1,26 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableData`" - errorLine1=" super(context);" - errorLine2=" ~~~~~"> + message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableData`" + errorLine1="public class SearchIndexableRaw extends SearchIndexableData {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java" - line="62" - column="9"/> + line="29" + column="41"/> </issue> <issue id="NewApi" - message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableData`" - errorLine1="public class SearchIndexableRaw extends SearchIndexableData {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableData`" + errorLine1=" super(context);" + errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java" - line="29" - column="41"/> + line="62" + column="9"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java index 5bc271954b25..943e3fc27ebb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java @@ -70,10 +70,8 @@ public abstract class AbstractWifiMacAddressPreferenceController @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - if (isAvailable()) { - mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS); - updateConnectivity(); - } + mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS); + updateConnectivity(); } @Override @@ -84,16 +82,16 @@ public abstract class AbstractWifiMacAddressPreferenceController @SuppressLint("HardwareIds") @Override protected void updateConnectivity() { + if (mWifiManager == null || mWifiMacAddress == null) { + return; + } + final String[] macAddresses = mWifiManager.getFactoryMacAddresses(); String macAddress = null; if (macAddresses != null && macAddresses.length > 0) { macAddress = macAddresses[0]; } - if (mWifiMacAddress == null) { - return; - } - if (TextUtils.isEmpty(macAddress) || macAddress.equals(WifiInfo.DEFAULT_MAC_ADDRESS)) { mWifiMacAddress.setSummary(R.string.status_unavailable); } else { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java index 24fd06e51418..e7487e857464 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java @@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -73,13 +74,13 @@ public class HearingAidDeviceManagerTest { private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22"; private final BluetoothClass DEVICE_CLASS = createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE); + private final Context mContext = ApplicationProvider.getApplicationContext(); private CachedBluetoothDevice mCachedDevice1; private CachedBluetoothDevice mCachedDevice2; private CachedBluetoothDeviceManager mCachedDeviceManager; private HearingAidDeviceManager mHearingAidDeviceManager; private AudioDeviceAttributes mHearingDeviceAttribute; - private final Context mContext = ApplicationProvider.getApplicationContext(); @Spy private HearingAidAudioRoutingHelper mHelper = new HearingAidAudioRoutingHelper(mContext); @Mock @@ -517,6 +518,8 @@ public class HearingAidDeviceManagerTest { when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn( mHearingDeviceAttribute); when(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true); + doReturn(true).when(mHelper).setPreferredDeviceRoutingStrategies(anyList(), + eq(mHearingDeviceAttribute), anyInt()); mHearingAidDeviceManager.onActiveDeviceChanged(mCachedDevice1); @@ -529,6 +532,8 @@ public class HearingAidDeviceManagerTest { when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn( mHearingDeviceAttribute); when(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(false); + doReturn(true).when(mHelper).setPreferredDeviceRoutingStrategies(anyList(), any(), + anyInt()); mHearingAidDeviceManager.onActiveDeviceChanged(mCachedDevice1); diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index d47527a0a191..2fea6a56daaf 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close @@ -21,10 +20,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.ElementKey @@ -41,7 +38,6 @@ import com.android.compose.animation.scene.updateSceneTransitionLayoutState import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.transform @@ -66,7 +62,6 @@ val sceneTransitions = transitions { * This is a temporary container to allow the communal UI to use [SceneTransitionLayout] for gesture * handling and transitions before the full Flexiglass layout is ready. */ -@OptIn(ExperimentalComposeUiApi::class, ExperimentalCoroutinesApi::class) @Composable fun CommunalContainer( modifier: Modifier = Modifier, @@ -83,8 +78,8 @@ fun CommunalContainer( transitions = sceneTransitions, ) - // Don't show hub mode UI if keyguard is present. This is important since we're in the shade, - // which can be opened from many locations. + // Don't show hub mode UI if keyguard is not present. This is important since we're in the + // shade, which can be opened from many locations. val isKeyguardShowing by viewModel.isKeyguardVisible.collectAsState(initial = false) // Failsafe to hide the whole SceneTransitionLayout in case of bugginess. @@ -102,56 +97,30 @@ fun CommunalContainer( onDispose { viewModel.setTransitionState(null) } } - Box(modifier = modifier.fillMaxSize()) { - SceneTransitionLayout( - state = sceneTransitionLayoutState, - modifier = Modifier.fillMaxSize(), - edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize), + SceneTransitionLayout( + state = sceneTransitionLayoutState, + modifier = modifier.fillMaxSize(), + edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize), + ) { + scene( + TransitionSceneKey.Blank, + userActions = + mapOf( + Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to TransitionSceneKey.Communal + ) ) { - scene( - TransitionSceneKey.Blank, - userActions = - mapOf( - Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to - TransitionSceneKey.Communal - ) - ) { - BlankScene { showSceneTransitionLayout = false } - } - - scene( - TransitionSceneKey.Communal, - userActions = - mapOf( - Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to - TransitionSceneKey.Blank - ), - ) { - CommunalScene(viewModel, modifier = modifier) - } + BlankScene { showSceneTransitionLayout = false } } - // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't - // block touches anymore. - Box( - modifier = - Modifier.fillMaxSize() - // Offsetting to the left so that edge swipe to open the hub still works. This - // does mean that the very right edge of the hub won't refresh the screen - // timeout, but should be good enough for a temporary solution. - .offset(x = -ContainerDimensions.EdgeSwipeSize) - .pointerInteropFilter { - viewModel.onUserActivity() - if ( - sceneTransitionLayoutState.transitionState.currentScene == - TransitionSceneKey.Blank - ) { - viewModel.onOuterTouch(it) - return@pointerInteropFilter true - } - false - } - ) + scene( + TransitionSceneKey.Communal, + userActions = + mapOf( + Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to TransitionSceneKey.Blank + ), + ) { + CommunalScene(viewModel, modifier = modifier) + } } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt index 0acc76f8d4ef..b346a70e61f9 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt @@ -20,24 +20,26 @@ import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size -import androidx.compose.ui.geometry.toRect import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.Outline -import androidx.compose.ui.graphics.Paint import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.drawOutline import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas import androidx.compose.ui.graphics.drawscope.translate -import androidx.compose.ui.graphics.withSaveLayer import androidx.compose.ui.layout.LayoutCoordinates +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope import androidx.compose.ui.node.DelegatingNode import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.node.GlobalPositionAwareModifierNode +import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.toSize @@ -88,25 +90,29 @@ private class PunchHoleNode( var size: () -> Size, var offset: () -> Offset, var shape: () -> Shape, -) : Modifier.Node(), DrawModifierNode { +) : Modifier.Node(), DrawModifierNode, LayoutModifierNode { private var lastSize: Size = Size.Unspecified private var lastLayoutDirection: LayoutDirection = LayoutDirection.Ltr private var lastOutline: Outline? = null - override fun ContentDrawScope.draw() { - val holeSize = size() - if (holeSize == Size.Zero) { - drawContent() - return + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + return measurable.measure(constraints).run { + layout(width, height) { + placeWithLayer(0, 0) { compositingStrategy = CompositingStrategy.Offscreen } + } } + } - drawIntoCanvas { canvas -> - canvas.withSaveLayer(size.toRect(), Paint()) { - drawContent() + override fun ContentDrawScope.draw() { + drawContent() - val offset = offset() - translate(offset.x, offset.y) { drawHole(holeSize) } - } + val holeSize = size() + if (holeSize != Size.Zero) { + val offset = offset() + translate(offset.x, offset.y) { drawHole(holeSize) } } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index c05591900aa0..64388b7653e0 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -315,10 +315,13 @@ internal class SceneGestureHandler( with(layoutImpl.state) { coroutineScope.onChangeScene(targetScene.key) } } - animateOffset( + swipeTransition.animateOffset( + coroutineScope = coroutineScope, initialVelocity = velocity, targetOffset = targetOffset, - targetScene = targetScene.key + onAnimationCompleted = { + layoutState.finishTransition(swipeTransition, idleScene = targetScene.key) + } ) } @@ -410,34 +413,6 @@ internal class SceneGestureHandler( } } - private fun animateOffset( - initialVelocity: Float, - targetOffset: Float, - targetScene: SceneKey, - ) { - swipeTransition.startOffsetAnimation { - coroutineScope.launch { - if (!swipeTransition.isAnimatingOffset) { - swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset) - } - swipeTransition.isAnimatingOffset = true - - swipeTransition.offsetAnimatable.animateTo( - targetOffset, - // TODO(b/290184746): Make this spring spec configurable. - spring( - stiffness = Spring.StiffnessMediumLow, - visibilityThreshold = OffsetVisibilityThreshold - ), - initialVelocity = initialVelocity, - ) - - swipeTransition.finishOffsetAnimation() - layoutState.finishTransition(swipeTransition, targetScene) - } - } - } - internal class SwipeTransition( val _fromScene: Scene, val _toScene: Scene, @@ -479,12 +454,14 @@ internal class SceneGestureHandler( private var offsetAnimationJob: Job? = null /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ - fun startOffsetAnimation(job: () -> Job) { + private fun startOffsetAnimation(job: () -> Job) { cancelOffsetAnimation() offsetAnimationJob = job() } /** Cancel any ongoing offset animation. */ + // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at + // the same time. fun cancelOffsetAnimation() { offsetAnimationJob?.cancel() finishOffsetAnimation() @@ -496,6 +473,43 @@ internal class SceneGestureHandler( dragOffset = offsetAnimatable.value } } + + // TODO(b/290184746): Make this spring spec configurable. + private val animationSpec = + spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = OffsetVisibilityThreshold + ) + + fun animateOffset( + // TODO(b/317063114) The CoroutineScope should be removed. + coroutineScope: CoroutineScope, + initialVelocity: Float, + targetOffset: Float, + onAnimationCompleted: () -> Unit, + ) { + startOffsetAnimation { + coroutineScope.launch { + animateOffset(targetOffset, initialVelocity) + onAnimationCompleted() + } + } + } + + private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) { + if (!isAnimatingOffset) { + offsetAnimatable.snapTo(dragOffset) + } + isAnimatingOffset = true + + offsetAnimatable.animateTo( + targetValue = targetOffset, + animationSpec = animationSpec, + initialVelocity = initialVelocity, + ) + + finishOffsetAnimation() + } } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index c8461d2c5415..02d30c5ea20a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -197,7 +197,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(true) featureFlags = FakeFeatureFlags() - featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false) featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false) featureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java index 1c1335f0db4d..343280de17b8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -801,6 +801,20 @@ public class AuthControllerTest extends SysuiTestCase { } @Test + public void testOnBiometricPromptDismissedCallback_hideAuthenticationDialog() { + // GIVEN a callback is registered + AuthController.Callback callback = mock(AuthController.Callback.class); + mAuthController.addCallback(callback); + + // WHEN dialog is shown and then dismissed + showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); + mAuthController.hideAuthenticationDialog(mAuthController.mCurrentDialog.getRequestId()); + + // THEN callback should be received + verify(callback).onBiometricPromptDismissed(); + } + + @Test public void testSubscribesToLogContext() { mAuthController.setBiometricContextListener(mContextListener); verify(mLogContextInteractor).addBiometricContextListener(same(mContextListener)); 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 cec2d7459817..a59a4b864ac4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -1219,6 +1219,40 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test + public void onDownTouchReceivedWithoutPreviousUp() throws RemoteException { + final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, + 0L); + final TouchProcessorResult processorResultDown = + new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, + -1 /* pointerId */, touchData); + + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + mFgExecutor.runAllReady(); + + verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + + // WHEN ACTION_DOWN is received and touch is within sensor + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + processorResultDown); + MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); + mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent); + mBiometricExecutor.runAllReady(); + firstDownEvent.recycle(); + + // And another ACTION_DOWN is received without an ACTION_UP before + MotionEvent secondDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); + mTouchListenerCaptor.getValue().onTouch(mUdfpsView, secondDownEvent); + mBiometricExecutor.runAllReady(); + secondDownEvent.recycle(); + + // THEN the touch is still processed + verify(mFingerprintManager, times(2)).onPointerDown(anyLong(), anyInt(), anyInt(), + anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), + anyBoolean()); + } + + @Test public void onTouch_pilferPointerWhenAltBouncerShowing() throws RemoteException { final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index ff6fd43745df..54510a82201a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -21,7 +21,6 @@ import android.app.Activity.RESULT_OK import android.app.smartspace.SmartspaceTarget import android.appwidget.AppWidgetHost import android.content.ComponentName -import android.os.PowerManager import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -41,13 +40,11 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.ui.MediaHost -import com.android.systemui.shade.ShadeViewController import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -64,8 +61,6 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) class CommunalEditModeViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost - @Mock private lateinit var shadeViewController: ShadeViewController - @Mock private lateinit var powerManager: PowerManager @Mock private lateinit var appWidgetHost: AppWidgetHost @Mock private lateinit var uiEventLogger: UiEventLogger @@ -97,8 +92,6 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { CommunalEditModeViewModel( withDeps.communalInteractor, appWidgetHost, - Provider { shadeViewController }, - powerManager, mediaHost, uiEventLogger, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 8e3f66464de6..a7760621e97c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.communal.view.viewmodel import android.app.smartspace.SmartspaceTarget -import android.os.PowerManager import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -36,12 +35,10 @@ import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.media.controls.ui.MediaHost -import com.android.systemui.shade.ShadeViewController import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy @@ -58,8 +55,6 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) class CommunalViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost - @Mock private lateinit var shadeViewController: ShadeViewController - @Mock private lateinit var powerManager: PowerManager private lateinit var testScope: TestScope @@ -92,8 +87,6 @@ class CommunalViewModelTest : SysuiTestCase() { withDeps.communalInteractor, WidgetInteractionHandler(mock()), withDeps.tutorialInteractor, - Provider { shadeViewController }, - powerManager, mediaHost, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt index bd1c310ab8de..c1049773cabf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt @@ -16,32 +16,45 @@ package com.android.systemui.qs.tiles.base.actions +import android.app.PendingIntent +import android.content.ComponentName import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.PackageManager +import android.content.pm.PackageManager.ResolveInfoFlags +import android.content.pm.ResolveInfo +import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.util.mockito.argThat +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatcher import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.eq +import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class QSTileIntentUserInputHandlerTest : SysuiTestCase() { - - @Mock private lateinit var activityStarted: ActivityStarter + @Mock private lateinit var packageManager: PackageManager + @Mock private lateinit var activityStarter: ActivityStarter lateinit var underTest: QSTileIntentUserInputHandler @Before fun setup() { MockitoAnnotations.initMocks(this) - underTest = QSTileIntentUserInputHandlerImpl(activityStarted) + underTest = QSTileIntentUserInputHandlerImpl(activityStarter, packageManager, user) } @Test @@ -50,6 +63,103 @@ class QSTileIntentUserInputHandlerTest : SysuiTestCase() { underTest.handle(null, intent) - verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any()) + verify(activityStarter).postStartActivityDismissingKeyguard(eq(intent), eq(0), any()) + } + + @Test + fun testPassesActivityPendingIntentToStarterAsPendingIntent() { + val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) } + + underTest.handle(null, pendingIntent, true) + + verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any()) + } + + @Test + fun testPassesActivityPendingIntentToStarterAsPendingIntentWhenNotRequestingActivityStart() { + val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) } + + underTest.handle(null, pendingIntent, false) + + verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any()) + } + + @Test + fun testPassNonActivityPendingIntentAndRequestStartingActivity_findsIntentAndStarts() { + val pendingIntent = + mock<PendingIntent> { + whenever(isActivity).thenReturn(false) + whenever(creatorPackage).thenReturn(ORIGINAL_PACKAGE) + } + setUpQueryResult(listOf(createActivityInfo(testResolvedComponent, exported = true))) + + underTest.handle(null, pendingIntent, true) + + val expectedIntent = + Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_LAUNCHER) + .setPackage(null) + .addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + ) + .setComponent(testResolvedComponent) + + verify(activityStarter) + .postStartActivityDismissingKeyguard( + argThat(IntentMatcher(expectedIntent)), + eq(0), + any() + ) + } + + @Test + fun testPassNonActivityPendingIntentAndDoNotRequestStartingActivity_doesNotStartActivity() { + val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(false) } + + underTest.handle(null, pendingIntent, false) + + verify(activityStarter, never()) + .postStartActivityDismissingKeyguard(any(Intent::class.java), eq(0), any()) + } + + private fun createActivityInfo( + componentName: ComponentName, + exported: Boolean = false, + ): ActivityInfo { + return ActivityInfo().apply { + packageName = componentName.packageName + name = componentName.className + this.exported = exported + } + } + + private fun setUpQueryResult(infos: List<ActivityInfo>) { + `when`( + packageManager.queryIntentActivitiesAsUser( + any(Intent::class.java), + any(ResolveInfoFlags::class.java), + eq(user.identifier) + ) + ) + .thenReturn(infos.map { ResolveInfo().apply { activityInfo = it } }) + } + + private class IntentMatcher(intent: Intent) : ArgumentMatcher<Intent> { + private val expectedIntent = intent + override fun matches(argument: Intent?): Boolean { + return argument?.action.equals(expectedIntent.action) && + argument?.`package`.equals(expectedIntent.`package`) && + argument?.component?.equals(expectedIntent.component)!! && + argument?.categories?.equals(expectedIntent.categories)!! && + argument?.flags?.equals(expectedIntent.flags)!! + } + } + + companion object { + private const val ORIGINAL_PACKAGE = "original_pkg" + private const val TEST_PACKAGE = "test_pkg" + private const val TEST_COMPONENT_CLASS_NAME = "test_component_class_name" + private val testResolvedComponent = ComponentName(TEST_PACKAGE, TEST_COMPONENT_CLASS_NAME) + private val user = UserHandle.of(0) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt index e44c8493244c..be2da174250b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt @@ -18,42 +18,25 @@ package com.android.systemui.qs.tiles.impl.alarm.domain.interactor import android.app.AlarmManager.AlarmClockInfo import android.app.PendingIntent -import android.content.Intent import android.provider.AlarmClock import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.nullable import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Mockito.verify @SmallTest @RunWith(AndroidJUnit4::class) class AlarmTileUserActionInteractorTest : SysuiTestCase() { - private lateinit var activityStarter: ActivityStarter - private lateinit var intentCaptor: ArgumentCaptor<Intent> - private lateinit var pendingIntentCaptor: ArgumentCaptor<PendingIntent> - - lateinit var underTest: AlarmTileUserActionInteractor - - @Before - fun setup() { - activityStarter = mock<ActivityStarter>() - intentCaptor = ArgumentCaptor.forClass(Intent::class.java) - pendingIntentCaptor = ArgumentCaptor.forClass(PendingIntent::class.java) - underTest = AlarmTileUserActionInteractor(activityStarter) - } + private val inputHandler = FakeQSTileIntentUserInputHandler() + private val underTest = AlarmTileUserActionInteractor(inputHandler) @Test fun handleClickWithDefaultIntent() = runTest { @@ -62,21 +45,21 @@ class AlarmTileUserActionInteractorTest : SysuiTestCase() { underTest.handleInput(click(inputModel)) - verify(activityStarter) - .postStartActivityDismissingKeyguard(capture(intentCaptor), eq(0), nullable()) - assertThat(intentCaptor.value.action).isEqualTo(AlarmClock.ACTION_SHOW_ALARMS) + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(AlarmClock.ACTION_SHOW_ALARMS) + } } @Test fun handleClickWithPendingIntent() = runTest { - val expectedIntent: PendingIntent = mock<PendingIntent>() + val expectedIntent = mock<PendingIntent>() val alarmInfo = AlarmClockInfo(1L, expectedIntent) val inputModel = AlarmTileModel.NextAlarmSet(true, alarmInfo) underTest.handleInput(click(inputModel)) - verify(activityStarter) - .postStartActivityDismissingKeyguard(capture(pendingIntentCaptor), nullable()) - assertThat(pendingIntentCaptor.value).isEqualTo(expectedIntent) + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOnePendingIntentInput { + assertThat(it.pendingIntent).isEqualTo(expectedIntent) + } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt new file mode 100644 index 000000000000..8a0400d092c3 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt @@ -0,0 +1,74 @@ +/* + * 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.data.repository + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.NotificationRemoteInputManager +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +class RemoteInputRepositoryImplTest : SysuiTestCase() { + @Mock private lateinit var remoteInputManager: NotificationRemoteInputManager + + private lateinit var testScope: TestScope + private lateinit var underTest: RemoteInputRepositoryImpl + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + testScope = TestScope() + underTest = RemoteInputRepositoryImpl(remoteInputManager) + } + + @Test + fun isRemoteInputActive_updatesOnChange() = + testScope.runTest { + val active by collectLastValue(underTest.isRemoteInputActive) + runCurrent() + assertThat(active).isFalse() + + val callback = withArgCaptor { + verify(remoteInputManager).addControllerCallback(capture()) + } + + callback.onRemoteInputActive(true) + runCurrent() + assertThat(active).isTrue() + + callback.onRemoteInputActive(false) + runCurrent() + assertThat(active).isFalse() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorTest.kt new file mode 100644 index 000000000000..12469ddcafc2 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorTest.kt @@ -0,0 +1,64 @@ +/* + * 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class RemoteInputInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val fakeRemoteInputRepository = kosmos.fakeRemoteInputRepository + private val underTest = kosmos.remoteInputInteractor + + @Test + fun isRemoteInputActive_true() = + testScope.runTest { + val active by collectLastValue(underTest.isRemoteInputActive) + + fakeRemoteInputRepository.isRemoteInputActive.value = true + runCurrent() + + assertThat(active).isTrue() + } + + @Test + fun isRemoteInputActive_false() = + testScope.runTest { + val active by collectLastValue(underTest.isRemoteInputActive) + + fakeRemoteInputRepository.isRemoteInputActive.value = false + runCurrent() + + assertThat(active).isFalse() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepositoryTest.kt new file mode 100644 index 000000000000..ebc81be6d4b6 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepositoryTest.kt @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.policy.data.repository + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener +import com.android.systemui.statusbar.policy.deviceProvisionedController +import com.android.systemui.testKosmos +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.verify + +@SmallTest +@RunWith(AndroidJUnit4::class) +class UserSetupRepositoryTest : SysuiTestCase() { + + private val kosmos = testKosmos() + + private val testScope = kosmos.testScope + private val deviceProvisionedController : DeviceProvisionedController = mock() + + private val underTest = UserSetupRepositoryImpl( + deviceProvisionedController, + kosmos.testDispatcher, + kosmos.applicationCoroutineScope, + ) + + @Test + fun userSetup_defaultFalse() = + testScope.runTest { + val latest by collectLastValue(underTest.isUserSetUp) + + assertThat(latest).isFalse() + } + + @Test + fun userSetup_updatesOnChange() = + testScope.runTest { + val latest by collectLastValue(underTest.isUserSetUp) + runCurrent() + + whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true) + val callback = getDeviceProvisionedListener() + callback.onUserSetupChanged() + + assertThat(latest).isTrue() + } + + private fun getDeviceProvisionedListener(): DeviceProvisionedListener { + val captor = argumentCaptor<DeviceProvisionedListener>() + verify(deviceProvisionedController).addCallback(captor.capture()) + return captor.value!! + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/UserSetupInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/UserSetupInteractorTest.kt new file mode 100644 index 000000000000..26c0f80c53de --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/UserSetupInteractorTest.kt @@ -0,0 +1,58 @@ +/* + * 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.statusbar.policy.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class UserSetupInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val fakeUserSetupRepository = kosmos.fakeUserSetupRepository + private val underTest = kosmos.userSetupInteractor + + @Test + fun isUserSetup_false() = + testScope.runTest { + val setup by collectLastValue(underTest.isUserSetUp) + + fakeUserSetupRepository.setUserSetUp(false) + + assertThat(setup).isFalse() + } + + @Test + fun isUserSetup_true() = + testScope.runTest { + val setup by collectLastValue(underTest.isUserSetUp) + + fakeUserSetupRepository.setUserSetUp(true) + + assertThat(setup).isTrue() + } +} diff --git a/packages/SystemUI/res/layout/power_notification_controls_settings.xml b/packages/SystemUI/res/layout/power_notification_controls_settings.xml deleted file mode 100644 index 83c8a51f6330..000000000000 --- a/packages/SystemUI/res/layout/power_notification_controls_settings.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 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. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> - - <include layout="@layout/switch_bar" /> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:padding="16dp" - android:text="@string/power_notification_controls_description"/> - -</LinearLayout> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 921be0502c40..19895897ef31 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -449,11 +449,11 @@ <!-- Content description after successful auth when confirmation required --> <string name="fingerprint_dialog_authenticated_confirmation">Press the unlock icon to continue</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> - <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string> + <string name="fingerprint_dialog_use_fingerprint_instead">Face not recognized. Use fingerprint instead.</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> <string name="keyguard_face_failed_use_fp">@string/fingerprint_dialog_use_fingerprint_instead</string> <!-- Message shown to inform the user a face cannot be recognized. [CHAR LIMIT=25] --> - <string name="keyguard_face_failed">Can\u2019t recognize face</string> + <string name="keyguard_face_failed">Face not recognized</string> <!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] --> <string name="keyguard_suggest_fingerprint">Use fingerprint instead</string> <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=59] --> @@ -1612,37 +1612,9 @@ <!-- Bluetooth enablement ok text [CHAR LIMIT=40] --> <string name="enable_bluetooth_confirmation_ok">Turn on</string> - <!-- [CHAR LIMIT=NONE] Importance Tuner setting title --> - <string name="tuner_full_importance_settings">Power notification controls</string> - <!-- [CHAR LIMIT=NONE] Notification camera based rotation enabled description --> <string name="rotation_lock_camera_rotation_on">On - Face-based</string> - <string name="power_notification_controls_description">With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. - \n\n<b>Level 5</b> - \n- Show at the top of the notification list - \n- Allow full screen interruption - \n- Always peek - \n\n<b>Level 4</b> - \n- Prevent full screen interruption - \n- Always peek - \n\n<b>Level 3</b> - \n- Prevent full screen interruption - \n- Never peek - \n\n<b>Level 2</b> - \n- Prevent full screen interruption - \n- Never peek - \n- Never make sound and vibration - \n\n<b>Level 1</b> - \n- Prevent full screen interruption - \n- Never peek - \n- Never make sound or vibrate - \n- Hide from lock screen and status bar - \n- Show at the bottom of the notification list - \n\n<b>Level 0</b> - \n- Block all notifications from the app - </string> - <!-- Notification Inline controls: button to dismiss the blocking helper [CHAR_LIMIT=20] --> <string name="inline_done_button">Done</string> diff --git a/packages/SystemUI/res/xml/other_settings.xml b/packages/SystemUI/res/xml/other_settings.xml deleted file mode 100644 index 7719d5e03df0..000000000000 --- a/packages/SystemUI/res/xml/other_settings.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 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. ---> - -<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:sysui="http://schemas.android.com/apk/res-auto" - android:title="@string/other"> - - <!-- importance --> - <Preference - android:key="power_notification_controls" - android:title="@string/tuner_full_importance_settings" - android:fragment="com.android.systemui.tuner.PowerNotificationControlsFragment"/> - -</PreferenceScreen> diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 05106c904d3d..326c7ef52fce 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -34,9 +34,6 @@ java_library { srcs: [ ":statslog-SystemUI-java-gen", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } android_library { @@ -74,9 +71,6 @@ android_library { min_sdk_version: "current", plugins: ["dagger2-compiler"], kotlincflags: ["-Xjvm-default=all"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -88,9 +82,6 @@ java_library { static_kotlin_stdlib: false, java_version: "1.8", min_sdk_version: "current", - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -110,7 +101,4 @@ java_library { }, java_version: "1.8", min_sdk_version: "current", - lint: { - baseline_filename: "lint-baseline.xml", - }, } diff --git a/packages/SystemUI/shared/lint-baseline.xml b/packages/SystemUI/shared/lint-baseline.xml deleted file mode 100644 index 4bd6729227e8..000000000000 --- a/packages/SystemUI/shared/lint-baseline.xml +++ /dev/null @@ -1,708 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev"> - - <issue - id="NewApi" - message="Call requires API level R (current min is 26): `android.os.RemoteException#rethrowFromSystemServer`" - errorLine1=" throw e.rethrowFromSystemServer();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java" - line="90" - column="21"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.graphics.Bitmap#getHardwareBuffer`" - errorLine1=" mBuffer != null ? mBuffer.getHardwareBuffer() : null, mRect);" - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecCompat.java" - line="39" - column="43"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `new android.graphics.ParcelableColorSpace`" - errorLine1=" ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="57" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `new android.graphics.ParcelableColorSpace`" - errorLine1=" : new ParcelableColorSpace(bitmap.getColorSpace());" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="58" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.graphics.Bitmap#getHardwareBuffer`" - errorLine1=" bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());" - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="61" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Cast from `ParcelableColorSpace` to `Parcelable` requires API level 31 (current min is 26)" - errorLine1=" bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);" - errorLine2=" ~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="62" - column="47"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`" - errorLine1=" return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer)," - errorLine2=" ~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="84" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.graphics.ParcelableColorSpace#getColorSpace`" - errorLine1=" colorSpace.getColorSpace());" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="85" - column="28"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java" - line="122" - column="47"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.util.ArraySet`" - errorLine1=" mPluginActions = new ArraySet<>(mSharedPrefs.getStringSet(PLUGIN_ACTIONS, null));" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java" - line="41" - column="26"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.util.ArraySet`" - errorLine1=" return new ArraySet<>(mPluginActions);" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java" - line="45" - column="16"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 28 (current min is 26): `android.graphics.Bitmap#createBitmap`" - errorLine1=" return Bitmap.createBitmap(picture);" - errorLine2=" ~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java" - line="113" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Builder`" - errorLine1=" mSurface = new SurfaceControl.Builder()" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="116" - column="24"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#setName`" - errorLine1=" .setName("Transition Unrotate")" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="117" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#setParent`" - errorLine1=" .setParent(parent)" - errorLine2=" ~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="119" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#build`" - errorLine1=" .build();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="120" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#reparent`" - errorLine1=" t.reparent(child, mSurface);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="137" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" SurfaceControl.Transaction t = new SurfaceControl.Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="143" - column="44"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#reparent`" - errorLine1=" t.reparent(mRotateChildren.get(i), rootLeash);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="145" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="148" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(counterLauncher.mSurface, launcherLayer);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="200" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="206" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(leash, info.getChanges().size() * 3 - i);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="216" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="223" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(counterWallpaper.mSurface, -1);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="233" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="238" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#taskId`" - errorLine1=" taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="101" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`" - errorLine1=" taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="192" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#isRunning`" - errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="116" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#isRunning`" - errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="210" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`" - errorLine1=" leash.mSurfaceControl.release();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="159" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`" - errorLine1=" mStartLeash.release();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="161" - column="25"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(change.getLeash(), info.getChanges().size() * 3 - i);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java" - line="97" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java" - line="105" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java" - line="107" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#isValid`" - errorLine1=" return mSurfaceControl != null && mSurfaceControl.isValid();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java" - line="41" - column="59"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#release`" - errorLine1=" mSurfaceControlViewHost.release();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java" - line="61" - column="37"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level R (current min is 26): `android.view.SurfaceView#getHostToken`" - errorLine1=" bundle.putBinder(KEY_HOST_TOKEN, surfaceView.getHostToken());" - errorLine2=" ~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java" - line="34" - column="54"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceView#getSurfaceControl`" - errorLine1=" bundle.putParcelable(KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl());" - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java" - line="35" - column="63"/> - </issue> - - <issue - id="NewApi" - message="Cast from `SurfaceControl` to `Parcelable` requires API level 29 (current min is 26)" - errorLine1=" bundle.putParcelable(KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl());" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java" - line="35" - column="51"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#isValid`" - errorLine1=" if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) {" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="107" - column="79"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" Transaction t = new Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="113" - column="33"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="122" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" t.setAlpha(surface, alpha);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="361" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(surface, layer);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="364" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#origActivity`" - errorLine1=" ComponentName sourceComponent = t.origActivity != null" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="73" - column="45"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#origActivity`" - errorLine1=" ? t.origActivity" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="75" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`" - errorLine1=" this.id = t.taskId;" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="78" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#baseIntent`" - errorLine1=" this.baseIntent = t.baseIntent;" - errorLine2=" ~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="80" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskDescription`" - errorLine1=" ActivityManager.TaskDescription td = taskInfo.taskDescription;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="242" - column="46"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#topActivity`" - errorLine1=" taskInfo.supportsSplitScreenMultiWindow, isLocked, td, taskInfo.topActivity);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="246" - column="72"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#topActivity`" - errorLine1=" return info.topActivity;" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java" - line="49" - column="16"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskDescription`" - errorLine1=" return info.taskDescription;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java" - line="53" - column="16"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#taskId`" - errorLine1=" onTaskMovedToFront(taskInfo.taskId);" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java" - line="70" - column="28"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`" - errorLine1=" onTaskMovedToFront(taskInfo.taskId);" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java" - line="70" - column="28"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`" - errorLine1=" thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());" - errorLine2=" ~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java" - line="69" - column="36"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.app.WallpaperColors#getColorHints`" - errorLine1=" (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TonalCompat.java" - line="42" - column="29"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" mTransaction = new Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="31" - column="24"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" mTransaction.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="35" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setBufferSize`" - errorLine1=" mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="54" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" mTransaction.setLayer(surfaceControl.mSurfaceControl, z);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="59" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" mTransaction.setAlpha(surfaceControl.mSurfaceControl, alpha);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="64" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java" - line="64" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`" - errorLine1=" .getFloat(Resources.getSystem().getIdentifier(" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java" - line="46" - column="18"/> - </issue> - -</issues> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index e457ca18d071..8e5d0dac7bef 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -1111,7 +1111,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } private boolean canDisplayUserSwitcher() { - return mFeatureFlags.isEnabled(Flags.BOUNCER_USER_SWITCHER); + return getContext().getResources().getBoolean(R.bool.config_enableBouncerUserSwitcher); } private void configureMode() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 093a1ffb4635..26f76469d0db 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -1127,6 +1127,9 @@ public class AuthController implements } mCurrentDialog.dismissFromSystemServer(); + for (Callback cb : mCallbacks) { + cb.onBiometricPromptDismissed(); + } // BiometricService will have already sent the callback to the client in this case. // This avoids a round trip to SystemUI. So, just dismiss the dialog and we're done. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index d6646378681a..81de0a283e88 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -187,7 +187,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @Nullable private UdfpsDisplayModeProvider mUdfpsDisplayMode; // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active. - private int mActivePointerId = -1; + private int mActivePointerId = MotionEvent.INVALID_POINTER_ID; // Whether a pointer has been pilfered for current gesture private boolean mPointerPilfered = false; // The timestamp of the most recent touch log. @@ -510,7 +510,16 @@ public class UdfpsController implements DozeReceiver, Dumpable { + mOverlay.getRequestId()); return false; } - if (!DeviceEntryUdfpsRefactor.isEnabled()) { + if (event.getAction() == MotionEvent.ACTION_DOWN + || event.getAction() == MotionEvent.ACTION_HOVER_ENTER) { + // Reset on ACTION_DOWN, start of new gesture + mPointerPilfered = false; + if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) { + Log.w(TAG, "onTouch down received without a preceding up"); + } + mActivePointerId = MotionEvent.INVALID_POINTER_ID; + mOnFingerDown = false; + } else if (!DeviceEntryUdfpsRefactor.isEnabled()) { if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f && !mAlternateBouncerInteractor.isVisibleState()) || mPrimaryBouncerInteractor.isInTransit()) { @@ -518,11 +527,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { return false; } } - if (event.getAction() == MotionEvent.ACTION_DOWN - || event.getAction() == MotionEvent.ACTION_HOVER_ENTER) { - // Reset on ACTION_DOWN, start of new gesture - mPointerPilfered = false; - } final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId, mOverlayParams); @@ -1080,7 +1084,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { long gestureStart, boolean isAod) { mExecution.assertIsMainThread(); - mActivePointerId = -1; + mActivePointerId = MotionEvent.INVALID_POINTER_ID; mAcquiredReceived = false; if (mOnFingerDown) { mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x, diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 28f48ce1e647..84708a49f469 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -17,17 +17,12 @@ package com.android.systemui.communal.ui.viewmodel import android.content.ComponentName -import android.os.PowerManager -import android.os.SystemClock -import android.view.MotionEvent import android.widget.RemoteViews import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.media.controls.ui.MediaHost -import com.android.systemui.shade.ShadeViewController -import javax.inject.Provider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOf @@ -35,8 +30,6 @@ import kotlinx.coroutines.flow.flowOf /** The base view model for the communal hub. */ abstract class BaseCommunalViewModel( private val communalInteractor: CommunalInteractor, - private val shadeViewController: Provider<ShadeViewController>, - private val powerManager: PowerManager, val mediaHost: MediaHost, ) { val isKeyguardVisible: Flow<Boolean> = communalInteractor.isKeyguardVisible @@ -71,26 +64,6 @@ abstract class BaseCommunalViewModel( return true } - // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block - // touches anymore. - /** Called when a touch is received outside the edge swipe area when hub mode is closed. */ - fun onOuterTouch(motionEvent: MotionEvent) { - // Forward the touch to the shade so that basic gestures like swipe up/down for - // shade/bouncer work. - shadeViewController.get().handleExternalTouch(motionEvent) - } - - // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block - // touches anymore. - /** Called to refresh the screen timeout when a user touch is received. */ - fun onUserActivity() { - powerManager.userActivity( - SystemClock.uptimeMillis(), - PowerManager.USER_ACTIVITY_EVENT_TOUCH, - 0 - ) - } - /** A list of all the communal content to be displayed in the communal hub. */ abstract val communalContent: Flow<List<CommunalContentModel>> diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index 0cbf3f1312e2..7faf653cc177 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -23,7 +23,6 @@ import android.app.ActivityOptions import android.appwidget.AppWidgetHost import android.content.ActivityNotFoundException import android.content.ComponentName -import android.os.PowerManager import android.widget.RemoteViews import com.android.internal.logging.UiEventLogger import com.android.systemui.communal.domain.interactor.CommunalInteractor @@ -32,11 +31,9 @@ import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.controls.ui.MediaHost import com.android.systemui.media.dagger.MediaModule -import com.android.systemui.shade.ShadeViewController import com.android.systemui.util.nullableAtomicReference import javax.inject.Inject import javax.inject.Named -import javax.inject.Provider import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -49,11 +46,9 @@ class CommunalEditModeViewModel constructor( private val communalInteractor: CommunalInteractor, private val appWidgetHost: AppWidgetHost, - shadeViewController: Provider<ShadeViewController>, - powerManager: PowerManager, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, private val uiEventLogger: UiEventLogger, -) : BaseCommunalViewModel(communalInteractor, shadeViewController, powerManager, mediaHost) { +) : BaseCommunalViewModel(communalInteractor, mediaHost) { private companion object { private const val KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle" diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 1c696851bb70..7a96fabd2433 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.communal.ui.viewmodel -import android.os.PowerManager import android.widget.RemoteViews import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor @@ -26,10 +25,8 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.controls.ui.MediaHost import com.android.systemui.media.dagger.MediaModule -import com.android.systemui.shade.ShadeViewController import javax.inject.Inject import javax.inject.Named -import javax.inject.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job @@ -51,10 +48,8 @@ constructor( private val communalInteractor: CommunalInteractor, private val interactionHandler: WidgetInteractionHandler, tutorialInteractor: CommunalTutorialInteractor, - shadeViewController: Provider<ShadeViewController>, - powerManager: PowerManager, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, -) : BaseCommunalViewModel(communalInteractor, shadeViewController, powerManager, mediaHost) { +) : BaseCommunalViewModel(communalInteractor, mediaHost) { @OptIn(ExperimentalCoroutinesApi::class) override val communalContent: Flow<List<CommunalContentModel>> = tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode -> diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 699532cbfca3..7876a6f74293 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -122,11 +122,6 @@ object Flags { val NEW_UNLOCK_SWIPE_ANIMATION = releasedFlag("new_unlock_swipe_animation") val CHARGING_RIPPLE = resourceBooleanFlag(R.bool.flag_charging_ripple, "charging_ripple") - // TODO(b/254512281): Tracking Bug - @JvmField - val BOUNCER_USER_SWITCHER = - resourceBooleanFlag(R.bool.config_enableBouncerUserSwitcher, "bouncer_user_switcher") - // TODO(b/254512676): Tracking Bug @JvmField val LOCKSCREEN_CUSTOM_CLOCKS = @@ -353,12 +348,6 @@ object Flags { // TODO(b/254512673): Tracking Bug @JvmField val DREAM_MEDIA_TAP_TO_OPEN = unreleasedFlag("dream_media_tap_to_open") - // TODO(b/254513168): Tracking Bug - @JvmField val UMO_SURFACE_RIPPLE = releasedFlag("umo_surface_ripple") - - // TODO(b/261734857): Tracking Bug - @JvmField val UMO_TURBULENCE_NOISE = releasedFlag("umo_turbulence_noise") - // TODO(b/263272731): Tracking Bug val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE = releasedFlag("media_ttt_receiver_success_ripple") @@ -377,9 +366,6 @@ object Flags { // 1000 - dock val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging") - // TODO(b/254512758): Tracking Bug - @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag("rounded_box_ripple") - // TODO(b/273509374): Tracking Bug @JvmField val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt index ecf78d550a3f..b1a2297526ce 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt @@ -30,6 +30,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map @@ -82,10 +83,11 @@ constructor( */ val showIndicatorForDeviceEntry: Flow<Boolean> = combine(showIndicatorForPrimaryBouncer, showIndicatorForAlternateBouncer) { - showForPrimaryBouncer, - showForAlternateBouncer -> - showForPrimaryBouncer || showForAlternateBouncer - } + showForPrimaryBouncer, + showForAlternateBouncer -> + showForPrimaryBouncer || showForAlternateBouncer + } + .distinctUntilChanged() private fun shouldShowIndicatorForPrimaryBouncer(): Boolean { val sfpsEnabled: Boolean = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 52f2759fe63d..d7a2aa041ffc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -18,7 +18,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator 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.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.sample import com.android.wm.shell.animation.Interpolators import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine @@ -39,13 +41,18 @@ class FromAlternateBouncerTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val powerInteractor: PowerInteractor, ) : TransitionInteractor( fromState = KeyguardState.ALTERNATE_BOUNCER, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -65,7 +72,7 @@ constructor( .sample( combine( keyguardInteractor.primaryBouncerShowing, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, powerInteractor.isAwake, keyguardInteractor.isAodAvailable, ::toQuad @@ -102,20 +109,19 @@ constructor( private fun listenForAlternateBouncerToGone() { scope.launch { - keyguardInteractor.isKeyguardGoingAway - .sample(transitionInteractor.finishedKeyguardState, ::Pair) - .collect { (isKeyguardGoingAway, keyguardState) -> - if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) { - startTransitionTo(KeyguardState.GONE) - } + keyguardInteractor.isKeyguardGoingAway.sample(finishedKeyguardState, ::Pair).collect { + (isKeyguardGoingAway, keyguardState) -> + if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) { + startTransitionTo(KeyguardState.GONE) } + } } } private fun listenForAlternateBouncerToPrimaryBouncer() { scope.launch { keyguardInteractor.primaryBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isPrimaryBouncerShowing, startedKeyguardState) -> if ( isPrimaryBouncerShowing && @@ -139,6 +145,7 @@ constructor( } companion object { + const val TAG = "FromAlternateBouncerTransitionInteractor" val TRANSITION_DURATION_MS = 300.milliseconds val TO_GONE_DURATION = 500.milliseconds val TO_AOD_DURATION = TRANSITION_DURATION_MS diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 858440185568..fedd63be1454 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators 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.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock import com.android.systemui.keyguard.shared.model.DozeStateModel @@ -29,6 +30,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -38,12 +40,17 @@ class FromAodTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, ) : TransitionInteractor( fromState = KeyguardState.AOD, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -58,7 +65,7 @@ constructor( .dozeTransitionTo(DozeStateModel.FINISH) .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isKeyguardOccluded, ::Pair ), @@ -77,7 +84,6 @@ constructor( } else { TransitionModeOnCanceled.LAST_VALUE } - startTransitionTo( toState = toState, modeOnCanceled = modeOnCanceled, @@ -89,16 +95,13 @@ constructor( private fun listenForAodToGone() { scope.launch { - keyguardInteractor.biometricUnlockState - .sample(transitionInteractor.finishedKeyguardState, ::Pair) - .collect { pair -> - val (biometricUnlockState, keyguardState) = pair - if ( - keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState) - ) { - startTransitionTo(KeyguardState.GONE) - } + keyguardInteractor.biometricUnlockState.sample(finishedKeyguardState, ::Pair).collect { + pair -> + val (biometricUnlockState, keyguardState) = pair + if (keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)) { + startTransitionTo(KeyguardState.GONE) } + } } } override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { @@ -113,6 +116,7 @@ constructor( } companion object { + const val TAG = "FromAodTransitionInteractor" private val DEFAULT_DURATION = 500.milliseconds val TO_LOCKSCREEN_DURATION = 500.milliseconds val TO_GONE_DURATION = DEFAULT_DURATION diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index eca7088c079a..fcb7698c9bf5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators 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.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock import com.android.systemui.keyguard.shared.model.KeyguardState @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -37,13 +39,18 @@ class FromDozingTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val powerInteractor: PowerInteractor, ) : TransitionInteractor( fromState = KeyguardState.DOZING, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -57,7 +64,7 @@ constructor( powerInteractor.isAwake .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isKeyguardOccluded, ::Pair ), @@ -76,7 +83,7 @@ constructor( private fun listenForDozingToGone() { scope.launch { keyguardInteractor.biometricUnlockState - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (biometricUnlockState, lastStartedTransition) -> if ( lastStartedTransition.to == KeyguardState.DOZING && @@ -96,6 +103,7 @@ constructor( } companion object { + const val TAG = "FromDozingTransitionInteractor" private val DEFAULT_DURATION = 500.milliseconds val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt index dac6ef5e6082..a6cdaa8c6761 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators 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.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.DozeStateModel @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine @@ -39,12 +41,17 @@ class FromDreamingLockscreenHostedTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, ) : TransitionInteractor( fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -64,7 +71,7 @@ constructor( .sample( combine( keyguardInteractor.dozeTransitionModel, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair ), ::toTriple @@ -88,7 +95,7 @@ constructor( .sample( combine( keyguardInteractor.isKeyguardOccluded, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair, ), ::toTriple @@ -108,7 +115,7 @@ constructor( private fun listenForDreamingLockscreenHostedToPrimaryBouncer() { scope.launch { keyguardInteractor.primaryBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isBouncerShowing, lastStartedTransitionStep) -> if ( isBouncerShowing && @@ -123,7 +130,7 @@ constructor( private fun listenForDreamingLockscreenHostedToGone() { scope.launch { keyguardInteractor.biometricUnlockState - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (biometricUnlockState, lastStartedTransitionStep) -> if ( lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED && @@ -137,11 +144,7 @@ constructor( private fun listenForDreamingLockscreenHostedToDozing() { scope.launch { - combine( - keyguardInteractor.dozeTransitionModel, - transitionInteractor.startedKeyguardTransitionStep, - ::Pair - ) + combine(keyguardInteractor.dozeTransitionModel, startedKeyguardTransitionStep, ::Pair) .collect { (dozeTransitionModel, lastStartedTransitionStep) -> if ( dozeTransitionModel.to == DozeStateModel.DOZE && diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 7fdcf2f09bc1..13ffd6396cda 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators 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.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.DozeStateModel @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -37,12 +39,17 @@ class FromDreamingTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, ) : TransitionInteractor( fromState = KeyguardState.DREAMING, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -66,7 +73,7 @@ constructor( private fun listenForDreamingToOccluded() { scope.launch { combine(keyguardInteractor.isKeyguardOccluded, keyguardInteractor.isDreaming, ::Pair) - .sample(transitionInteractor.startedKeyguardTransitionStep, ::toTriple) + .sample(startedKeyguardTransitionStep, ::toTriple) .collect { (isOccluded, isDreaming, lastStartedTransition) -> if ( isOccluded && @@ -82,7 +89,7 @@ constructor( private fun listenForDreamingToGone() { scope.launch { keyguardInteractor.biometricUnlockState - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (biometricUnlockState, lastStartedTransitionStep) -> if ( lastStartedTransitionStep.to == KeyguardState.DREAMING && @@ -96,11 +103,7 @@ constructor( private fun listenForDreamingToAodOrDozing() { scope.launch { - combine( - keyguardInteractor.dozeTransitionModel, - transitionInteractor.finishedKeyguardState, - ::Pair - ) + combine(keyguardInteractor.dozeTransitionModel, finishedKeyguardState, ::Pair) .collect { (dozeTransitionModel, keyguardState) -> if (keyguardState == KeyguardState.DREAMING) { if (dozeTransitionModel.to == DozeStateModel.DOZE) { @@ -123,6 +126,7 @@ constructor( } companion object { + const val TAG = "FromDreamingTransitionInteractor" private val DEFAULT_DURATION = 500.milliseconds val TO_LOCKSCREEN_DURATION = 1167.milliseconds } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index 70c2e6d56ca3..19fd7f9168e7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -20,18 +20,29 @@ import android.animation.ValueAnimator import com.android.app.animation.Interpolators import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher @SysUISingleton class FromGlanceableHubTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, -) : TransitionInteractor(fromState = KeyguardState.GLANCEABLE_HUB) { + transitionInteractor: KeyguardTransitionInteractor, + @Main mainDispatcher: CoroutineDispatcher, + @Background bgDispatcher: CoroutineDispatcher, +) : + TransitionInteractor( + fromState = KeyguardState.GLANCEABLE_HUB, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, + ) { override fun start() { if (!Flags.communalHub()) { return diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index 62a0b0ebc08c..742790eeaedb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators 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.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -37,13 +39,18 @@ class FromGoneTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val powerInteractor: PowerInteractor, ) : TransitionInteractor( fromState = KeyguardState.GONE, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -57,7 +64,7 @@ constructor( private fun listenForGoneToLockscreen() { scope.launch { keyguardInteractor.isKeyguardShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isKeyguardShowing, lastStartedStep) -> if (isKeyguardShowing && lastStartedStep.to == KeyguardState.GONE) { startTransitionTo(KeyguardState.LOCKSCREEN) @@ -69,7 +76,7 @@ constructor( private fun listenForGoneToDreamingLockscreenHosted() { scope.launch { keyguardInteractor.isActiveDreamLockscreenHosted - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isActiveDreamLockscreenHosted, lastStartedStep) -> if (isActiveDreamLockscreenHosted && lastStartedStep.to == KeyguardState.GONE) { startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) @@ -83,7 +90,7 @@ constructor( keyguardInteractor.isAbleToDream .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair ), @@ -106,7 +113,7 @@ constructor( powerInteractor.isAsleep .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isAodAvailable, ::Pair ), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index cecc6537e16e..2d0baa8be1b4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -18,9 +18,9 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch 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.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository @@ -39,20 +39,25 @@ import dagger.Lazy import java.util.UUID import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext @SysUISingleton class FromLockscreenTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val flags: FeatureFlags, private val shadeRepository: ShadeRepository, @@ -61,6 +66,9 @@ constructor( ) : TransitionInteractor( fromState = KeyguardState.LOCKSCREEN, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -147,12 +155,12 @@ constructor( private fun listenForLockscreenToDreaming() { val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING) - scope.launch("$TAG#listenForLockscreenToDreaming") { + scope.launch { keyguardInteractor.isAbleToDream .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, - transitionInteractor.finishedKeyguardState, + startedKeyguardTransitionStep, + finishedKeyguardState, keyguardInteractor.isActiveDreamLockscreenHosted, ::Triple ), @@ -180,9 +188,9 @@ constructor( } private fun listenForLockscreenToPrimaryBouncer() { - scope.launch("$TAG#listenForLockscreenToPrimaryBouncer") { + scope.launch { keyguardInteractor.primaryBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (isBouncerShowing, lastStartedTransitionStep) = pair if ( @@ -195,9 +203,9 @@ constructor( } private fun listenForLockscreenToAlternateBouncer() { - scope.launch("$TAG#listenForLockscreenToAlternateBouncer") { + scope.launch { keyguardInteractor.alternateBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (isAlternateBouncerShowing, lastStartedTransitionStep) = pair if ( @@ -213,11 +221,11 @@ constructor( /* Starts transitions when manually dragging up the bouncer from the lockscreen. */ private fun listenForLockscreenToPrimaryBouncerDragging() { var transitionId: UUID? = null - scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") { + scope.launch { shadeRepository.legacyShadeExpansion .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.statusBarState, keyguardInteractor.isKeyguardUnlocked, ::Triple @@ -225,72 +233,74 @@ constructor( ::toQuad ) .collect { (shadeExpansion, keyguardState, statusBarState, isKeyguardUnlocked) -> - val id = transitionId - if (id != null) { - if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) { - // An existing `id` means a transition is started, and calls to - // `updateTransition` will control it until FINISHED or CANCELED - var nextState = - if (shadeExpansion == 0f) { - TransitionState.FINISHED - } else if (shadeExpansion == 1f) { - TransitionState.CANCELED - } else { - TransitionState.RUNNING + withContext(mainDispatcher) { + val id = transitionId + if (id != null) { + if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) { + // An existing `id` means a transition is started, and calls to + // `updateTransition` will control it until FINISHED or CANCELED + var nextState = + if (shadeExpansion == 0f) { + TransitionState.FINISHED + } else if (shadeExpansion == 1f) { + TransitionState.CANCELED + } else { + TransitionState.RUNNING + } + transitionRepository.updateTransition( + id, + 1f - shadeExpansion, + nextState, + ) + + if ( + nextState == TransitionState.CANCELED || + nextState == TransitionState.FINISHED + ) { + transitionId = null } - transitionRepository.updateTransition( - id, - 1f - shadeExpansion, - nextState, - ) + // If canceled, just put the state back + // TODO(b/278086361): This logic should happen in + // FromPrimaryBouncerInteractor. + if (nextState == TransitionState.CANCELED) { + transitionRepository.startTransition( + TransitionInfo( + ownerName = name, + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.LOCKSCREEN, + animator = + getDefaultAnimatorForTransitionsToState( + KeyguardState.LOCKSCREEN + ) + .apply { duration = 0 } + ) + ) + } + } + } else { + // TODO (b/251849525): Remove statusbarstate check when that state is + // integrated into KeyguardTransitionRepository if ( - nextState == TransitionState.CANCELED || - nextState == TransitionState.FINISHED + keyguardState.to == KeyguardState.LOCKSCREEN && + shadeRepository.legacyShadeTracking.value && + !isKeyguardUnlocked && + statusBarState == KEYGUARD ) { - transitionId = null - } - - // If canceled, just put the state back - // TODO(b/278086361): This logic should happen in - // FromPrimaryBouncerInteractor. - if (nextState == TransitionState.CANCELED) { - transitionRepository.startTransition( - TransitionInfo( - ownerName = name, - from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.LOCKSCREEN, - animator = - getDefaultAnimatorForTransitionsToState( - KeyguardState.LOCKSCREEN - ) - .apply { duration = 0 } + transitionId = + startTransitionTo( + toState = KeyguardState.PRIMARY_BOUNCER, + animator = null, // transition will be manually controlled ) - ) } } - } else { - // TODO (b/251849525): Remove statusbarstate check when that state is - // integrated into KeyguardTransitionRepository - if ( - keyguardState.to == KeyguardState.LOCKSCREEN && - shadeRepository.legacyShadeTracking.value && - !isKeyguardUnlocked && - statusBarState == KEYGUARD - ) { - transitionId = - startTransitionTo( - toState = KeyguardState.PRIMARY_BOUNCER, - animator = null, // transition will be manually controlled - ) - } } } } } fun dismissKeyguard() { - startTransitionTo(KeyguardState.GONE) + scope.launch { startTransitionTo(KeyguardState.GONE) } } private fun listenForLockscreenToGone() { @@ -298,9 +308,9 @@ constructor( return } - scope.launch("$TAG#listenForLockscreenToGone") { + scope.launch { keyguardInteractor.isKeyguardGoingAway - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (isKeyguardGoingAway, lastStartedStep) = pair if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) { @@ -315,9 +325,9 @@ constructor( return } - scope.launch("$TAG#listenForLockscreenToGoneDragging") { + scope.launch { keyguardInteractor.isKeyguardGoingAway - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (isKeyguardGoingAway, lastStartedStep) = pair if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) { @@ -328,23 +338,22 @@ constructor( } private fun listenForLockscreenToOccluded() { - scope.launch("$TAG#listenForLockscreenToOccluded") { - keyguardInteractor.isKeyguardOccluded - .sample(transitionInteractor.startedKeyguardState, ::Pair) - .collect { (isOccluded, keyguardState) -> - if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) { - startTransitionTo(KeyguardState.OCCLUDED) - } + scope.launch { + keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect { + (isOccluded, keyguardState) -> + if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) { + startTransitionTo(KeyguardState.OCCLUDED) } + } } } private fun listenForLockscreenToAodOrDozing() { - scope.launch("$TAG#listenForLockscreenToAodOrDozing") { + scope.launch { powerInteractor.isAsleep .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isAodAvailable, ::Pair ), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt index 6a8555cb7f6b..40061f410456 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators 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.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor @@ -27,6 +28,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -36,13 +38,18 @@ class FromOccludedTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val powerInteractor: PowerInteractor, ) : TransitionInteractor( fromState = KeyguardState.OCCLUDED, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -57,7 +64,7 @@ constructor( private fun listenForOccludedToPrimaryBouncer() { scope.launch { keyguardInteractor.primaryBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isBouncerShowing, lastStartedTransitionStep) -> if ( isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.OCCLUDED @@ -70,13 +77,12 @@ constructor( private fun listenForOccludedToDreaming() { scope.launch { - keyguardInteractor.isAbleToDream - .sample(transitionInteractor.finishedKeyguardState, ::Pair) - .collect { (isAbleToDream, keyguardState) -> - if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) { - startTransitionTo(KeyguardState.DREAMING) - } + keyguardInteractor.isAbleToDream.sample(finishedKeyguardState, ::Pair).collect { + (isAbleToDream, keyguardState) -> + if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) { + startTransitionTo(KeyguardState.DREAMING) } + } } } @@ -86,7 +92,7 @@ constructor( .sample( combine( keyguardInteractor.isKeyguardShowing, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair ), ::toTriple @@ -111,7 +117,7 @@ constructor( .sample( combine( keyguardInteractor.isKeyguardShowing, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair ), ::toTriple @@ -135,7 +141,7 @@ constructor( powerInteractor.isAsleep .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isAodAvailable, ::Pair ), @@ -154,7 +160,7 @@ constructor( private fun listenForOccludedToAlternateBouncer() { scope.launch { keyguardInteractor.alternateBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isAlternateBouncerShowing, lastStartedTransitionStep) -> if ( isAlternateBouncerShowing && @@ -183,6 +189,7 @@ constructor( } companion object { + const val TAG = "FromOccludedTransitionInteractor" private val DEFAULT_DURATION = 500.milliseconds val TO_LOCKSCREEN_DURATION = 933.milliseconds val TO_AOD_DURATION = DEFAULT_DURATION diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index 5f246e181c26..c62055f83517 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.keyguard.KeyguardSecurityModel 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.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository @@ -35,6 +36,7 @@ import com.android.systemui.util.kotlin.sample import com.android.wm.shell.animation.Interpolators import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -47,8 +49,10 @@ class FromPrimaryBouncerTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val flags: FeatureFlags, private val keyguardSecurityModel: KeyguardSecurityModel, @@ -57,6 +61,9 @@ constructor( ) : TransitionInteractor( fromState = KeyguardState.PRIMARY_BOUNCER, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -115,7 +122,7 @@ constructor( .distinctUntilChanged() fun dismissPrimaryBouncer() { - startTransitionTo(KeyguardState.GONE) + scope.launch { startTransitionTo(KeyguardState.GONE) } } private fun listenForPrimaryBouncerToLockscreenOrOccluded() { @@ -124,7 +131,7 @@ constructor( .sample( combine( powerInteractor.isAwake, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isKeyguardOccluded, keyguardInteractor.isActiveDreamLockscreenHosted, ::toQuad @@ -158,7 +165,7 @@ constructor( .sample( combine( powerInteractor.isAsleep, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isAodAvailable, ::Triple ), @@ -185,7 +192,7 @@ constructor( .sample( combine( keyguardInteractor.isActiveDreamLockscreenHosted, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair ), ::toTriple @@ -213,7 +220,7 @@ constructor( scope.launch { keyguardInteractor.isKeyguardGoingAway - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isKeyguardGoingAway, lastStartedTransitionStep) -> if ( isKeyguardGoingAway && diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index d5ac2838a2f1..5c2df4581ff0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -24,8 +24,11 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled import com.android.systemui.util.kotlin.sample import java.util.UUID +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** * Each TransitionInteractor is responsible for determining under which conditions to notify @@ -40,14 +43,25 @@ import kotlinx.coroutines.launch */ sealed class TransitionInteractor( val fromState: KeyguardState, + val transitionInteractor: KeyguardTransitionInteractor, + val mainDispatcher: CoroutineDispatcher, + val bgDispatcher: CoroutineDispatcher, ) { val name = this::class.simpleName ?: "UnknownTransitionInteractor" - abstract val transitionRepository: KeyguardTransitionRepository - abstract val transitionInteractor: KeyguardTransitionInteractor abstract fun start() - fun startTransitionTo( + /* Use background dispatcher for all [KeyguardTransitionInteractor] flows. Necessary because + * the [sample] utility internally runs a collect on the Unconfined dispatcher, resulting + * in continuations on the main thread. We don't want that for classes that inherit from this. + */ + val startedKeyguardTransitionStep = + transitionInteractor.startedKeyguardTransitionStep.flowOn(bgDispatcher) + // The following are MutableSharedFlows, and do not require flowOn + val startedKeyguardState = transitionInteractor.startedKeyguardState + val finishedKeyguardState = transitionInteractor.finishedKeyguardState + + suspend fun startTransitionTo( toState: KeyguardState, animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState), modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE @@ -67,16 +81,17 @@ sealed class TransitionInteractor( ) return null } - - return transitionRepository.startTransition( - TransitionInfo( - name, - fromState, - toState, - animator, - modeOnCanceled, + return withContext(mainDispatcher) { + transitionRepository.startTransition( + TransitionInfo( + name, + fromState, + toState, + animator, + modeOnCanceled, + ) ) - ) + } } /** This signal may come in before the occlusion signal, and can provide a custom transition */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt index 9b404338b9e5..d118d4d11948 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt @@ -17,12 +17,14 @@ package com.android.systemui.keyguard.ui.view.layout.blueprints +import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.KeyguardBlueprint import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection +import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntrySection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection @@ -31,7 +33,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSec import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule -import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines +import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection import com.android.systemui.util.kotlin.getOrNull import java.util.Optional import javax.inject.Inject @@ -44,18 +46,20 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi class ShortcutsBesideUdfpsKeyguardBlueprint @Inject constructor( + alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection, defaultIndicationAreaSection: DefaultIndicationAreaSection, defaultDeviceEntrySection: DefaultDeviceEntrySection, @Named(KeyguardSectionsModule.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION) defaultAmbientIndicationAreaSection: Optional<KeyguardSection>, defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection, - alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection, defaultStatusViewSection: DefaultStatusViewSection, defaultStatusBarSection: DefaultStatusBarSection, - splitShadeGuidelines: SplitShadeGuidelines, defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection, aodNotificationIconsSection: AodNotificationIconsSection, aodBurnInSection: AodBurnInSection, + communalTutorialIndicatorSection: CommunalTutorialIndicatorSection, + clockSection: ClockSection, + smartspaceSection: SmartspaceSection, udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection, ) : KeyguardBlueprint { override val id: String = SHORTCUTS_BESIDE_UDFPS @@ -63,15 +67,17 @@ constructor( override val sections = listOfNotNull( defaultIndicationAreaSection, + alignShortcutsToUdfpsSection, defaultAmbientIndicationAreaSection.getOrNull(), defaultSettingsPopupMenuSection, - alignShortcutsToUdfpsSection, defaultStatusViewSection, defaultStatusBarSection, defaultNotificationStackScrollLayoutSection, - splitShadeGuidelines, aodNotificationIconsSection, + smartspaceSection, aodBurnInSection, + communalTutorialIndicatorSection, + clockSection, defaultDeviceEntrySection, udfpsAccessibilityOverlaySection, // Add LAST: Intentionally has z-order above others ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt index fe0b3656c3d7..310ec95a22df 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt @@ -20,7 +20,7 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.Context import com.android.settingslib.Utils import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.common.ui.data.repository.ConfigurationRepository +import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState @@ -41,7 +41,7 @@ class DeviceEntryForegroundViewModel @Inject constructor( val context: Context, - configurationRepository: ConfigurationRepository, // TODO (b/309655554): create & use interactor + configurationInteractor: ConfigurationInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, transitionInteractor: KeyguardTransitionInteractor, deviceEntryIconViewModel: DeviceEntryIconViewModel, @@ -62,7 +62,7 @@ constructor( private val color: Flow<Int> = deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBgProtection -> - configurationRepository.onAnyConfigurationChange + configurationInteractor.onAnyConfigurationChange .map { getColor(useBgProtection) } .onStart { emit(getColor(useBgProtection)) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index 2551da8e7795..5720cc74002b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -87,8 +87,6 @@ import com.android.systemui.bluetooth.BroadcastDialogController; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.media.controls.models.GutsViewHolder; import com.android.systemui.media.controls.models.player.MediaAction; import com.android.systemui.media.controls.models.player.MediaButton; @@ -247,7 +245,6 @@ public class MediaControlPanel { private String mCurrentBroadcastApp; private MultiRippleController mMultiRippleController; private TurbulenceNoiseController mTurbulenceNoiseController; - private final FeatureFlags mFeatureFlags; private final GlobalSettings mGlobalSettings; private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig; @@ -281,7 +278,6 @@ public class MediaControlPanel { ActivityIntentHelper activityIntentHelper, NotificationLockscreenUserManager lockscreenUserManager, BroadcastDialogController broadcastDialogController, - FeatureFlags featureFlags, GlobalSettings globalSettings, MediaFlags mediaFlags ) { @@ -312,8 +308,6 @@ public class MediaControlPanel { return Unit.INSTANCE; }); - mFeatureFlags = featureFlags; - mGlobalSettings = globalSettings; updateAnimatorDurationScale(); } @@ -1187,9 +1181,7 @@ public class MediaControlPanel { action.run(); - if (mFeatureFlags.isEnabled(Flags.UMO_SURFACE_RIPPLE)) { - mMultiRippleController.play(createTouchRippleAnimation(button)); - } + mMultiRippleController.play(createTouchRippleAnimation(button)); if (icon instanceof Animatable) { ((Animatable) icon).start(); @@ -1228,8 +1220,7 @@ public class MediaControlPanel { } private boolean shouldPlayTurbulenceNoise() { - return mFeatureFlags.isEnabled(Flags.UMO_TURBULENCE_NOISE) && mButtonClicked && !mWasPlaying - && isPlaying(); + return mButtonClicked && !mWasPlaying && isPlaying(); } private TurbulenceNoiseAnimationConfig createTurbulenceNoiseAnimation() { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt index 0385aeba32b7..523414cfddbf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt @@ -1153,7 +1153,7 @@ constructor( qsExpansion > 0.4f && onLockscreen -> LOCATION_QS onLockscreen && isSplitShadeExpanding() -> LOCATION_QS onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS - // TODO(b/308813166): revisit logic once interactions between the hub and + // TODO(b/311234666): revisit logic once interactions between the hub and // shade/keyguard state are finalized isCommunalShowing && communalInteractor.isCommunalEnabled -> LOCATION_COMMUNAL_HUB onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt index fc06090750ec..fe10eaaec793 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt @@ -18,6 +18,8 @@ package com.android.systemui.qs.tiles.base.actions import android.app.PendingIntent import android.content.Intent +import android.content.pm.PackageManager +import android.os.UserHandle import android.view.View import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.ActivityLaunchAnimator @@ -32,13 +34,23 @@ import javax.inject.Inject interface QSTileIntentUserInputHandler { fun handle(view: View?, intent: Intent) - fun handle(view: View?, pendingIntent: PendingIntent) + + /** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */ + fun handle( + view: View?, + pendingIntent: PendingIntent, + requestLaunchingDefaultActivity: Boolean = false + ) } @SysUISingleton class QSTileIntentUserInputHandlerImpl @Inject -constructor(private val activityStarter: ActivityStarter) : QSTileIntentUserInputHandler { +constructor( + private val activityStarter: ActivityStarter, + private val packageManager: PackageManager, + private val userHandle: UserHandle, +) : QSTileIntentUserInputHandler { override fun handle(view: View?, intent: Intent) { val animationController: ActivityLaunchAnimator.Controller? = @@ -52,21 +64,41 @@ constructor(private val activityStarter: ActivityStarter) : QSTileIntentUserInpu } // TODO(b/249804373): make sure to allow showing activities over the lockscreen. See b/292112939 - override fun handle(view: View?, pendingIntent: PendingIntent) { - if (!pendingIntent.isActivity) { - return - } - val animationController: ActivityLaunchAnimator.Controller? = - view?.let { - ActivityLaunchAnimator.Controller.fromView( - it, - InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE, + override fun handle( + view: View?, + pendingIntent: PendingIntent, + requestLaunchingDefaultActivity: Boolean + ) { + if (pendingIntent.isActivity) { + val animationController: ActivityLaunchAnimator.Controller? = + view?.let { + ActivityLaunchAnimator.Controller.fromView( + it, + InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE, + ) + } + activityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController) + } else if (requestLaunchingDefaultActivity) { + val intent = + Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_LAUNCHER) + .setPackage(pendingIntent.creatorPackage) + .addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + ) + val intents = + packageManager.queryIntentActivitiesAsUser( + intent, + PackageManager.ResolveInfoFlags.of(0L), + userHandle.identifier ) - } - activityStarter.startPendingIntentMaybeDismissingKeyguard( - pendingIntent, - null, - animationController - ) + intents + .firstOrNull { it.activityInfo.exported } + ?.let { resolved -> + intent.setPackage(null) + intent.setComponent(resolved.activityInfo.componentName) + handle(view, intent) + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt index afca57c75788..0ad520bd31ee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt @@ -18,9 +18,7 @@ package com.android.systemui.qs.tiles.impl.alarm.domain.interactor import android.content.Intent import android.provider.AlarmClock -import com.android.internal.jank.InteractionJankMonitor -import com.android.systemui.animation.ActivityLaunchAnimator -import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.interactor.QSTileInput import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel @@ -31,34 +29,20 @@ import javax.inject.Inject class AlarmTileUserActionInteractor @Inject constructor( - private val activityStarter: ActivityStarter, + private val inputHandler: QSTileIntentUserInputHandler, ) : QSTileUserActionInteractor<AlarmTileModel> { override suspend fun handleInput(input: QSTileInput<AlarmTileModel>): Unit = with(input) { when (action) { is QSTileUserAction.Click -> { - val animationController = - action.view?.let { - ActivityLaunchAnimator.Controller.fromView( - it, - InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE - ) - } if ( data is AlarmTileModel.NextAlarmSet && data.alarmClockInfo.showIntent != null ) { val pendingIndent = data.alarmClockInfo.showIntent - activityStarter.postStartActivityDismissingKeyguard( - pendingIndent, - animationController - ) + inputHandler.handle(action.view, pendingIndent, true) } else { - activityStarter.postStartActivityDismissingKeyguard( - Intent(AlarmClock.ACTION_SHOW_ALARMS), - 0, - animationController - ) + inputHandler.handle(action.view, Intent(AlarmClock.ACTION_SHOW_ALARMS)) } } is QSTileUserAction.LongClick -> {} diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index bd43307dba5a..7aa0dadba16c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -32,6 +32,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -143,6 +144,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList channel.enableVibration(true); mNotificationManager.createNotificationChannel(channel); + int currentUid = Process.myUid(); int currentUserId = mUserContextTracker.getUserContext().getUserId(); UserHandle currentUser = new UserHandle(currentUserId); switch (action) { @@ -166,7 +168,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList mRecorder = new ScreenMediaRecorder( mUserContextTracker.getUserContext(), mMainHandler, - currentUserId, + currentUid, mAudioSource, captureTarget, this diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 3aab3bf62809..a170d0dac100 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -83,7 +83,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { private Surface mInputSurface; private VirtualDisplay mVirtualDisplay; private MediaRecorder mMediaRecorder; - private int mUser; + private int mUid; private ScreenRecordingMuxer mMuxer; private ScreenInternalAudioRecorder mAudio; private ScreenRecordingAudioSource mAudioSource; @@ -94,12 +94,12 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { ScreenMediaRecorderListener mListener; public ScreenMediaRecorder(Context context, Handler handler, - int user, ScreenRecordingAudioSource audioSource, + int uid, ScreenRecordingAudioSource audioSource, MediaProjectionCaptureTarget captureRegion, ScreenMediaRecorderListener listener) { mContext = context; mHandler = handler; - mUser = user; + mUid = uid; mCaptureRegion = captureRegion; mListener = listener; mAudioSource = audioSource; @@ -111,7 +111,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { IMediaProjectionManager mediaService = IMediaProjectionManager.Stub.asInterface(b); IMediaProjection proj = null; - proj = mediaService.createProjection(mUser, mContext.getPackageName(), + proj = mediaService.createProjection(mUid, mContext.getPackageName(), MediaProjectionManager.TYPE_SCREEN_CAPTURE, false); IMediaProjection projection = IMediaProjection.Stub.asInterface(proj.asBinder()); if (mCaptureRegion != null) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 3be60b74af21..782d6519468c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -17,6 +17,8 @@ package com.android.systemui.shade import android.content.Context +import android.os.PowerManager +import android.os.SystemClock import android.view.GestureDetector import android.view.MotionEvent import android.view.View @@ -44,6 +46,7 @@ constructor( private val communalViewModel: CommunalViewModel, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val shadeInteractor: ShadeInteractor, + private val powerManager: PowerManager, ) { /** The container view for the hub. This will not be initialized until [initView] is called. */ private lateinit var communalContainerView: View @@ -157,7 +160,7 @@ constructor( // If the hub is fully visible, send all touch events to it. val communalVisible = hubShowing && !hubOccluded if (communalVisible) { - communalContainerView.dispatchTouchEvent(ev) + dispatchTouchEvent(ev) // Return true regardless of dispatch result as some touches at the start of a gesture // may return false from dispatchTouchEvent. return true @@ -175,7 +178,7 @@ constructor( x >= communalContainerView.width - edgeSwipeRegionWidth if (inOpeningSwipeRegion && !hubOccluded) { isTrackingOpenGesture = true - communalContainerView.dispatchTouchEvent(ev) + dispatchTouchEvent(ev) // Return true regardless of dispatch result as some touches at the start of a // gesture may return false from dispatchTouchEvent. return true @@ -184,7 +187,7 @@ constructor( if (isUp || isCancel) { isTrackingOpenGesture = false } - communalContainerView.dispatchTouchEvent(ev) + dispatchTouchEvent(ev) // Return true regardless of dispatch result as some touches at the start of a gesture // may return false from dispatchTouchEvent. return true @@ -192,4 +195,17 @@ constructor( return false } + + /** + * Dispatches the touch event to the communal container and sends a user activity event to reset + * the screen timeout. + */ + private fun dispatchTouchEvent(ev: MotionEvent) { + communalContainerView.dispatchTouchEvent(ev) + powerManager.userActivity( + SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_TOUCH, + 0 + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt index 7a340d2f0268..6407b5a4d16c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt @@ -25,8 +25,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository +import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository import com.android.systemui.user.domain.interactor.UserSwitcherInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -94,7 +94,7 @@ constructor( disableFlagsRepository.disableFlags, isShadeEnabled, keyguardRepository.isDozing, - userSetupRepository.isUserSetupFlow, + userSetupRepository.isUserSetUp, deviceProvisioningRepository.isDeviceProvisioned, ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned -> isDeviceProvisioned && diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 2438298f6a6e..7f8be1cc7e55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -22,6 +22,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver +import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.media.controls.ui.MediaHierarchyManager import com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll import com.android.systemui.plugins.ActivityStarter @@ -888,6 +889,9 @@ class DragDownHelper( isDraggingDown = false isTrackpadReverseScroll = false shadeRepository.setLegacyLockscreenShadeTracking(false) + if (KeyguardShadeMigrationNssl.isEnabled) { + return true + } } else { stopDragging() return false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt index 29d53fc15e8b..9f878b241d73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.data import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule +import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule import dagger.Module @@ -24,6 +25,7 @@ import dagger.Module includes = [ KeyguardStatusBarRepositoryModule::class, + RemoteInputRepositoryModule::class, StatusBarModeRepositoryModule::class, StatusBarPhoneDataLayerModule::class ] diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt new file mode 100644 index 000000000000..c0302bc348b6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt @@ -0,0 +1,60 @@ +/* + * 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.statusbar.data.repository + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.NotificationRemoteInputManager +import com.android.systemui.statusbar.RemoteInputController +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** + * Repository used for tracking the state of notification remote input (e.g. when the user presses + * "reply" on a notification and the keyboard opens). + */ +interface RemoteInputRepository { + /** Whether remote input is currently active for any notification. */ + val isRemoteInputActive: Flow<Boolean> +} + +@SysUISingleton +class RemoteInputRepositoryImpl +@Inject +constructor( + private val notificationRemoteInputManager: NotificationRemoteInputManager, +) : RemoteInputRepository { + override val isRemoteInputActive: Flow<Boolean> = conflatedCallbackFlow { + trySend(false) // initial value is false + val callback = + object : RemoteInputController.Callback { + override fun onRemoteInputActive(active: Boolean) { + trySend(active) + } + } + notificationRemoteInputManager.addControllerCallback(callback) + awaitClose { notificationRemoteInputManager.removeControllerCallback(callback) } + } +} + +@Module +interface RemoteInputRepositoryModule { + @Binds fun bindImpl(impl: RemoteInputRepositoryImpl): RemoteInputRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt new file mode 100644 index 000000000000..68f727b046c0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt @@ -0,0 +1,32 @@ +/* + * 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.statusbar.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.data.repository.RemoteInputRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** + * Interactor used for business logic pertaining to the notification remote input (e.g. when the + * user presses "reply" on a notification and the keyboard opens). + */ +@SysUISingleton +class RemoteInputInteractor @Inject constructor(remoteInputRepository: RemoteInputRepository) { + /** Is remote input currently active for a notification? */ + val isRemoteInputActive: Flow<Boolean> = remoteInputRepository.isRemoteInputActive +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 31ca106d2bc9..e200e65a9f4a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -273,12 +273,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private final RefactorFlag mInlineReplyAnimation = RefactorFlag.forView(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION); - private static final boolean mSimulateSlowMeasure = Compile.IS_DEBUG && RefactorFlag.forView( - Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE).isEnabled(); + private static boolean shouldSimulateSlowMeasure() { + return Compile.IS_DEBUG && RefactorFlag.forView( + Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE).isEnabled(); + } + private static final String SLOW_MEASURE_SIMULATE_DELAY_PROPERTY = "persist.notifications.extra_measure_delay_ms"; - private static final int SLOW_MEASURE_SIMULATE_DELAY_MS = mSimulateSlowMeasure ? - SystemProperties.getInt(SLOW_MEASURE_SIMULATE_DELAY_PROPERTY, 150) : 0; + private static final int SLOW_MEASURE_SIMULATE_DELAY_MS = + SystemProperties.getInt(SLOW_MEASURE_SIMULATE_DELAY_PROPERTY, 150); // Listener will be called when receiving a long click event. // Use #setLongPressPosition to optionally assign positional data with the long press. @@ -1886,7 +1889,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (Compile.IS_DEBUG && mSimulateSlowMeasure) { + if (shouldSimulateSlowMeasure()) { simulateExtraMeasureDelay(); } Trace.endSection(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index abc04b87f831..a30c29456b3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -69,6 +69,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; +import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.media.controls.ui.KeyguardMediaController; @@ -1957,18 +1958,34 @@ public class NotificationStackScrollLayoutController implements Dumpable { mView.dispatchDownEventToScroller(ev); } } - boolean scrollerWantsIt = false; - if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping() - && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) { - scrollerWantsIt = mView.onScrollTouch(ev); - } boolean horizontalSwipeWantsIt = false; - if (mLongPressedView == null && !mView.isBeingDragged() - && !expandingNotification - && !mView.getExpandedInThisMotion() - && !onlyScrollingInThisMotion - && !mView.getDisallowDismissInThisMotion()) { - horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + boolean scrollerWantsIt = false; + if (KeyguardShadeMigrationNssl.isEnabled()) { + // Reverse the order relative to the else statement. onScrollTouch will reset on an + // UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes. + if (mLongPressedView == null && !mView.isBeingDragged() + && !expandingNotification + && !mView.getExpandedInThisMotion() + && !onlyScrollingInThisMotion + && !mView.getDisallowDismissInThisMotion()) { + horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + } + if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping() + && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) { + scrollerWantsIt = mView.onScrollTouch(ev); + } + } else { + if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping() + && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) { + scrollerWantsIt = mView.onScrollTouch(ev); + } + if (mLongPressedView == null && !mView.isBeingDragged() + && !expandingNotification + && !mView.getExpandedInThisMotion() + && !onlyScrollingInThisMotion + && !mView.getDisallowDismissInThisMotion()) { + horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + } } // Check if we need to clear any snooze leavebehinds diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt index 89a2fb78635b..e309c32df64e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt @@ -34,8 +34,6 @@ import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistr import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigCoreStartable import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileRepositorySwitcher -import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository -import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepositoryImpl import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter @@ -62,6 +60,8 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRep import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryViaTrackerLib import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl +import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository +import com.android.systemui.statusbar.policy.data.repository.UserSetupRepositoryImpl import dagger.Binds import dagger.Module import dagger.Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt index 39135c70788d..d555c47f4da2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt @@ -32,9 +32,9 @@ import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository +import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository import com.android.systemui.util.CarrierConfigTracker import java.lang.ref.WeakReference import javax.inject.Inject @@ -105,7 +105,7 @@ interface MobileIconsInteractor { val isDefaultConnectionFailed: StateFlow<Boolean> /** True once the user has been set up */ - val isUserSetup: StateFlow<Boolean> + val isUserSetUp: StateFlow<Boolean> /** True if we're configured to force-hide the mobile icons and false otherwise. */ val isForceHidden: Flow<Boolean> @@ -362,7 +362,7 @@ constructor( ) .stateIn(scope, SharingStarted.WhileSubscribed(), false) - override val isUserSetup: StateFlow<Boolean> = userSetupRepo.isUserSetupFlow + override val isUserSetUp: StateFlow<Boolean> = userSetupRepo.isUserSetUp override val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepository.kt index 91886bb121d5..2a0812b8ec8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.pipeline.mobile.data.repository +package com.android.systemui.statusbar.policy.data.repository import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton @@ -34,15 +34,14 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext /** - * Repository to observe the state of [DeviceProvisionedController.isUserSetup]. This information - * can change some policy related to display + * Repository to observe whether the user has completed the setup steps. This information can change + * some policy related to display. */ interface UserSetupRepository { - /** Observable tracking [DeviceProvisionedController.isUserSetup] */ - val isUserSetupFlow: StateFlow<Boolean> + /** Whether the user has completed the setup steps. */ + val isUserSetUp: StateFlow<Boolean> } -@Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class UserSetupRepositoryImpl @@ -52,8 +51,7 @@ constructor( @Background private val bgDispatcher: CoroutineDispatcher, @Application scope: CoroutineScope, ) : UserSetupRepository { - /** State flow that tracks [DeviceProvisionedController.isUserSetup] */ - override val isUserSetupFlow: StateFlow<Boolean> = + override val isUserSetUp: StateFlow<Boolean> = conflatedCallbackFlow { val callback = object : DeviceProvisionedController.DeviceProvisionedListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/UserSetupInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/UserSetupInteractor.kt new file mode 100644 index 000000000000..ca36e392b563 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/UserSetupInteractor.kt @@ -0,0 +1,26 @@ +/* + * 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.statusbar.policy.domain.interactor + +import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +class UserSetupInteractor @Inject constructor(repository: UserSetupRepository) { + /** Whether the user has completed the setup steps. */ + val isUserSetUp: Flow<Boolean> = repository.isUserSetUp +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java b/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java deleted file mode 100644 index 8d8599900530..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2016 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.tuner; - -import android.os.Bundle; - -import androidx.preference.PreferenceFragment; - -import com.android.systemui.res.R; -import com.android.tools.r8.keepanno.annotations.KeepTarget; -import com.android.tools.r8.keepanno.annotations.UsesReflection; - -public class OtherPrefs extends PreferenceFragment { - // aapt doesn't generate keep rules for android:fragment references in <Preference> tags, so - // explicitly declare references per usage in `R.xml.other_settings`. See b/120445169. - @UsesReflection(@KeepTarget(classConstant = PowerNotificationControlsFragment.class)) - @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - addPreferencesFromResource(R.xml.other_settings); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java deleted file mode 100644 index ce1a2e9b329c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tuner/PowerNotificationControlsFragment.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) 2016, 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.tuner; - -import android.annotation.Nullable; -import android.app.Fragment; -import android.os.Bundle; -import android.provider.Settings; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Switch; -import android.widget.TextView; - -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.res.R; - -public class PowerNotificationControlsFragment extends Fragment { - - private static final String KEY_SHOW_PNC = "show_importance_slider"; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - return inflater.inflate(R.layout.power_notification_controls_settings, container, false); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - final View switchBar = view.findViewById(R.id.switch_bar); - final Switch switchWidget = (Switch) switchBar.findViewById(android.R.id.switch_widget); - final TextView switchText = (TextView) switchBar.findViewById(R.id.switch_text); - switchWidget.setChecked(isEnabled()); - switchText.setText(isEnabled() - ? getString(R.string.switch_bar_on) - : getString(R.string.switch_bar_off)); - - switchWidget.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean newState = !isEnabled(); - MetricsLogger.action(getContext(), - MetricsEvent.ACTION_TUNER_POWER_NOTIFICATION_CONTROLS, newState); - Settings.Secure.putInt(getContext().getContentResolver(), - KEY_SHOW_PNC, newState ? 1 : 0); - switchWidget.setChecked(newState); - switchText.setText(newState - ? getString(R.string.switch_bar_on) - : getString(R.string.switch_bar_off)); - } - }); - } - - @Override - public void onResume() { - super.onResume(); - MetricsLogger.visibility( - getContext(), MetricsEvent.TUNER_POWER_NOTIFICATION_CONTROLS, true); - } - - @Override - public void onPause() { - super.onPause(); - MetricsLogger.visibility( - getContext(), MetricsEvent.TUNER_POWER_NOTIFICATION_CONTROLS, false); - } - - private boolean isEnabled() { - int setting = Settings.Secure.getInt(getContext().getContentResolver(), KEY_SHOW_PNC, 0); - return setting == 1; - } - -} 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 c4df27c2ccb2..cb8c40c333b3 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.coroutines.collectValues import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository @@ -69,6 +70,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { private val bouncerRepository = FakeKeyguardBouncerRepository() private val biometricSettingsRepository = FakeBiometricSettingsRepository() + private val deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository() private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @@ -112,7 +114,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { DeviceEntrySideFpsOverlayInteractor( testScope.backgroundScope, mContext, - FakeDeviceEntryFingerprintAuthRepository(), + deviceEntryFingerprintAuthRepository, primaryBouncerInteractor, alternateBouncerInteractor, keyguardUpdateMonitor @@ -216,6 +218,30 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { assertThat(showIndicatorForDeviceEntry).isEqualTo(false) } + @Test + fun ignoresDuplicateRequestsToShowIndicatorForDeviceEntry() = + testScope.runTest { + val showIndicatorForDeviceEntry by collectValues(underTest.showIndicatorForDeviceEntry) + runCurrent() + + // Request to show indicator for primary bouncer showing + updatePrimaryBouncer( + isShowing = true, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + + // Another request to show indicator for deviceEntryFingerprintAuthRepository update + deviceEntryFingerprintAuthRepository.setShouldUpdateIndicatorVisibility(true) + + // Request to show indicator for alternate bouncer showing + bouncerRepository.setAlternateVisible(true) + + // Ensure only one show request is sent + assertThat(showIndicatorForDeviceEntry).containsExactly(false, true) + } + private fun updatePrimaryBouncer( isShowing: Boolean, isAnimatingAway: Boolean, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt index 8e81185d6dcf..809947d2fec7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt @@ -63,6 +63,8 @@ class FromPrimaryBouncerTransitionInteractorTest : KeyguardTransitionInteractorT transitionRepository = super.transitionRepository, transitionInteractor = super.transitionInteractor, scope = super.testScope.backgroundScope, + bgDispatcher = super.testDispatcher, + mainDispatcher = super.testDispatcher, keyguardInteractor = super.keyguardInteractor, flags = FakeFeatureFlags(), keyguardSecurityModel = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index b8a8bdf06954..e531d44d5fdd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -20,7 +20,6 @@ import android.app.StatusBarManager import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN -import com.android.keyguard.TestScopeProvider import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.flags.FakeFeatureFlags @@ -51,6 +50,7 @@ import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runCurrent @@ -109,7 +109,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - testScope = TestScopeProvider.getTestScope() + val testDispatcher = StandardTestDispatcher() + testScope = TestScope(testDispatcher) keyguardRepository = FakeKeyguardRepository() bouncerRepository = FakeKeyguardBouncerRepository() @@ -139,6 +140,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -160,6 +163,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -173,6 +178,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromDreamingTransitionInteractor = FromDreamingTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -182,6 +189,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromDreamingLockscreenHostedTransitionInteractor = FromDreamingLockscreenHostedTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -191,6 +200,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromAodTransitionInteractor = FromAodTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -200,6 +211,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromGoneTransitionInteractor = FromGoneTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -210,6 +223,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromDozingTransitionInteractor = FromDozingTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -220,6 +235,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromOccludedTransitionInteractor = FromOccludedTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -230,6 +247,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromAlternateBouncerTransitionInteractor = FromAlternateBouncerTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt index 2f35380e562b..59965022d7cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt @@ -64,8 +64,6 @@ import com.android.systemui.ActivityIntentHelper import com.android.systemui.SysuiTestCase import com.android.systemui.bluetooth.BroadcastDialogController import com.android.systemui.broadcast.BroadcastSender -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.media.controls.MediaTestUtils import com.android.systemui.media.controls.models.GutsViewHolder import com.android.systemui.media.controls.models.player.MediaAction @@ -227,11 +225,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var recProgressBar2: SeekBar @Mock private lateinit var recProgressBar3: SeekBar private var shouldShowBroadcastButton: Boolean = false - private val fakeFeatureFlag = - FakeFeatureFlags().apply { - this.set(Flags.UMO_SURFACE_RIPPLE, false) - this.set(Flags.UMO_TURBULENCE_NOISE, false) - } @Mock private lateinit var globalSettings: GlobalSettings @Mock private lateinit var mediaFlags: MediaFlags @@ -275,7 +268,6 @@ public class MediaControlPanelTest : SysuiTestCase() { activityIntentHelper, lockscreenUserManager, broadcastDialogController, - fakeFeatureFlag, globalSettings, mediaFlags, ) { @@ -2397,8 +2389,7 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test - fun onButtonClick_touchRippleFlagEnabled_playsTouchRipple() { - fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true) + fun onButtonClick_playsTouchRipple() { val semanticActions = MediaButton( playOrPause = @@ -2419,31 +2410,7 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test - fun onButtonClick_touchRippleFlagDisabled_doesNotPlayTouchRipple() { - fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, false) - val semanticActions = - MediaButton( - playOrPause = - MediaAction( - icon = null, - action = {}, - contentDescription = "play", - background = null - ) - ) - val data = mediaData.copy(semanticActions = semanticActions) - player.attachPlayer(viewHolder) - player.bindPlayer(data, KEY) - - viewHolder.actionPlayPause.callOnClick() - - assertThat(viewHolder.multiRippleView.ripples.size).isEqualTo(0) - } - - @Test fun playTurbulenceNoise_finishesAfterDuration() { - fakeFeatureFlag.set(Flags.UMO_TURBULENCE_NOISE, true) - val semanticActions = MediaButton( playOrPause = @@ -2474,8 +2441,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun playTurbulenceNoise_whenPlaybackStateIsNotPlaying_doesNotPlayTurbulence() { - fakeFeatureFlag.set(Flags.UMO_TURBULENCE_NOISE, true) - val semanticActions = MediaButton( custom0 = diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 5569ca9520e9..b7a9ea751438 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade +import android.os.PowerManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.ViewUtils @@ -43,6 +44,8 @@ import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -52,6 +55,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Mock private lateinit var communalViewModel: CommunalViewModel @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock private lateinit var shadeInteractor: ShadeInteractor + @Mock private lateinit var powerManager: PowerManager private lateinit var containerView: View private lateinit var testableLooper: TestableLooper @@ -76,7 +80,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { communalInteractor, communalViewModel, keyguardTransitionInteractor, - shadeInteractor + shadeInteractor, + powerManager ) testableLooper = TestableLooper.get(this) @@ -90,14 +95,14 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun isEnabled_interactorEnabled_returnsTrue() { + fun isEnabled_interactorEnabled_interceptsTouches() { communalRepository.setIsCommunalEnabled(true) assertThat(underTest.isEnabled()).isTrue() } @Test - fun isEnabled_interactorDisabled_returnsFalse() { + fun isEnabled_interactorDisabled_doesNotIntercept() { communalRepository.setIsCommunalEnabled(false) assertThat(underTest.isEnabled()).isFalse() @@ -120,7 +125,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun onTouchEvent_touchInsideGestureRegion_returnsTrue() { + fun onTouchEvent_touchInsideGestureRegion_interceptsTouches() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -131,7 +136,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun onTouchEvent_subsequentTouchesAfterGestureStart_returnsTrue() { + fun onTouchEvent_subsequentTouchesAfterGestureStart_interceptsTouches() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -146,7 +151,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun onTouchEvent_communalOpen_returnsTrue() { + fun onTouchEvent_communalOpen_interceptsTouches() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -155,10 +160,12 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { // Touch events are intercepted. assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue() + // User activity sent to PowerManager. + verify(powerManager).userActivity(any(), any(), any()) } @Test - fun onTouchEvent_communalAndBouncerShowing_returnsFalse() { + fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -170,10 +177,12 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { // Touch events are not intercepted. assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() + // User activity is not sent to PowerManager. + verify(powerManager, times(0)).userActivity(any(), any(), any()) } @Test - fun onTouchEvent_communalAndShadeShowing_returnsFalse() { + fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) 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 b2394820b2af..49579f6f46b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -181,7 +181,6 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -191,6 +190,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.user.domain.interactor.UserSwitcherInteractor; diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 437d00ac8723..1c98567592e8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -23,8 +23,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static com.google.common.truth.Truth.assertThat; -import static kotlinx.coroutines.flow.FlowKt.emptyFlow; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -38,6 +36,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + import android.app.IActivityManager; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -94,11 +94,11 @@ import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.user.domain.interactor.UserSwitcherInteractor; @@ -216,6 +216,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, shadeRepository, @@ -234,6 +236,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, mKeyguardSecurityModel, 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 39051eba3ad9..f0a230379d27 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -100,11 +100,11 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.user.domain.interactor.UserSwitcherInteractor; import com.android.systemui.util.kotlin.JavaAdapter; @@ -251,6 +251,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, mShadeRepository, @@ -269,6 +271,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, mock(KeyguardSecurityModel.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt index 65e0fa146fe3..71a7420636cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt @@ -50,8 +50,8 @@ import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository import com.android.systemui.user.data.model.UserSwitcherSettingsModel import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.domain.UserDomainLayerModule @@ -163,7 +163,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { testComponent.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) - userSetupRepository.setUserSetup(false) + userSetupRepository.setUserSetUp(false) userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true)) val actual by collectLastValue(underTest.isExpandToQsEnabled) @@ -175,7 +175,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { fun isExpandToQsEnabled_shadeNotEnabled_false() = testComponent.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) - userSetupRepository.setUserSetup(true) + userSetupRepository.setUserSetUp(true) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -191,7 +191,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { fun isExpandToQsEnabled_quickSettingsNotEnabled_false() = testComponent.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) - userSetupRepository.setUserSetup(true) + userSetupRepository.setUserSetUp(true) disableFlagsRepository.disableFlags.value = DisableFlagsModel( @@ -206,7 +206,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { fun isExpandToQsEnabled_dozing_false() = testComponent.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) - userSetupRepository.setUserSetup(true) + userSetupRepository.setUserSetUp(true) disableFlagsRepository.disableFlags.value = DisableFlagsModel( disable2 = DISABLE2_NONE, @@ -229,7 +229,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { disable2 = DISABLE2_NONE, ) - userSetupRepository.setUserSetup(true) + userSetupRepository.setUserSetUp(true) val actual by collectLastValue(underTest.isExpandToQsEnabled) @@ -262,7 +262,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { DisableFlagsModel( disable2 = DISABLE2_NONE, ) - userSetupRepository.setUserSetup(true) + userSetupRepository.setUserSetUp(true) val actual by collectLastValue(underTest.isExpandToQsEnabled) @@ -290,7 +290,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { DisableFlagsModel( disable2 = DISABLE2_NONE, ) - userSetupRepository.setUserSetup(true) + userSetupRepository.setUserSetUp(true) val actual by collectLastValue(underTest.isExpandToQsEnabled) @@ -322,21 +322,21 @@ class ShadeInteractorImplTest : SysuiTestCase() { DisableFlagsModel( disable2 = DISABLE2_NONE, ) - userSetupRepository.setUserSetup(true) + userSetupRepository.setUserSetUp(true) val actual by collectLastValue(underTest.isExpandToQsEnabled) assertThat(actual).isTrue() // WHEN the user is no longer setup - userSetupRepository.setUserSetup(false) + userSetupRepository.setUserSetUp(false) userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true)) // THEN expand is disabled assertThat(actual).isFalse() // WHEN the user is setup again - userSetupRepository.setUserSetup(true) + userSetupRepository.setUserSetUp(true) // THEN expand is enabled assertThat(actual).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index aed616349eb3..1a6a06742f9e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -51,9 +51,9 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository import com.android.systemui.util.mockito.mock import kotlinx.coroutines.flow.emptyFlow import org.junit.Assert.assertEquals @@ -69,8 +69,8 @@ import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @@ -79,6 +79,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope + private val testDispatcher = utils.testDispatcher private lateinit var shadeInteractor: ShadeInteractor private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor private lateinit var fromPrimaryBouncerTransitionInteractor: @@ -143,6 +144,8 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { keyguardTransitionRepository, keyguardTransitionInteractor, testScope.backgroundScope, + testDispatcher, + testDispatcher, keyguardInteractor, featureFlags, shadeRepository, @@ -162,6 +165,8 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { keyguardTransitionRepository, keyguardTransitionInteractor, testScope.backgroundScope, + testDispatcher, + testDispatcher, keyguardInteractor, featureFlags, mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 83ba68460aa5..a1721208b2f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -626,8 +626,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testClearNotifications_clearAllInProgress() { - mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false); - ExpandableNotificationRow row = createClearableRow(); when(row.getEntry().hasFinishedInitialization()).thenReturn(true); doReturn(true).when(mStackScroller).isVisible(row); @@ -672,8 +670,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testAddNotificationUpdatesSpeedBumpIndex() { - mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false); - // initial state calculated == 0 assertEquals(0, mStackScroller.getSpeedBumpIndex()); @@ -690,8 +686,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testAddAmbientNotificationNoSpeedBumpUpdate() { - mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false); - // initial state calculated == 0 assertEquals(0, mStackScroller.getSpeedBumpIndex()); @@ -708,8 +702,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testRemoveNotificationUpdatesSpeedBump() { - mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false); - // initial state calculated == 0 assertEquals(0, mStackScroller.getSpeedBumpIndex()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryTest.kt deleted file mode 100644 index 91c233a4177d..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryTest.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.pipeline.mobile.data.repository - -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener -import com.android.systemui.util.mockito.argumentCaptor -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@SmallTest -class UserSetupRepositoryTest : SysuiTestCase() { - private lateinit var underTest: UserSetupRepository - @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController - private val scope = CoroutineScope(IMMEDIATE) - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - underTest = - UserSetupRepositoryImpl( - deviceProvisionedController, - IMMEDIATE, - scope, - ) - } - - @After - fun tearDown() { - scope.cancel() - } - - @Test - fun testUserSetup_defaultFalse() = - runBlocking(IMMEDIATE) { - var latest: Boolean? = null - - val job = underTest.isUserSetupFlow.onEach { latest = it }.launchIn(this) - - assertThat(latest).isFalse() - - job.cancel() - } - - @Test - fun testUserSetup_updatesOnChange() = - runBlocking(IMMEDIATE) { - var latest: Boolean? = null - - val job = underTest.isUserSetupFlow.onEach { latest = it }.launchIn(this) - - whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true) - val callback = getDeviceProvisionedListener() - callback.onUserSetupChanged() - - assertThat(latest).isTrue() - - job.cancel() - } - - private fun getDeviceProvisionedListener(): DeviceProvisionedListener { - val captor = argumentCaptor<DeviceProvisionedListener>() - verify(deviceProvisionedController).addCallback(captor.capture()) - return captor.value!! - } - - companion object { - private val IMMEDIATE = Dispatchers.Main.immediate - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt index 2060288c28a4..0b14be1eefbd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt @@ -31,10 +31,10 @@ import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository import com.android.systemui.util.CarrierConfigTracker import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt index 52fc2589a3f9..889130f47820 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt @@ -30,7 +30,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionS import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor @@ -39,6 +38,7 @@ import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconMod import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository import com.android.systemui.util.CarrierConfigTracker import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt index 44fa13283991..147efcbd67c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt @@ -42,7 +42,6 @@ import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.Airpla import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel @@ -51,6 +50,7 @@ import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository import com.android.systemui.util.CarrierConfigTracker import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt index 1bdf64434fcb..0cb3329dcb4d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt @@ -35,7 +35,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionS import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy @@ -48,6 +47,7 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiIntera import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository import com.android.systemui.util.CarrierConfigTracker import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index b7529a82dd3d..30434c84c692 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -160,7 +160,6 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.Share import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -169,6 +168,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.user.domain.interactor.UserSwitcherInteractor; import com.android.systemui.util.FakeEventLog; @@ -444,6 +444,8 @@ public class BubblesTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, shadeRepository, @@ -462,6 +464,8 @@ public class BubblesTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, mock(KeyguardSecurityModel.class), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt index e24ba265e260..c2dc67319fff 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt @@ -48,6 +48,9 @@ private constructor( @get:[Provides Application] val appScope: CoroutineScope = scope.backgroundScope + @get:[Provides Background] + val bgScope: CoroutineScope = scope.backgroundScope + @Module interface Bindings { @Binds @Main fun bindMainContext(dispatcher: TestDispatcher): CoroutineContext diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt index 1d44929a20f0..93e0b418d076 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt @@ -62,6 +62,10 @@ class FakeDeviceEntryFingerprintAuthRepository @Inject constructor() : fun setAuthenticationStatus(status: FingerprintAuthenticationStatus) { _authenticationStatus.value = status } + + fun setShouldUpdateIndicatorVisibility(shouldUpdateIndicatorVisibility: Boolean) { + _shouldUpdateIndicatorVisibility.value = shouldUpdateIndicatorVisibility + } } @Module diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt index b03d0b822161..b1a0b67d6648 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt @@ -20,6 +20,7 @@ import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.shade.data.repository.shadeRepository import dagger.Lazy @@ -30,6 +31,8 @@ val Kosmos.fromLockscreenTransitionInteractor by transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, flags = featureFlagsClassic, shadeRepository = shadeRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt index ade3e1a82297..97536e20cb0a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.user.domain.interactor.selectedUserInteractor @@ -30,6 +31,8 @@ val Kosmos.fromPrimaryBouncerTransitionInteractor by transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, flags = featureFlagsClassic, keyguardSecurityModel = keyguardSecurityModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryFgIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryFgIconViewModelKosmos.kt index 4bfe4f571b05..4f638d0e4a38 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryFgIconViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryFgIconViewModelKosmos.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.applicationContext import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor -import com.android.systemui.common.ui.data.repository.configurationRepository +import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos @@ -29,7 +29,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.deviceEntryForegroundIconViewModel by Fixture { DeviceEntryForegroundViewModel( context = applicationContext, - configurationRepository = configurationRepository, + configurationInteractor = configurationInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, transitionInteractor = keyguardTransitionInteractor, deviceEntryIconViewModel = deviceEntryIconViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt index 1185f2edef3b..0307c414351f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt @@ -35,14 +35,21 @@ class FakeQSTileIntentUserInputHandler : QSTileIntentUserInputHandler { mutableInputs.add(Input.Intent(view, intent)) } - override fun handle(view: View?, pendingIntent: PendingIntent) { - mutableInputs.add(Input.PendingIntent(view, pendingIntent)) + override fun handle( + view: View?, + pendingIntent: PendingIntent, + requestLaunchingDefaultActivity: Boolean + ) { + mutableInputs.add(Input.PendingIntent(view, pendingIntent, requestLaunchingDefaultActivity)) } sealed interface Input { data class Intent(val view: View?, val intent: android.content.Intent) : Input - data class PendingIntent(val view: View?, val pendingIntent: android.app.PendingIntent) : - Input + data class PendingIntent( + val view: View?, + val pendingIntent: android.app.PendingIntent, + val requestLaunchingDefaultActivity: Boolean + ) : Input } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt index 7da57f024ec7..afd37b3f92dc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt @@ -29,8 +29,8 @@ import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor import com.android.systemui.statusbar.phone.dozeParameters -import com.android.systemui.statusbar.pipeline.mobile.data.repository.userSetupRepository import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository +import com.android.systemui.statusbar.policy.data.repository.userSetupRepository import com.android.systemui.user.domain.interactor.userSwitcherInteractor var Kosmos.baseShadeInteractor: BaseShadeInteractor by diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt index f1e6a053643f..f1e6a053643f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt new file mode 100644 index 000000000000..c416ea1c1b39 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt @@ -0,0 +1,23 @@ +/* + * 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.statusbar.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeRemoteInputRepository : RemoteInputRepository { + override val isRemoteInputActive = MutableStateFlow(false) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryKosmos.kt new file mode 100644 index 000000000000..1684efba2ca2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * 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.statusbar.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.remoteInputRepository: RemoteInputRepository by + Kosmos.Fixture { fakeRemoteInputRepository } +val Kosmos.fakeRemoteInputRepository by Kosmos.Fixture { FakeRemoteInputRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorKosmos.kt new file mode 100644 index 000000000000..07b39dc87a9a --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorKosmos.kt @@ -0,0 +1,22 @@ +/* + * 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.statusbar.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.data.repository.remoteInputRepository + +val Kosmos.remoteInputInteractor by Kosmos.Fixture { RemoteInputInteractor(remoteInputRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt index 44f31343b06d..f5a4c034d836 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt @@ -24,6 +24,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.activeNotif import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor import com.android.systemui.statusbar.notification.footer.ui.viewmodel.footerViewModel import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.notificationShelfViewModel +import com.android.systemui.statusbar.policy.domain.interactor.userSetupInteractor import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import java.util.Optional diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/FakeStatusBarPipelineMobileDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/FakeStatusBarPipelineMobileDataLayerModule.kt index 549929c2c04a..6e2d12ac06b6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/FakeStatusBarPipelineMobileDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/FakeStatusBarPipelineMobileDataLayerModule.kt @@ -15,7 +15,7 @@ */ package com.android.systemui.statusbar.pipeline.mobile.data -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepositoryModule +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepositoryModule import dagger.Module @Module(includes = [FakeUserSetupRepositoryModule::class]) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt index 5f4d7bf6f371..5f4d7bf6f371 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt index a9ee4055d1a8..de6c87c2b515 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt @@ -76,8 +76,8 @@ class FakeMobileIconsInteractor( private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON) override val defaultMobileIconGroup = _defaultMobileIconGroup - private val _isUserSetup = MutableStateFlow(true) - override val isUserSetup = _isUserSetup + private val _isUserSetUp = MutableStateFlow(true) + override val isUserSetUp = _isUserSetUp override val isForceHidden = MutableStateFlow(false) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeUserSetupRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeUserSetupRepository.kt index 55e81bbc77e7..76a9861f5a6f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeUserSetupRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeUserSetupRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.pipeline.mobile.data.repository +package com.android.systemui.statusbar.policy.data.repository import com.android.systemui.dagger.SysUISingleton import dagger.Binds @@ -26,10 +26,10 @@ import kotlinx.coroutines.flow.MutableStateFlow @SysUISingleton class FakeUserSetupRepository @Inject constructor() : UserSetupRepository { private val _isUserSetup: MutableStateFlow<Boolean> = MutableStateFlow(true) - override val isUserSetupFlow = _isUserSetup + override val isUserSetUp = _isUserSetup - fun setUserSetup(setup: Boolean) { - _isUserSetup.value = setup + fun setUserSetUp(isSetUp: Boolean) { + _isUserSetup.value = isSetUp } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepositoryKosmos.kt index 7b9634a7abb5..a1c5b9aef54e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/UserSetupRepositoryKosmos.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.pipeline.mobile.data.repository +package com.android.systemui.statusbar.policy.data.repository import com.android.systemui.kosmos.Kosmos diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/UserSetupInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/UserSetupInteractorKosmos.kt new file mode 100644 index 000000000000..83f4939cee1f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/UserSetupInteractorKosmos.kt @@ -0,0 +1,22 @@ +/* + * 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.statusbar.policy.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.policy.data.repository.userSetupRepository + +val Kosmos.userSetupInteractor by Kosmos.Fixture { UserSetupInteractor(userSetupRepository) } diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp index e52cefb2d7e4..81fd8ce12f05 100644 --- a/packages/SystemUI/unfold/Android.bp +++ b/packages/SystemUI/unfold/Android.bp @@ -39,7 +39,4 @@ android_library { sdk_version: "current", min_sdk_version: "current", plugins: ["dagger2-compiler"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } diff --git a/packages/SystemUI/unfold/lint-baseline.xml b/packages/SystemUI/unfold/lint-baseline.xml deleted file mode 100644 index 449ed2e60853..000000000000 --- a/packages/SystemUI/unfold/lint-baseline.xml +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev"> -</issues> diff --git a/services/Android.bp b/services/Android.bp index 0b484f473d36..7e8333c7ba5f 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -148,9 +148,6 @@ filegroup { java_library { name: "Slogf", srcs: ["core/java/com/android/server/utils/Slogf.java"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // merge all required services into one jar diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp index a3546716d5ca..69cc68a55108 100644 --- a/services/accessibility/Android.bp +++ b/services/accessibility/Android.bp @@ -22,6 +22,7 @@ java_library_static { lint: { error_checks: ["MissingPermissionAnnotation"], baseline_filename: "lint-baseline.xml", + }, srcs: [ ":services.accessibility-sources", @@ -50,9 +51,6 @@ java_library_static { libs: [ "androidx.annotation_annotation", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } aconfig_declarations { diff --git a/services/accessibility/lint-baseline.xml b/services/accessibility/lint-baseline.xml index 6bec8cf5f018..b808219ef9c6 100644 --- a/services/accessibility/lint-baseline.xml +++ b/services/accessibility/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="SimpleManualPermissionEnforcement" @@ -23,4 +23,4 @@ column="9"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/backup/lint-baseline.xml b/services/backup/lint-baseline.xml index 93c9390feb9c..46de2cdd7a47 100644 --- a/services/backup/lint-baseline.xml +++ b/services/backup/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -36,4 +36,4 @@ line="207"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index 4b3772a7a54d..d0eb59d83f5a 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -22,6 +22,7 @@ import static android.app.PendingIntent.FLAG_ONE_SHOT; import static android.companion.CompanionDeviceManager.REASON_INTERNAL_ERROR; import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR; import static android.content.ComponentName.createRelative; +import static android.content.pm.PackageManager.FEATURE_WATCH; import static com.android.server.companion.CompanionDeviceManagerService.DEBUG; import static com.android.server.companion.MetricUtils.logCreateAssociation; @@ -169,16 +170,29 @@ class AssociationRequestsProcessor { enforcePermissionsForAssociation(mContext, request, packageUid); enforceUsesCompanionDeviceFeature(mContext, userId, packageName); - // 2. Check if association can be created without launching UI (i.e. CDM needs NEITHER + // 2a. Check if association can be created without launching UI (i.e. CDM needs NEITHER // to perform discovery NOR to collect user consent). if (request.isSelfManaged() && !request.isForceConfirmation() && !willAddRoleHolder(request, packageName, userId)) { - // 2a. Create association right away. + // 2a.1. Create association right away. createAssociationAndNotifyApplication(request, packageName, userId, /* macAddress */ null, callback, /* resultReceiver */ null); return; } + // 2a.2. Report an error if a 3p app tries to create a non-self-managed association and + // launch UI on watch. + if (mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH)) { + String errorMessage = "3p apps are not allowed to create associations on watch."; + Slog.e(TAG, errorMessage); + try { + callback.onFailure(errorMessage); + } catch (RemoteException e) { + // ignored + } + return; + } + // 2b. Build a PendingIntent for launching the confirmation UI, and send it back to the app: // 2b.1. Populate the request with required info. diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java index 4e471f5b0bc9..260b21f109d0 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java @@ -21,6 +21,7 @@ import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PERMISSION_RESTORE; import static android.content.ComponentName.createRelative; +import static android.content.pm.PackageManager.FEATURE_WATCH; import static com.android.server.companion.Utils.prepareForIpc; @@ -40,6 +41,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManagerInternal; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -306,6 +308,13 @@ public class SystemDataTransferProcessor { } private void onReceivePermissionRestore(byte[] message) { + // TODO: Disable Permissions Sync for non-watch devices until we figure out a better UX + // model + if (!Build.isDebuggable() && !mContext.getPackageManager().hasSystemFeature( + FEATURE_WATCH)) { + Slog.e(LOG_TAG, "Permissions restore is only available on watch."); + return; + } Slog.i(LOG_TAG, "Applying permissions."); // Start applying permissions UserHandle user = mContext.getUser(); diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java index 62c670317f5f..3e45626d9799 100644 --- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java +++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java @@ -125,7 +125,7 @@ public class CompanionTransportManager { * Send a message to remote devices through the transports */ public void sendMessage(int message, byte[] data, int[] associationIds) { - Slog.i(TAG, "Sending message 0x" + Integer.toHexString(message) + Slog.d(TAG, "Sending message 0x" + Integer.toHexString(message) + " data length " + data.length); synchronized (mTransports) { for (int i = 0; i < associationIds.length; i++) { diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java index 22b18ac9653b..8a5774e55ce2 100644 --- a/services/companion/java/com/android/server/companion/transport/Transport.java +++ b/services/companion/java/com/android/server/companion/transport/Transport.java @@ -284,7 +284,7 @@ public abstract class Transport { if (mListeners.containsKey(message)) { try { mListeners.get(message).onMessageReceived(getAssociationId(), data); - Slog.i(TAG, "Message 0x" + Integer.toHexString(message) + Slog.d(TAG, "Message 0x" + Integer.toHexString(message) + " is received from associationId " + mAssociationId + ", sending data length " + data.length + " to the listener."); } catch (RemoteException ignored) { diff --git a/services/companion/lint-baseline.xml b/services/companion/lint-baseline.xml index 03eae3901e51..020126f75ea0 100644 --- a/services/companion/lint-baseline.xml +++ b/services/companion/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -12,4 +12,4 @@ column="14"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/core/Android.bp b/services/core/Android.bp index a3fc3bf5ec72..a6ed498e93db 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -234,9 +234,6 @@ java_genrule { java_library { name: "services.core", static_libs: ["services.core.priorityboosted"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library_host { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 02f4485d5b40..0cff8b7e88ed 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -4464,6 +4464,12 @@ public final class ActiveServices { } } if (userId > 0) { + if (mAm.isSystemUserOnly(sInfo.flags)) { + Slog.w(TAG_SERVICE, service + " is only available for the SYSTEM user," + + " calling userId is: " + userId); + return null; + } + if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo, sInfo.name, sInfo.flags) && mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) { @@ -5453,7 +5459,7 @@ public final class ActiveServices { // Force an immediate oomAdjUpdate, so the client app could be in the correct process state // before doing any service related transactions mAm.enqueueOomAdjTargetLocked(app); - mAm.updateOomAdjLocked(app, OOM_ADJ_REASON_START_SERVICE); + mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE); boolean created = false; try { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f20c2d892879..e583a6cd6b1f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -13747,6 +13747,11 @@ public class ActivityManagerService extends IActivityManager.Stub return result; } + boolean isSystemUserOnly(int flags) { + return android.multiuser.Flags.enableSystemUserOnlyForServicesAndProviders() + && (flags & ServiceInfo.FLAG_SYSTEM_USER_ONLY) != 0; + } + /** * Checks to see if the caller is in the same app as the singleton * component, or the component is in a special app. It allows special apps diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java index e0a224629174..9fc0bf920969 100644 --- a/services/core/java/com/android/server/am/AnrHelper.java +++ b/services/core/java/com/android/server/am/AnrHelper.java @@ -63,6 +63,11 @@ class AnrHelper { private static final long CONSECUTIVE_ANR_TIME_MS = TimeUnit.MINUTES.toMillis(2); /** + * Time to wait before taking dumps for other processes to reduce load at boot time. + */ + private static final long SELF_ONLY_AFTER_BOOT_MS = TimeUnit.MINUTES.toMillis(10); + + /** * The keep alive time for the threads in the helper threadpool executor */ private static final int DEFAULT_THREAD_KEEP_ALIVE_SECOND = 10; @@ -231,7 +236,8 @@ class AnrHelper { // If there are many ANR at the same time, the latency may be larger. // If the latency is too large, the stack trace might not be meaningful. final long reportLatency = startTime - r.mTimestamp; - final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS; + final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS + || startTime < SELF_ONLY_AFTER_BOOT_MS; r.appNotResponding(onlyDumpSelf); final long endTime = SystemClock.uptimeMillis(); Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in " diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 095d907d7df6..30f21a65b5b1 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -1249,9 +1249,9 @@ public class ContentProviderHelper { ProviderInfo cpi = providers.get(i); boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags); - if (singleton && app.userId != UserHandle.USER_SYSTEM) { - // This is a singleton provider, but a user besides the - // default user is asking to initialize a process it runs + if (isSingletonOrSystemUserOnly(cpi) && app.userId != UserHandle.USER_SYSTEM) { + // This is a singleton or a SYSTEM user only provider, but a user besides the + // SYSTEM user is asking to initialize a process it runs // in... well, no, it doesn't actually run in this process, // it runs in the process of the default user. Get rid of it. providers.remove(i); @@ -1398,8 +1398,7 @@ public class ContentProviderHelper { final boolean processMatch = Objects.equals(pi.processName, app.processName) || pi.multiprocess; - final boolean userMatch = !mService.isSingleton( - pi.processName, pi.applicationInfo, pi.name, pi.flags) + final boolean userMatch = !isSingletonOrSystemUserOnly(pi) || app.userId == UserHandle.USER_SYSTEM; final boolean isInstantApp = pi.applicationInfo.isInstantApp(); final boolean splitInstalled = pi.splitName == null @@ -1985,4 +1984,13 @@ public class ContentProviderHelper { return isAuthRedirected; } } + + /** + * Returns true if Provider is either singleUser or systemUserOnly provider. + */ + private boolean isSingletonOrSystemUserOnly(ProviderInfo pi) { + return (android.multiuser.Flags.enableSystemUserOnlyForServicesAndProviders() + && mService.isSystemUserOnly(pi.flags)) + || mService.isSingleton(pi.processName, pi.applicationInfo, pi.name, pi.flags); + } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 91d533c73b5d..4cbee2b89bb2 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -8835,6 +8835,8 @@ public class AudioService extends IAudioService.Stub synchronized (VolumeStreamState.class) { oldIndex = getIndex(device); index = getValidIndex(index, hasModifyAudioSettings); + // for STREAM_SYSTEM_ENFORCED, do not sync aliased streams on the enforced index + int aliasIndex = index; if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) { index = mIndexMax; } @@ -8853,7 +8855,8 @@ public class AudioService extends IAudioService.Stub if (streamType != mStreamType && mStreamVolumeAlias[streamType] == mStreamType && (changed || !aliasStreamState.hasIndexForDevice(device))) { - final int scaledIndex = rescaleIndex(index, mStreamType, streamType); + final int scaledIndex = + rescaleIndex(aliasIndex, mStreamType, streamType); aliasStreamState.setIndex(scaledIndex, device, caller, hasModifyAudioSettings); if (isCurrentDevice) { @@ -9375,6 +9378,14 @@ public class AudioService extends IAudioService.Stub if (mIsSingleVolume && (streamState.mStreamType != AudioSystem.STREAM_MUSIC)) { return; } + + // Persisting STREAM_SYSTEM_ENFORCED index is not needed as its alias (STREAM_RING) + // is persisted. This can also be problematic when the enforcement is active as it will + // override current SYSTEM_RING persisted value given they share the same settings name + // (due to aliasing). + if (streamState.mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) { + return; + } if (streamState.hasValidSettingsName()) { mSettings.putSystemIntForUser(mContentResolver, streamState.getSettingNameForDevice(device), diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index c2bc1e4f6be2..a30cdc47a461 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -62,6 +62,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -147,6 +148,15 @@ public class SoundDoseHelper { private static final int SAFE_MEDIA_VOLUME_UNINITIALIZED = -1; + // see {@link #recordToPersistedString(SoundDoseRecord)} + // this is computed conservatively to accommodate the legacy persisting of SoundDoseRecords in + // which we materialized more decimal values. + // TODO: adjust value after soaking in + private static final int MAX_RECORDS_STRING_LENGTH = 50; + private static final int MAX_SETTINGS_LENGTH = 32768; + private static final int MAX_NUMBER_OF_CACHED_RECORDS = + MAX_SETTINGS_LENGTH / MAX_RECORDS_STRING_LENGTH; + private final EventLogger mLogger = new EventLogger(AudioService.LOG_NB_EVENTS_SOUND_DOSE, "CSD updates"); @@ -923,7 +933,7 @@ public class SoundDoseHelper { Log.v(TAG, "Initializing sound dose"); try { - if (mCachedAudioDeviceCategories.size() > 0) { + if (!mCachedAudioDeviceCategories.isEmpty()) { soundDose.initCachedAudioDeviceCategories(mCachedAudioDeviceCategories.toArray( new ISoundDose.AudioDeviceCategory[0])); mCachedAudioDeviceCategories.clear(); @@ -957,6 +967,7 @@ public class SoundDoseHelper { mGlobalTimeOffsetInSecs); if (records != null) { mDoseRecords.addAll(records); + sanitizeDoseRecords_l(); } } } @@ -1176,17 +1187,35 @@ public class SoundDoseHelper { && r.duration == record.duration)) { Log.w(TAG, "Could not find cached record to remove: " + record); } - } else { + } else if (record.value > 0) { mDoseRecords.add(record); } } + sanitizeDoseRecords_l(); + mAudioHandler.sendMessageAtTime(mAudioHandler.obtainMessage(MSG_PERSIST_CSD_VALUES, /* arg1= */0, /* arg2= */0, /* obj= */null), /* delay= */0); mLogger.enqueue(SoundDoseEvent.getDoseUpdateEvent(currentCsd, totalDuration)); } + @GuardedBy("mCsdStateLock") + private void sanitizeDoseRecords_l() { + if (mDoseRecords.size() > MAX_NUMBER_OF_CACHED_RECORDS) { + int nrToRemove = MAX_NUMBER_OF_CACHED_RECORDS - mDoseRecords.size(); + Log.w(TAG, + "Removing " + nrToRemove + " records from the total of " + mDoseRecords.size()); + // Remove older elements to fit into persisted settings max length + Iterator<SoundDoseRecord> recordIterator = mDoseRecords.iterator(); + while (recordIterator.hasNext() && nrToRemove > 0) { + recordIterator.next(); + recordIterator.remove(); + --nrToRemove; + } + } + } + @SuppressWarnings("GuardedBy") // avoid limitation with intra-procedural analysis of lambdas private void onPersistSoundDoseRecords() { synchronized (mCsdStateLock) { @@ -1213,8 +1242,8 @@ public class SoundDoseHelper { long globalTimeOffsetInSecs) { return convertToGlobalTime(record.timestamp, globalTimeOffsetInSecs) + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.duration - + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.value - + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.averageMel; + + PERSIST_CSD_RECORD_FIELD_SEPARATOR + String.format("%.3f", record.value) + + PERSIST_CSD_RECORD_FIELD_SEPARATOR + String.format("%.3f", record.averageMel); } private static long convertToGlobalTime(long bootTimeInSecs, long globalTimeOffsetInSecs) { diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index c9569cbf4b9a..a2319a8a7c07 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -109,7 +109,7 @@ flag { name: "back_up_smooth_display_and_force_peak_refresh_rate" namespace: "display_manager" description: "Feature flag for backing up Smooth Display and Force Peak Refresh Rate" - bug: "211737588" + bug: "299552529" is_fixed_read_only: true } @@ -125,7 +125,7 @@ flag { name: "brightness_int_range_user_perception" namespace: "display_manager" description: "Feature flag for converting the brightness integer range to the user perception scale" - bug: "183655602" + bug: "319236956" is_fixed_read_only: true } 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 50e953323443..ad3deffb9590 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -22,7 +22,6 @@ import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT; import static android.view.Display.Mode.INVALID_MODE_ID; import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE; -import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay; import android.annotation.IntegerRes; import android.annotation.NonNull; @@ -238,8 +237,11 @@ public class DisplayModeDirector { * is ready. */ public void start(SensorManager sensorManager) { - mSettingsObserver.observe(); + // This has to be called first to read the supported display modes that will be used by + // other observers mDisplayObserver.observe(); + + mSettingsObserver.observe(); mBrightnessObserver.observe(sensorManager); mSensorObserver.observe(); mHbmObserver.observe(); @@ -620,11 +622,16 @@ public class DisplayModeDirector { } @VisibleForTesting + DisplayObserver getDisplayObserver() { + return mDisplayObserver; + } + + @VisibleForTesting DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { synchronized (mLock) { - mSettingsObserver.updateRefreshRateSettingLocked( - minRefreshRate, peakRefreshRate, defaultRefreshRate); + mSettingsObserver.updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, + defaultRefreshRate, Display.DEFAULT_DISPLAY); return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY); } } @@ -897,19 +904,17 @@ public class DisplayModeDirector { if (defaultPeakRefreshRate == null) { setDefaultPeakRefreshRate(mDefaultDisplayDeviceConfig, /* attemptReadFromFeatureParams= */ false); - updateRefreshRateSettingLocked(); } else if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) { mDefaultPeakRefreshRate = defaultPeakRefreshRate; - updateRefreshRateSettingLocked(); } + updateRefreshRateSettingLocked(); } } @Override public void onChange(boolean selfChange, Uri uri, int userId) { synchronized (mLock) { - if (mPeakRefreshRateSetting.equals(uri) - || mMinRefreshRateSetting.equals(uri)) { + if (mPeakRefreshRateSetting.equals(uri) || mMinRefreshRateSetting.equals(uri)) { updateRefreshRateSettingLocked(); } else if (mLowPowerModeSetting.equals(uri)) { updateLowPowerModeSettingLocked(); @@ -969,9 +974,29 @@ public class DisplayModeDirector { mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode); } + /** + * Update refresh rate settings for all displays + */ + @GuardedBy("mLock") private void updateRefreshRateSettingLocked() { + for (int i = 0; i < mSupportedModesByDisplay.size(); i++) { + updateRefreshRateSettingLocked(mSupportedModesByDisplay.keyAt(i)); + } + } + + /** + * Update refresh rate settings for a specific display + * @param displayId The display ID + */ + @GuardedBy("mLock") + private void updateRefreshRateSettingLocked(int displayId) { final ContentResolver cr = mContext.getContentResolver(); - float highestRefreshRate = findHighestRefreshRateForDefaultDisplay(mContext); + if (!mSupportedModesByDisplay.contains(displayId)) { + Slog.e(TAG, "Cannot update refresh rate setting: no supported modes for display " + + displayId); + return; + } + float highestRefreshRate = getMaxRefreshRateLocked(displayId); float minRefreshRate = Settings.System.getFloatForUser(cr, Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId()); @@ -1009,11 +1034,13 @@ public class DisplayModeDirector { Float.POSITIVE_INFINITY, cr.getUserId()); } - updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); + updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate, + displayId); } - private void updateRefreshRateSettingLocked( - float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { + @GuardedBy("mLock") + private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate, + float defaultRefreshRate, int displayId) { // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is // used to predict if we're going to be doing frequent refresh rate switching, and if // so, enable the brightness observer. The logic here is more complicated and fragile @@ -1021,9 +1048,9 @@ public class DisplayModeDirector { Vote peakVote = peakRefreshRate == 0f ? null : Vote.forRenderFrameRates(0f, Math.max(minRefreshRate, peakRefreshRate)); - mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, + mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, peakVote); - mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, Vote.forRenderFrameRates(minRefreshRate, Float.POSITIVE_INFINITY)); Vote defaultVote = defaultRefreshRate == 0f @@ -1050,6 +1077,14 @@ public class DisplayModeDirector { mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate); } + private void removeRefreshRateSetting(int displayId) { + mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, + null); + mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + null); + mVotesStorage.updateVote(displayId, Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE, null); + } + private void updateModeSwitchingTypeSettingLocked() { final ContentResolver cr = mContext.getContentResolver(); int switchingType = Settings.Secure.getIntForUser( @@ -1180,7 +1215,8 @@ public class DisplayModeDirector { } } - private final class DisplayObserver implements DisplayManager.DisplayListener { + @VisibleForTesting + public final class DisplayObserver implements DisplayManager.DisplayListener { // Note that we can never call into DisplayManager or any of the non-POD classes it // returns, while holding mLock since it may call into DMS, which might be simultaneously // calling into us already holding its own lock. @@ -1227,11 +1263,10 @@ public class DisplayModeDirector { // Populate existing displays SparseArray<Display.Mode[]> modes = new SparseArray<>(); SparseArray<Display.Mode> defaultModes = new SparseArray<>(); - DisplayInfo info = new DisplayInfo(); Display[] displays = mInjector.getDisplays(); for (Display d : displays) { final int displayId = d.getDisplayId(); - d.getDisplayInfo(info); + DisplayInfo info = getDisplayInfo(displayId); modes.put(displayId, info.supportedModes); defaultModes.put(displayId, info.getDefaultMode()); } @@ -1259,6 +1294,7 @@ public class DisplayModeDirector { synchronized (mLock) { mSupportedModesByDisplay.remove(displayId); mDefaultModeByDisplay.remove(displayId); + mSettingsObserver.removeRefreshRateSetting(displayId); } updateLayoutLimitedFrameRate(displayId, null); removeUserSettingDisplayPreferredSize(displayId); @@ -1409,6 +1445,7 @@ public class DisplayModeDirector { } if (changed) { notifyDesiredDisplayModeSpecsChangedLocked(); + mSettingsObserver.updateRefreshRateSettingLocked(displayId); } } } diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index d02b6f4cff53..171fbb6f8a16 100644 --- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -25,6 +25,7 @@ import static com.android.server.location.gnss.GnssManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; +import android.location.GnssMeasurement; import android.location.GnssMeasurementRequest; import android.location.GnssMeasurementsEvent; import android.location.IGnssMeasurementsListener; @@ -33,6 +34,7 @@ import android.os.IBinder; import android.stats.location.LocationStatsEnums; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.server.location.gnss.GnssConfiguration.HalInterfaceVersion; import com.android.server.location.gnss.hal.GnssNative; import com.android.server.location.injector.AppOpsHelper; @@ -40,6 +42,8 @@ import com.android.server.location.injector.Injector; import com.android.server.location.injector.LocationUsageLogger; import com.android.server.location.injector.SettingsHelper; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.Collection; /** @@ -91,6 +95,9 @@ public final class GnssMeasurementsProvider extends private final LocationUsageLogger mLogger; private final GnssNative mGnssNative; + @GuardedBy("mMultiplexerLock") + private GnssMeasurementsEvent mLastGnssMeasurementsEvent; + public GnssMeasurementsProvider(Injector injector, GnssNative gnssNative) { super(injector); mAppOpsHelper = injector.getAppOpsHelper(); @@ -264,5 +271,46 @@ public final class GnssMeasurementsProvider extends return null; } }); + synchronized (mMultiplexerLock) { + mLastGnssMeasurementsEvent = event; + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + super.dump(fd, pw, args); + pw.print("last measurements="); + pw.println(getLastMeasurementEventSummary()); + } + + /** + * Returns a string of GnssMeasurementsEvent summary including received time, satellite count + * and average baseband C/No. + */ + private String getLastMeasurementEventSummary() { + synchronized (mMultiplexerLock) { + if (mLastGnssMeasurementsEvent == null) { + return null; + } + StringBuilder builder = new StringBuilder("["); + builder.append("elapsedRealtimeNs=").append( + mLastGnssMeasurementsEvent.getClock().getElapsedRealtimeNanos()); + builder.append(" measurementCount=").append( + mLastGnssMeasurementsEvent.getMeasurements().size()); + + float sumBasebandCn0 = 0; + int countBasebandCn0 = 0; + for (GnssMeasurement measurement : mLastGnssMeasurementsEvent.getMeasurements()) { + if (measurement.hasBasebandCn0DbHz()) { + sumBasebandCn0 += measurement.getBasebandCn0DbHz(); + countBasebandCn0++; + } + } + if (countBasebandCn0 > 0) { + builder.append(" avgBasebandCn0=").append(sumBasebandCn0 / countBasebandCn0); + } + builder.append("]"); + return builder.toString(); + } } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index ad090829a2f6..542b3b06184a 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -52,6 +52,7 @@ import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYP import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.IActivityManager; @@ -74,6 +75,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.content.pm.UserProperties; import android.content.res.Resources; import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; @@ -303,7 +305,7 @@ public class LockSettingsService extends ILockSettings.Stub { private boolean mThirdPartyAppsStarted; // Current password metrics for all secured users on the device. Updated when user unlocks the - // device or changes password. Removed when user is stopped. + // device or changes password. Removed if user is stopped with its CE key evicted. @GuardedBy("this") private final SparseArray<PasswordMetrics> mUserPasswordMetrics = new SparseArray<>(); @VisibleForTesting @@ -793,13 +795,33 @@ public class LockSettingsService extends ILockSettings.Stub { } @VisibleForTesting + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.QUERY_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) void onUserStopped(int userId) { hideEncryptionNotification(new UserHandle(userId)); - // User is stopped with its CE key evicted. Restore strong auth requirement to the default - // flags after boot since stopping and restarting a user later is equivalent to rebooting - // the device. + + // Normally, CE storage is locked when a user is stopped, and restarting the user requires + // strong auth. Therefore, reset the user's strong auth flags. The exception is users that + // allow delayed locking; under some circumstances, biometric authentication is allowed to + // restart such users. Don't reset the strong auth flags for such users. + // + // TODO(b/319142556): It might make more sense to reset the strong auth flags when CE + // storage is locked, instead of when the user is stopped. This would ensure the flags get + // reset if CE storage is locked later for a user that allows delayed locking. + if (android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) { + UserProperties userProperties = mUserManager.getUserProperties(UserHandle.of(userId)); + if (userProperties != null && userProperties.getAllowStoppingUserWithDelayedLocking()) { + return; + } + } int strongAuthRequired = LockPatternUtils.StrongAuthTracker.getDefaultFlags(mContext); requireStrongAuth(strongAuthRequired, userId); + + // Don't keep the password metrics in memory for a stopped user that will require strong + // auth to start again, since strong auth will make the password metrics available again. synchronized (this) { mUserPasswordMetrics.remove(userId); } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 06a8d989b930..e048522eee53 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -774,7 +774,7 @@ class MediaRouter2ServiceImpl { .generateDeviceRouteSelectedSessionInfo(packageName); } else { sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos(); - if (sessionInfos != null && !sessionInfos.isEmpty()) { + if (!sessionInfos.isEmpty()) { // Return a copy of the current system session with no modification, // except setting the client package name. return new RoutingSessionInfo.Builder(sessionInfos.get(0)) @@ -1158,14 +1158,7 @@ class MediaRouter2ServiceImpl { } else { if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission() - && !TextUtils.equals( - route.getId(), - routerRecord - .mUserRecord - .mHandler - .mSystemProvider - .getDefaultRoute() - .getId())) { + && !TextUtils.equals(route.getId(), MediaRoute2Info.ROUTE_ID_DEFAULT)) { Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to" + route); routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter( @@ -1252,11 +1245,9 @@ class MediaRouter2ServiceImpl { "transferToRouteWithRouter2 | router: %s(id: %d), route: %s", routerRecord.mPackageName, routerRecord.mRouterId, route.getId())); - String defaultRouteId = - routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId(); if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission() - && !TextUtils.equals(route.getId(), defaultRouteId)) { + && !TextUtils.equals(route.getId(), MediaRoute2Info.ROUTE_ID_DEFAULT)) { routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::notifySessionCreationFailedToRouter, routerRecord.mUserRecord.mHandler, @@ -2761,11 +2752,10 @@ class MediaRouter2ServiceImpl { if (manager != null) { notifyRequestFailedToManager( manager.mManager, toOriginalRequestId(uniqueRequestId), reason); - return; } - // Currently, only the manager can get notified of failures. - // TODO: Notify router too when the related callback is introduced. + // Currently, only manager records can get notified of failures. + // TODO(b/282936553): Notify regular routers of request failures. } private boolean handleSessionCreationRequestFailed(@NonNull MediaRoute2Provider provider, @@ -2909,11 +2899,9 @@ class MediaRouter2ServiceImpl { currentSystemSessionInfo = mSystemProvider.getDefaultSessionInfo(); } - if (currentRoutes.size() == 0) { - return; + if (!currentRoutes.isEmpty()) { + routerRecord.notifyRegistered(currentRoutes, currentSystemSessionInfo); } - - routerRecord.notifyRegistered(currentRoutes, currentSystemSessionInfo); } private static void notifyRoutesUpdatedToRouterRecords( diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 6deda468f9a2..f6571d94d554 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -653,14 +653,14 @@ public final class MediaProjectionManagerService extends SystemService } @Override // Binder call - public boolean hasProjectionPermission(int uid, String packageName) { + public boolean hasProjectionPermission(int processUid, String packageName) { final long token = Binder.clearCallingIdentity(); boolean hasPermission = false; try { hasPermission |= checkPermission(packageName, android.Manifest.permission.CAPTURE_VIDEO_OUTPUT) || mAppOps.noteOpNoThrow( - AppOpsManager.OP_PROJECT_MEDIA, uid, packageName) + AppOpsManager.OP_PROJECT_MEDIA, processUid, packageName) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(token); @@ -669,7 +669,7 @@ public final class MediaProjectionManagerService extends SystemService } @Override // Binder call - public IMediaProjection createProjection(int uid, String packageName, int type, + public IMediaProjection createProjection(int processUid, String packageName, int type, boolean isPermanentGrant) { if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION) != PackageManager.PERMISSION_GRANTED) { @@ -680,13 +680,13 @@ public final class MediaProjectionManagerService extends SystemService throw new IllegalArgumentException("package name must not be empty"); } final UserHandle callingUser = Binder.getCallingUserHandle(); - return createProjectionInternal(uid, packageName, type, isPermanentGrant, + return createProjectionInternal(processUid, packageName, type, isPermanentGrant, callingUser); } @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public IMediaProjection getProjection(int uid, String packageName) { + public IMediaProjection getProjection(int processUid, String packageName) { getProjection_enforcePermission(); if (packageName == null || packageName.isEmpty()) { throw new IllegalArgumentException("package name must not be empty"); @@ -695,7 +695,7 @@ public final class MediaProjectionManagerService extends SystemService MediaProjection projection; final long callingToken = Binder.clearCallingIdentity(); try { - projection = getProjectionInternal(uid, packageName); + projection = getProjectionInternal(processUid, packageName); } finally { Binder.restoreCallingIdentity(callingToken); } @@ -869,12 +869,13 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) { + public void notifyPermissionRequestInitiated( + int hostProcessUid, int sessionCreationSource) { notifyPermissionRequestInitiated_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { MediaProjectionManagerService.this.notifyPermissionRequestInitiated( - hostUid, sessionCreationSource); + hostProcessUid, sessionCreationSource); } finally { Binder.restoreCallingIdentity(token); } @@ -882,11 +883,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyPermissionRequestDisplayed(int hostUid) { + public void notifyPermissionRequestDisplayed(int hostProcessUid) { notifyPermissionRequestDisplayed_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { - MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostUid); + MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostProcessUid); } finally { Binder.restoreCallingIdentity(token); } @@ -894,11 +895,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyPermissionRequestCancelled(int hostUid) { + public void notifyPermissionRequestCancelled(int hostProcessUid) { notifyPermissionRequestCancelled_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { - MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostUid); + MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostProcessUid); } finally { Binder.restoreCallingIdentity(token); } @@ -906,11 +907,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyAppSelectorDisplayed(int hostUid) { + public void notifyAppSelectorDisplayed(int hostProcessUid) { notifyAppSelectorDisplayed_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { - MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostUid); + MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostProcessUid); } finally { Binder.restoreCallingIdentity(token); } @@ -919,12 +920,12 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) public void notifyWindowingModeChanged( - int contentToRecord, int targetUid, int windowingMode) { + int contentToRecord, int targetProcessUid, int windowingMode) { notifyWindowingModeChanged_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { MediaProjectionManagerService.this.notifyWindowingModeChanged( - contentToRecord, targetUid, windowingMode); + contentToRecord, targetProcessUid, windowingMode); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java index e3880c383632..deb95d8a392d 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java @@ -108,7 +108,7 @@ public class NotificationHistoryManager { for (int i = 0; i < pendingPackageRemovals.size(); i++) { userHistory.onPackageRemoved(pendingPackageRemovals.get(i)); } - mUserPendingPackageRemovals.put(userId, null); + mUserPendingPackageRemovals.remove(userId); } // delete history if it was disabled when the user was locked @@ -133,7 +133,7 @@ public class NotificationHistoryManager { synchronized (mLock) { // Actual data deletion is handled by other parts of the system (the entire directory is // removed) - we just need clean up our internal state for GC - mUserPendingPackageRemovals.put(userId, null); + mUserPendingPackageRemovals.remove(userId); mHistoryEnabled.put(userId, false); mUserPendingHistoryDisables.put(userId, false); onUserStopped(userId); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7aa7b7e1bfc1..9ddc362769f6 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -215,7 +215,6 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; @@ -373,6 +372,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; +import java.time.Clock; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -2466,8 +2466,8 @@ public class NotificationManagerService extends SystemService { mMetricsLogger = new MetricsLogger(); mRankingHandler = rankingHandler; mConditionProviders = conditionProviders; - mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders, - flagResolver, new ZenModeEventLogger(mPackageManagerClient)); + mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), Clock.systemUTC(), + mConditionProviders, flagResolver, new ZenModeEventLogger(mPackageManagerClient)); mZenModeHelper.addCallback(new ZenModeHelper.Callback() { @Override public void onConfigChanged() { diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 911643b1a634..afbf08d9b77d 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -117,6 +117,9 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.PrintWriter; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -130,9 +133,12 @@ public class ZenModeHelper { static final String TAG = "ZenModeHelper"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String PACKAGE_ANDROID = "android"; + // The amount of time rules instances can exist without their owning app being installed. private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72; static final int RULE_LIMIT_PER_PACKAGE = 100; + private static final Duration DELETED_RULE_KEPT_FOR = Duration.ofDays(30); private static final String IMPLICIT_RULE_ID_PREFIX = "implicit_"; // + pkg_name @@ -148,6 +154,7 @@ public class ZenModeHelper { private final Context mContext; private final H mHandler; + private final Clock mClock; private final SettingsObserver mSettingsObserver; private final AppOpsManager mAppOps; private final NotificationManager mNotificationManager; @@ -189,11 +196,13 @@ public class ZenModeHelper { private String[] mPriorityOnlyDndExemptPackages; - public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders, + public ZenModeHelper(Context context, Looper looper, Clock clock, + ConditionProviders conditionProviders, SystemUiSystemPropertiesFlags.FlagResolver flagResolver, ZenModeEventLogger zenModeEventLogger) { mContext = context; mHandler = new H(looper); + mClock = clock; addCallback(mMetrics); mAppOps = context.getSystemService(AppOpsManager.class); mNotificationManager = context.getSystemService(NotificationManager.class); @@ -452,6 +461,7 @@ public class ZenModeHelper { newConfig = mConfig.copy(); ZenRule rule = new ZenRule(); populateZenRule(pkg, automaticZenRule, rule, origin, /* isNew= */ true); + rule = maybeRestoreRemovedRule(newConfig, rule, automaticZenRule, origin); newConfig.automaticRules.put(rule.id, rule); maybeReplaceDefaultRule(newConfig, automaticZenRule); @@ -463,6 +473,37 @@ public class ZenModeHelper { } } + private ZenRule maybeRestoreRemovedRule(ZenModeConfig config, ZenRule ruleToAdd, + AutomaticZenRule azrToAdd, @ConfigChangeOrigin int origin) { + if (!Flags.modesApi()) { + return ruleToAdd; + } + String deletedKey = ZenModeConfig.deletedRuleKey(ruleToAdd); + if (deletedKey == null) { + // Couldn't calculate the deletedRuleKey (condition or pkg null?). This should + // never happen for an app-provided rule because NMS validates both. + return ruleToAdd; + } + ZenRule ruleToRestore = config.deletedRules.get(deletedKey); + if (ruleToRestore == null) { + return ruleToAdd; // Cannot restore. + } + + // We have found a previous rule to maybe restore. Whether we do that or not, we don't need + // to keep it around (if not restored now, it won't be in future calls either). + config.deletedRules.remove(deletedKey); + ruleToRestore.deletionInstant = null; + + if (origin != UPDATE_ORIGIN_APP) { + return ruleToAdd; // Okay to create anew. + } + + // "Preserve" the previous rule by considering the azrToAdd an update instead. + // Only app-modifiable fields will actually be modified. + populateZenRule(ruleToRestore.pkg, azrToAdd, ruleToRestore, origin, /* isNew= */ false); + return ruleToRestore; + } + private static void maybeReplaceDefaultRule(ZenModeConfig config, AutomaticZenRule addedRule) { if (!Flags.modesApi()) { return; @@ -644,7 +685,7 @@ public class ZenModeHelper { ZenRule rule = new ZenRule(); rule.id = implicitRuleId(pkg); rule.pkg = pkg; - rule.creationTime = System.currentTimeMillis(); + rule.creationTime = mClock.millis(); Binder.withCleanCallingIdentity(() -> { try { @@ -664,7 +705,7 @@ public class ZenModeHelper { rule.condition = null; rule.conditionId = new Uri.Builder() .scheme(Condition.SCHEME) - .authority("android") + .authority(PACKAGE_ANDROID) .appendPath("implicit") .appendPath(pkg) .build(); @@ -693,7 +734,9 @@ public class ZenModeHelper { if (ruleToRemove == null) return false; if (canManageAutomaticZenRule(ruleToRemove)) { newConfig.automaticRules.remove(id); - if (ruleToRemove.getPkg() != null && !"android".equals(ruleToRemove.getPkg())) { + maybePreserveRemovedRule(newConfig, ruleToRemove, origin); + if (ruleToRemove.getPkg() != null + && !PACKAGE_ANDROID.equals(ruleToRemove.getPkg())) { for (ZenRule currRule : newConfig.automaticRules.values()) { if (currRule.getPkg() != null && currRule.getPkg().equals(ruleToRemove.getPkg())) { @@ -723,12 +766,44 @@ public class ZenModeHelper { ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i)); if (Objects.equals(rule.getPkg(), packageName) && canManageAutomaticZenRule(rule)) { newConfig.automaticRules.removeAt(i); + maybePreserveRemovedRule(newConfig, rule, origin); + } + } + // If the system is clearing all rules this means DND access is revoked or the package + // was uninstalled, so also clear the preserved-deleted rules. + if (origin == UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI) { + for (int i = newConfig.deletedRules.size() - 1; i >= 0; i--) { + ZenRule rule = newConfig.deletedRules.get(newConfig.deletedRules.keyAt(i)); + if (Objects.equals(rule.getPkg(), packageName)) { + newConfig.deletedRules.removeAt(i); + } } } return setConfigLocked(newConfig, origin, reason, null, true, callingUid); } } + private void maybePreserveRemovedRule(ZenModeConfig config, ZenRule ruleToRemove, + @ConfigChangeOrigin int origin) { + if (!Flags.modesApi()) { + return; + } + // If an app deletes a previously customized rule, keep it around to preserve + // the user's customization when/if it's recreated later. + // We don't try to preserve system-owned rules because their conditionIds (used as + // deletedRuleKey) are not stable. This is almost moot anyway because an app cannot + // delete a system-owned rule. + if (origin == UPDATE_ORIGIN_APP && !ruleToRemove.canBeUpdatedByApp() + && !PACKAGE_ANDROID.equals(ruleToRemove.pkg)) { + String deletedKey = ZenModeConfig.deletedRuleKey(ruleToRemove); + if (deletedKey != null) { + ruleToRemove.deletionInstant = Instant.now(mClock); + // Overwrites a previously-deleted rule with the same conditionId, but that's okay. + config.deletedRules.put(deletedKey, ruleToRemove); + } + } + } + void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin, int callingUid) { ZenModeConfig newConfig; @@ -919,7 +994,7 @@ public class ZenModeHelper { // These values can always be edited by the app, so we apply changes immediately. if (isNew) { rule.id = ZenModeConfig.newRuleId(); - rule.creationTime = System.currentTimeMillis(); + rule.creationTime = mClock.millis(); rule.component = automaticZenRule.getOwner(); rule.pkg = pkg; } @@ -1379,7 +1454,7 @@ public class ZenModeHelper { boolean hasDefaultRules = config.automaticRules.containsAll( ZenModeConfig.DEFAULT_RULE_IDS); - long time = System.currentTimeMillis(); + long time = Flags.modesApi() ? mClock.millis() : System.currentTimeMillis(); if (config.automaticRules != null && config.automaticRules.size() > 0) { for (ZenRule automaticRule : config.automaticRules.values()) { if (forRestore) { @@ -1419,6 +1494,12 @@ public class ZenModeHelper { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId); } + + if (Flags.modesApi() && forRestore) { + // Note: forBackup doesn't write deletedRules, but just in case. + config.deletedRules.clear(); + } + if (DEBUG) Log.d(TAG, reason); synchronized (mConfigLock) { setConfigLocked(config, null, @@ -1436,7 +1517,7 @@ public class ZenModeHelper { if (forBackup && mConfigs.keyAt(i) != userId) { continue; } - mConfigs.valueAt(i).writeXml(out, version); + mConfigs.valueAt(i).writeXml(out, version, forBackup); } } } @@ -1468,28 +1549,51 @@ public class ZenModeHelper { } /** - * Removes old rule instances whose owner is not installed. + * Cleans up obsolete rules: + * <ul> + * <li>Rule instances whose owner is not installed. + * <li>Deleted rules that were deleted more than 30 days ago. + * </ul> */ private void cleanUpZenRules() { - long currentTime = System.currentTimeMillis(); + Instant keptRuleThreshold = mClock.instant().minus(DELETED_RULE_KEPT_FOR); synchronized (mConfigLock) { final ZenModeConfig newConfig = mConfig.copy(); - if (newConfig.automaticRules != null) { - for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) { - ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i)); - if (RULE_INSTANCE_GRACE_PERIOD < (currentTime - rule.creationTime)) { - try { - if (rule.getPkg() != null) { - mPm.getPackageInfo(rule.getPkg(), PackageManager.MATCH_ANY_USER); - } - } catch (PackageManager.NameNotFoundException e) { - newConfig.automaticRules.removeAt(i); + + deleteRulesWithoutOwner(newConfig.automaticRules); + if (Flags.modesApi()) { + deleteRulesWithoutOwner(newConfig.deletedRules); + for (int i = newConfig.deletedRules.size() - 1; i >= 0; i--) { + ZenRule deletedRule = newConfig.deletedRules.valueAt(i); + if (deletedRule.deletionInstant == null + || deletedRule.deletionInstant.isBefore(keptRuleThreshold)) { + newConfig.deletedRules.removeAt(i); + } + } + } + + if (!newConfig.equals(mConfig)) { + setConfigLocked(newConfig, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "cleanUpZenRules", Process.SYSTEM_UID); + } + } + } + + private void deleteRulesWithoutOwner(ArrayMap<String, ZenRule> ruleList) { + long currentTime = Flags.modesApi() ? mClock.millis() : System.currentTimeMillis(); + if (ruleList != null) { + for (int i = ruleList.size() - 1; i >= 0; i--) { + ZenRule rule = ruleList.valueAt(i); + if (RULE_INSTANCE_GRACE_PERIOD < (currentTime - rule.creationTime)) { + try { + if (rule.getPkg() != null) { + mPm.getPackageInfo(rule.getPkg(), PackageManager.MATCH_ANY_USER); } + } catch (PackageManager.NameNotFoundException e) { + ruleList.removeAt(i); } } } - setConfigLocked(newConfig, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "cleanUpZenRules", - Process.SYSTEM_UID); } } diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java deleted file mode 100644 index 4454601f2254..000000000000 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java +++ /dev/null @@ -1,97 +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.server.pm; - -import static android.os.Process.THREAD_PRIORITY_BACKGROUND; - -import android.annotation.NonNull; -import android.app.BackgroundInstallControlManager; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IRemoteCallback; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.ServiceThread; - -public class BackgroundInstallControlCallbackHelper { - - @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; - @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId"; - private static final String TAG = "BackgroundInstallControlCallbackHelper"; - - private final Handler mHandler; - - BackgroundInstallControlCallbackHelper() { - HandlerThread backgroundThread = - new ServiceThread( - "BackgroundInstallControlCallbackHelperBg", - THREAD_PRIORITY_BACKGROUND, - true); - backgroundThread.start(); - mHandler = new Handler(backgroundThread.getLooper()); - } - - @NonNull @VisibleForTesting - final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>(); - - /** Registers callback that gets invoked upon detection of an MBA - * - * NOTE: The callback is user context agnostic and currently broadcasts to all users of other - * users app installs. This is fine because the API is for SystemServer use only. - */ - public void registerBackgroundInstallCallback(IRemoteCallback callback) { - synchronized (mCallbacks) { - mCallbacks.register(callback, null); - } - } - - /** Unregisters callback */ - public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { - synchronized (mCallbacks) { - mCallbacks.unregister(callback); - } - } - - /** - * Invokes all registered callbacks Callbacks are processed through user provided-threads and - * parameters are passed in via {@link BackgroundInstallControlManager} InstallEvent - */ - public void notifyAllCallbacks(int userId, String packageName) { - Bundle extras = new Bundle(); - extras.putCharSequence(FLAGGED_PACKAGE_NAME_KEY, packageName); - extras.putInt(FLAGGED_USER_ID_KEY, userId); - synchronized (mCallbacks) { - mHandler.post( - () -> - mCallbacks.broadcast( - callback -> { - try { - callback.sendResult(extras); - } catch (RemoteException e) { - Slog.e( - TAG, - "error detected: " + e.getLocalizedMessage(), - e); - } - })); - } - } -} diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java index 3a9dedcf2d7b..7f0aadce3143 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java @@ -16,12 +16,7 @@ package com.android.server.pm; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.QUERY_ALL_PACKAGES; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; @@ -35,7 +30,6 @@ import android.content.pm.ParceledListSlice; import android.os.Build; import android.os.Environment; import android.os.Handler; -import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.SystemClock; @@ -75,10 +69,8 @@ public class BackgroundInstallControlService extends SystemService { private static final String DISK_FILE_NAME = "states"; private static final String DISK_DIR_NAME = "bic"; - private static final String ENFORCE_PERMISSION_ERROR_MSG = - "User is not permitted to call service: "; - private static final int MAX_FOREGROUND_TIME_FRAMES_SIZE = 10; + private static final int MSG_USAGE_EVENT_RECEIVED = 0; private static final int MSG_PACKAGE_ADDED = 1; private static final int MSG_PACKAGE_REMOVED = 2; @@ -86,20 +78,19 @@ public class BackgroundInstallControlService extends SystemService { private final Context mContext; private final BinderService mBinderService; private final PackageManager mPackageManager; - // TODO migrate all internal PackageManager calls to PackageManagerInternal where possible. - // b/310983905 private final PackageManagerInternal mPackageManagerInternal; private final UsageStatsManagerInternal mUsageStatsManagerInternal; private final PermissionManagerServiceInternal mPermissionManager; private final Handler mHandler; private final File mDiskFile; + private SparseSetArray<String> mBackgroundInstalledPackages = null; - private final BackgroundInstallControlCallbackHelper mCallbackHelper; // User ID -> package name -> set of foreground time frame - private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>> - mInstallerForegroundTimeFrames = new SparseArrayMap<>(); + private final SparseArrayMap<String, + TreeSet<ForegroundTimeFrame>> mInstallerForegroundTimeFrames = + new SparseArrayMap<>(); public BackgroundInstallControlService(@NonNull Context context) { this(new InjectorImpl(context)); @@ -115,11 +106,13 @@ public class BackgroundInstallControlService extends SystemService { mHandler = new EventHandler(injector.getLooper(), this); mDiskFile = injector.getDiskFile(); mUsageStatsManagerInternal = injector.getUsageStatsManagerInternal(); - mCallbackHelper = injector.getBackgroundInstallControlCallbackHelper(); mUsageStatsManagerInternal.registerListener( (userId, event) -> - mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED, userId, 0, event) - .sendToTarget()); + mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED, + userId, + 0, + event).sendToTarget() + ); mBinderService = new BinderService(this); } @@ -133,15 +126,12 @@ public class BackgroundInstallControlService extends SystemService { @Override public ParceledListSlice<PackageInfo> getBackgroundInstalledPackages( @PackageManager.PackageInfoFlagsBits long flags, int userId) { - mService.enforceCallerQueryPackagesPermissions(); if (!Build.IS_DEBUGGABLE) { return mService.getBackgroundInstalledPackages(flags, userId); } // The debug.transparency.bg-install-apps (only works for debuggable builds) // is used to set mock list of background installed apps for testing. // The list of apps' names is delimited by ",". - // TODO: Remove after migrating test to new background install method using - // {@link BackgroundInstallControlCallbackHelperTest}.installPackage b/310983905 String propertyString = SystemProperties.get("debug.transparency.bg-install-apps"); if (TextUtils.isEmpty(propertyString)) { return mService.getBackgroundInstalledPackages(flags, userId); @@ -149,41 +139,16 @@ public class BackgroundInstallControlService extends SystemService { return mService.getMockBackgroundInstalledPackages(propertyString); } } - - @Override - public void registerBackgroundInstallCallback(IRemoteCallback callback) { - mService.enforceCallerQueryPackagesPermissions(); - mService.enforceCallerInteractCrossUserPermissions(); - mService.mCallbackHelper.registerBackgroundInstallCallback(callback); - } - - @Override - public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { - mService.enforceCallerQueryPackagesPermissions(); - mService.enforceCallerInteractCrossUserPermissions(); - mService.mCallbackHelper.unregisterBackgroundInstallCallback(callback); - } - } - - @RequiresPermission(QUERY_ALL_PACKAGES) - void enforceCallerQueryPackagesPermissions() throws SecurityException { - mContext.enforceCallingPermission(QUERY_ALL_PACKAGES, - ENFORCE_PERMISSION_ERROR_MSG + QUERY_ALL_PACKAGES); - } - - @RequiresPermission(INTERACT_ACROSS_USERS_FULL) - void enforceCallerInteractCrossUserPermissions() throws SecurityException { - mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, - ENFORCE_PERMISSION_ERROR_MSG + INTERACT_ACROSS_USERS_FULL); } @VisibleForTesting ParceledListSlice<PackageInfo> getBackgroundInstalledPackages( @PackageManager.PackageInfoFlagsBits long flags, int userId) { List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser( - PackageManager.PackageInfoFlags.of(flags), userId); + PackageManager.PackageInfoFlags.of(flags), userId); initBackgroundInstalledPackages(); + ListIterator<PackageInfo> iter = packages.listIterator(); while (iter.hasNext()) { String packageName = iter.next().packageName; @@ -205,9 +170,8 @@ public class BackgroundInstallControlService extends SystemService { List<PackageInfo> mockPackages = new ArrayList<>(); for (String name : mockPackageNames) { try { - PackageInfo packageInfo = - mPackageManager.getPackageInfo( - name, PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL)); + PackageInfo packageInfo = mPackageManager.getPackageInfo(name, + PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL)); mockPackages.add(packageInfo); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Package's PackageInfo not found " + name); @@ -228,16 +192,18 @@ public class BackgroundInstallControlService extends SystemService { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_USAGE_EVENT_RECEIVED: - mService.handleUsageEvent( - (UsageEvents.Event) msg.obj, msg.arg1 /* userId */); + case MSG_USAGE_EVENT_RECEIVED: { + mService.handleUsageEvent((UsageEvents.Event) msg.obj, msg.arg1 /* userId */); break; - case MSG_PACKAGE_ADDED: + } + case MSG_PACKAGE_ADDED: { mService.handlePackageAdd((String) msg.obj, msg.arg1 /* userId */); break; - case MSG_PACKAGE_REMOVED: + } + case MSG_PACKAGE_REMOVED: { mService.handlePackageRemove((String) msg.obj, msg.arg1 /* userId */); break; + } default: Slog.w(TAG, "Unknown message: " + msg.what); } @@ -247,9 +213,8 @@ public class BackgroundInstallControlService extends SystemService { void handlePackageAdd(String packageName, int userId) { ApplicationInfo appInfo = null; try { - appInfo = - mPackageManager.getApplicationInfoAsUser( - packageName, PackageManager.ApplicationInfoFlags.of(0), userId); + appInfo = mPackageManager.getApplicationInfoAsUser(packageName, + PackageManager.ApplicationInfoFlags.of(0), userId); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Package's appInfo not found " + packageName); return; @@ -268,18 +233,15 @@ public class BackgroundInstallControlService extends SystemService { // the installers without INSTALL_PACKAGES perm can't perform // the installation in background. So we can just filter out them. - if (mPermissionManager.checkPermission( - installerPackageName, - android.Manifest.permission.INSTALL_PACKAGES, - Context.DEVICE_ID_DEFAULT, - userId) - != PERMISSION_GRANTED) { + if (mPermissionManager.checkPermission(installerPackageName, + android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT, + userId) != PackageManager.PERMISSION_GRANTED) { return; } // convert up-time to current time. - final long installTimestamp = - System.currentTimeMillis() - (SystemClock.uptimeMillis() - appInfo.createTimestamp); + final long installTimestamp = System.currentTimeMillis() + - (SystemClock.uptimeMillis() - appInfo.createTimestamp); if (installedByAdb(initiatingPackageName) || wasForegroundInstallation(installerPackageName, userId, installTimestamp)) { @@ -288,7 +250,6 @@ public class BackgroundInstallControlService extends SystemService { initBackgroundInstalledPackages(); mBackgroundInstalledPackages.add(userId, packageName); - mCallbackHelper.notifyAllCallbacks(userId, packageName); writeBackgroundInstalledPackagesToDisk(); } @@ -298,8 +259,8 @@ public class BackgroundInstallControlService extends SystemService { return PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName); } - private boolean wasForegroundInstallation( - String installerPackageName, int userId, long installTimestamp) { + private boolean wasForegroundInstallation(String installerPackageName, + int userId, long installTimestamp) { TreeSet<BackgroundInstallControlService.ForegroundTimeFrame> foregroundTimeFrames = mInstallerForegroundTimeFrames.get(userId, installerPackageName); @@ -388,12 +349,12 @@ public class BackgroundInstallControlService extends SystemService { for (int i = 0; i < mBackgroundInstalledPackages.size(); i++) { int userId = mBackgroundInstalledPackages.keyAt(i); for (String packageName : mBackgroundInstalledPackages.get(userId)) { - long token = - protoOutputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoOutputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); protoOutputStream.write( BackgroundInstalledPackageProto.PACKAGE_NAME, packageName); - protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, userId + 1); + protoOutputStream.write( + BackgroundInstalledPackageProto.USER_ID, userId + 1); protoOutputStream.end(token); } } @@ -426,28 +387,23 @@ public class BackgroundInstallControlService extends SystemService { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = - protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoInputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = - protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = - protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - - 1; + userId = protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) - 1; break; default: - Slog.w( - TAG, - "Undefined field in proto: " - + protoInputStream.getFieldNumber()); + Slog.w(TAG, "Undefined field in proto: " + + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -476,12 +432,9 @@ public class BackgroundInstallControlService extends SystemService { if (mInstallerForegroundTimeFrames.contains(userId, pkgName)) { return true; } - return mPermissionManager.checkPermission( - pkgName, - android.Manifest.permission.INSTALL_PACKAGES, - Context.DEVICE_ID_DEFAULT, - userId) - == PERMISSION_GRANTED; + return mPermissionManager.checkPermission(pkgName, + android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT, + userId) == PackageManager.PERMISSION_GRANTED; } @Override @@ -495,22 +448,21 @@ public class BackgroundInstallControlService extends SystemService { publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService); } - mPackageManagerInternal.getPackageList( - new PackageManagerInternal.PackageListObserver() { - @Override - public void onPackageAdded(String packageName, int uid) { - final int userId = UserHandle.getUserId(uid); - mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName) - .sendToTarget(); - } + mPackageManagerInternal.getPackageList(new PackageManagerInternal.PackageListObserver() { + @Override + public void onPackageAdded(String packageName, int uid) { + final int userId = UserHandle.getUserId(uid); + mHandler.obtainMessage(MSG_PACKAGE_ADDED, + userId, 0, packageName).sendToTarget(); + } - @Override - public void onPackageRemoved(String packageName, int uid) { - final int userId = UserHandle.getUserId(uid); - mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName) - .sendToTarget(); - } - }); + @Override + public void onPackageRemoved(String packageName, int uid) { + final int userId = UserHandle.getUserId(uid); + mHandler.obtainMessage(MSG_PACKAGE_REMOVED, + userId, 0, packageName).sendToTarget(); + } + }); } // The foreground time frame (ForegroundTimeFrame) represents the period @@ -566,7 +518,7 @@ public class BackgroundInstallControlService extends SystemService { } /** - * Dependency injector for {@link BackgroundInstallControlService}. + * Dependency injector for {@link #BackgroundInstallControlService)}. */ interface Injector { Context getContext(); @@ -582,8 +534,6 @@ public class BackgroundInstallControlService extends SystemService { Looper getLooper(); File getDiskFile(); - - BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper(); } private static final class InjectorImpl implements Injector { @@ -620,11 +570,11 @@ public class BackgroundInstallControlService extends SystemService { @Override public Looper getLooper() { - ServiceThread serviceThread = - new ServiceThread( - TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */); + ServiceThread serviceThread = new ServiceThread(TAG, + android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */); serviceThread.start(); return serviceThread.getLooper(); + } @Override @@ -633,10 +583,5 @@ public class BackgroundInstallControlService extends SystemService { File file = new File(dir, DISK_FILE_NAME); return file; } - - @Override - public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() { - return new BackgroundInstallControlCallbackHelper(); - } } } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 127bf495d2ac..991555495ad2 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -1610,21 +1610,117 @@ public class LauncherAppsService extends SystemService { "Can't access AppMarketActivity for another user")) { return null; } + final int callingUser = getCallingUserId(); final long identity = Binder.clearCallingIdentity(); + try { - // TODO(b/316118005): Add code to launch the app installer for the packageName. - Intent appMarketIntent = new Intent(Intent.ACTION_MAIN); - appMarketIntent.addCategory(Intent.CATEGORY_APP_MARKET); - final PendingIntent pi = PendingIntent.getActivityAsUser( - mContext, /* requestCode */ 0, appMarketIntent, PendingIntent.FLAG_ONE_SHOT - | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT, - /* options */ null, user); - return pi == null ? null : pi.getIntentSender(); + if (packageName == null) { + return buildAppMarketIntentSenderForUser(user); + } + + String installerPackageName = getInstallerPackage(packageName, callingUser); + if (installerPackageName == null + || mPackageManagerInternal.getPackageUid( + installerPackageName, /* flags= */ 0, user.getIdentifier()) + < 0) { + if (DEBUG) { + Log.d( + TAG, + "Can't find installer for " + + packageName + + " in user: " + + user.getIdentifier()); + } + return buildAppMarketIntentSenderForUser(user); + } + + Intent packageInfoIntent = + buildMarketPackageInfoIntent( + packageName, installerPackageName, callingPackage); + if (mPackageManagerInternal + .queryIntentActivities( + packageInfoIntent, + packageInfoIntent.resolveTypeIfNeeded( + mContext.getContentResolver()), + PackageManager.MATCH_ALL, + Process.myUid(), + user.getIdentifier()) + .isEmpty()) { + if (DEBUG) { + Log.d( + TAG, + "Can't resolve package info intent for package " + + packageName + + " and installer: " + + installerPackageName); + } + return buildAppMarketIntentSenderForUser(user); + } + + return buildIntentSenderForUser(packageInfoIntent, user); } finally { Binder.restoreCallingIdentity(identity); } } + @Nullable + private IntentSender buildAppMarketIntentSenderForUser(@NonNull UserHandle user) { + Intent appMarketIntent = new Intent(Intent.ACTION_MAIN); + appMarketIntent.addCategory(Intent.CATEGORY_APP_MARKET); + return buildIntentSenderForUser(appMarketIntent, user); + } + + @Nullable + private IntentSender buildIntentSenderForUser( + @NonNull Intent intent, @NonNull UserHandle user) { + final PendingIntent pi = + PendingIntent.getActivityAsUser( + mContext, + /* requestCode */ 0, + intent, + PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_IMMUTABLE + | PendingIntent.FLAG_CANCEL_CURRENT, + /* options */ null, + user); + return pi == null ? null : pi.getIntentSender(); + } + + @Nullable + private String getInstallerPackage(@NonNull String packageName, int callingUserId) { + String installerPackageName = null; + try { + installerPackageName = + mIPM.getInstallSourceInfo(packageName, callingUserId) + .getInstallingPackageName(); + } catch (RemoteException re) { + Slog.e(TAG, "Couldn't find installer for " + packageName, re); + } + + return installerPackageName; + } + + @NonNull + private Intent buildMarketPackageInfoIntent( + @NonNull String packageName, + @NonNull String installerPackageName, + @NonNull String callingPackage) { + return new Intent(Intent.ACTION_VIEW) + .setData( + new Uri.Builder() + .scheme("market") + .authority("details") + .appendQueryParameter("id", packageName) + .build()) + .putExtra( + Intent.EXTRA_REFERRER, + new Uri.Builder() + .scheme("android-app") + .authority(callingPackage) + .build()) + .setPackage(installerPackageName); + } + @Override public void startActivityAsUser(IApplicationThread caller, String callingPackage, String callingFeatureId, ComponentName component, Rect sourceBounds, diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 3adeb4b5925f..446c6293aa35 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -77,6 +77,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.LocaleList; import android.os.Looper; @@ -113,7 +114,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.infra.AndroidFuture; import com.android.internal.logging.MetricsLogger; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; @@ -485,7 +485,14 @@ public class ShortcutService extends IShortcutService.Stub { } public ShortcutService(Context context) { - this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false); + this(context, getBgLooper(), /*onyForPackgeManagerApis*/ false); + } + + private static Looper getBgLooper() { + final HandlerThread handlerThread = new HandlerThread("shortcut", + android.os.Process.THREAD_PRIORITY_BACKGROUND); + handlerThread.start(); + return handlerThread.getLooper(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index c0e33084f9c4..b384725711c4 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -60,6 +60,7 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.Surface; +import com.android.internal.annotations.GuardedBy; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; @@ -88,15 +89,25 @@ class TvInputHardwareManager implements TvInputHal.Callback { private final Context mContext; private final Listener mListener; private final TvInputHal mHal = new TvInputHal(this); + + private final Object mLock = new Object(); + + @GuardedBy("mLock") private final SparseArray<Connection> mConnections = new SparseArray<>(); + @GuardedBy("mLock") private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>(); + @GuardedBy("mLock") private final List<HdmiDeviceInfo> mHdmiDeviceList = new ArrayList<>(); /* A map from a device ID to the matching TV input ID. */ + @GuardedBy("mLock") private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>(); /* A map from a HDMI logical address to the matching TV input ID. */ + @GuardedBy("mLock") private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>(); + @GuardedBy("mLock") private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>(); /* A map from a HDMI input parent ID to the related input IDs. */ + @GuardedBy("mLock") private final Map<String, List<String>> mHdmiParentInputMap = new ArrayMap<>(); private final AudioManager mAudioManager; @@ -114,16 +125,16 @@ class TvInputHardwareManager implements TvInputHal.Callback { private int mCurrentIndex = 0; private int mCurrentMaxIndex = 0; + @GuardedBy("mLock") private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray(); + @GuardedBy("mLock") private final List<Message> mPendingHdmiDeviceEvents = new ArrayList<>(); - + @GuardedBy("mLock") private final List<Message> mPendingTvinputInfoEvents = new ArrayList<>(); // Calls to mListener should happen here. private final Handler mHandler = new ListenerHandler(); - private final Object mLock = new Object(); - public TvInputHardwareManager(Context context, Listener listener) { mContext = context; mListener = listener; @@ -141,7 +152,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { hdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener); hdmiControlService.addSystemAudioModeChangeListener( mHdmiSystemAudioModeChangeListener); - mHdmiDeviceList.addAll(hdmiControlService.getInputDevices()); + synchronized (mLock) { + mHdmiDeviceList.addAll(hdmiControlService.getInputDevices()); + } } catch (RemoteException e) { Slog.w(TAG, "Error registering listeners to HdmiControlService:", e); } @@ -172,6 +185,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } + @GuardedBy("mLock") private void buildHardwareListLocked() { mHardwareList.clear(); for (int i = 0; i < mConnections.size(); ++i) { @@ -301,6 +315,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } + @GuardedBy("mLock") private boolean checkUidChangedLocked( Connection connection, int callingUid, int resolvedUserId) { Integer connectionCallingUid = connection.getCallingUidLocked(); @@ -496,6 +511,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } + @GuardedBy("mLock") private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) { for (TvInputHardwareInfo hardwareInfo : mHardwareList) { if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI @@ -506,6 +522,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { return null; } + @GuardedBy("mLock") private int findDeviceIdForInputIdLocked(String inputId) { for (int i = 0; i < mConnections.size(); ++i) { int key = mConnections.keyAt(i); @@ -597,6 +614,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { return false; } + @GuardedBy("mLock") private void processPendingHdmiDeviceEventsLocked() { for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) { Message msg = it.next(); @@ -611,6 +629,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } + @GuardedBy("mLock") private void processPendingTvInputInfoEventsLocked() { for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext(); ) { Message msg = it.next(); @@ -748,6 +767,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { // *Locked methods assume TvInputHardwareManager.mLock is held. + @GuardedBy("mLock") public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback, TvInputInfo info, Integer callingUid, Integer resolvedUserId, ResourceClientProfile profile) { @@ -776,50 +796,62 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } + @GuardedBy("mLock") public void updateConfigsLocked(TvStreamConfig[] configs) { mConfigs = configs; } + @GuardedBy("mLock") public TvInputHardwareInfo getHardwareInfoLocked() { return mHardwareInfo; } + @GuardedBy("mLock") public TvInputInfo getInfoLocked() { return mInfo; } + @GuardedBy("mLock") public ITvInputHardware getHardwareLocked() { return mHardware; } + @GuardedBy("mLock") public TvInputHardwareImpl getHardwareImplLocked() { return mHardware; } + @GuardedBy("mLock") public ITvInputHardwareCallback getCallbackLocked() { return mCallback; } + @GuardedBy("mLock") public TvStreamConfig[] getConfigsLocked() { return mConfigs; } + @GuardedBy("mLock") public Integer getCallingUidLocked() { return mCallingUid; } + @GuardedBy("mLock") public Integer getResolvedUserIdLocked() { return mResolvedUserId; } + @GuardedBy("mLock") public void setOnFirstFrameCapturedLocked(Runnable runnable) { mOnFirstFrameCaptured = runnable; } + @GuardedBy("mLock") public Runnable getOnFirstFrameCapturedLocked() { return mOnFirstFrameCaptured; } + @GuardedBy("mLock") public ResourceClientProfile getResourceClientProfileLocked() { return mResourceClientProfile; } @@ -844,6 +876,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { + " }"; } + @GuardedBy("mLock") public boolean updateCableConnectionStatusLocked(int cableConnectionStatus) { // Update connection status only if it's not default value if (cableConnectionStatus != TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN @@ -855,10 +888,12 @@ class TvInputHardwareManager implements TvInputHal.Callback { return mIsCableConnectionStatusUpdated; } + @GuardedBy("mLock") private int getConfigsLengthLocked() { return mConfigs == null ? 0 : mConfigs.length; } + @GuardedBy("mLock") private int getInputStateLocked() { int configsLength = getConfigsLengthLocked(); if (configsLength > 0) { @@ -880,7 +915,6 @@ class TvInputHardwareManager implements TvInputHal.Callback { private class TvInputHardwareImpl extends ITvInputHardware.Stub { private final TvInputHardwareInfo mInfo; - private boolean mReleased = false; private final Object mImplLock = new Object(); private final AudioManager.OnAudioPortUpdateListener mAudioListener = @@ -909,28 +943,44 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } }; + @GuardedBy("mImplLock") + private boolean mReleased = false; + @GuardedBy("mImplLock") private int mOverrideAudioType = AudioManager.DEVICE_NONE; + @GuardedBy("mImplLock") private String mOverrideAudioAddress = ""; + @GuardedBy("mImplLock") private AudioDevicePort mAudioSource; + @GuardedBy("mImplLock") private List<AudioDevicePort> mAudioSink = new ArrayList<>(); + @GuardedBy("mImplLock") private AudioPatch mAudioPatch = null; // Set to an invalid value for a volume, so that current volume can be applied at the // first call to updateAudioConfigLocked(). + @GuardedBy("mImplLock") private float mCommittedVolume = -1f; + @GuardedBy("mImplLock") private float mSourceVolume = 0.0f; + @GuardedBy("mImplLock") private TvStreamConfig mActiveConfig = null; + @GuardedBy("mImplLock") private int mDesiredSamplingRate = 0; + @GuardedBy("mImplLock") private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT; + @GuardedBy("mImplLock") private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT; public TvInputHardwareImpl(TvInputHardwareInfo info) { mInfo = info; mAudioManager.registerAudioPortUpdateListener(mAudioListener); if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) { - mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress()); - findAudioSinkFromAudioPolicy(mAudioSink); + synchronized (mImplLock) { + mAudioSource = + findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress()); + findAudioSinkFromAudioPolicy(mAudioSink); + } } } @@ -1025,6 +1075,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { /** * Update audio configuration (source, sink, patch) all up to current state. */ + @GuardedBy("mImplLock") private void updateAudioConfigLocked() { boolean sinkUpdated = updateAudioSinkLocked(); boolean sourceUpdated = updateAudioSourceLocked(); @@ -1204,6 +1255,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } + @GuardedBy("mImplLock") private boolean updateAudioSourceLocked() { if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) { return false; @@ -1214,6 +1266,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { : !mAudioSource.equals(previousSource); } + @GuardedBy("mImplLock") private boolean updateAudioSinkLocked() { if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) { return false; diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java index 6ce868540070..ed04e5fde024 100644 --- a/services/core/java/com/android/server/vcn/VcnContext.java +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -34,6 +34,7 @@ public class VcnContext { @NonNull private final Looper mLooper; @NonNull private final VcnNetworkProvider mVcnNetworkProvider; @NonNull private final FeatureFlags mFeatureFlags; + @NonNull private final com.android.net.flags.FeatureFlags mCoreNetFeatureFlags; private final boolean mIsInTestMode; public VcnContext( @@ -48,6 +49,7 @@ public class VcnContext { // Auto-generated class mFeatureFlags = new FeatureFlagsImpl(); + mCoreNetFeatureFlags = new com.android.net.flags.FeatureFlagsImpl(); } @NonNull @@ -69,6 +71,14 @@ public class VcnContext { return mIsInTestMode; } + public boolean isFlagNetworkMetricMonitorEnabled() { + return mFeatureFlags.networkMetricMonitor(); + } + + public boolean isFlagIpSecTransformStateEnabled() { + return mCoreNetFeatureFlags.ipsecTransformState(); + } + @NonNull public FeatureFlags getFeatureFlags() { return mFeatureFlags; diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java new file mode 100644 index 000000000000..5f4852f77727 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java @@ -0,0 +1,387 @@ +/* + * 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.server.vcn.routeselection; + +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.IpSecTransformState; +import android.net.Network; +import android.net.vcn.VcnManager; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.OutcomeReceiver; +import android.os.PowerManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.VcnContext; + +import java.util.BitSet; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * IpSecPacketLossDetector is responsible for continuously monitoring IPsec packet loss + * + * <p>When the packet loss rate surpass the threshold, IpSecPacketLossDetector will report it to the + * caller + * + * <p>IpSecPacketLossDetector will start monitoring when the network being monitored is selected AND + * an inbound IpSecTransform has been applied to this network. + * + * <p>This class is flag gated by "network_metric_monitor" and "ipsec_tramsform_state" + */ +public class IpSecPacketLossDetector extends NetworkMetricMonitor { + private static final String TAG = IpSecPacketLossDetector.class.getSimpleName(); + + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PACKET_LOSS_UNAVALAIBLE = -1; + + // For VoIP, losses between 5% and 10% of the total packet stream will affect the quality + // significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and + // Security"). For audio and video streaming, above 10-12% packet loss is unacceptable (as per + // "ICTP-SDU: About PingER"). Thus choose 12% as a conservative default threshold to declare a + // validation failure. + private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT = 12; + + private static final int POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT = 20; + + private long mPollIpSecStateIntervalMs; + private final int mPacketLossRatePercentThreshold; + + @NonNull private final Handler mHandler; + @NonNull private final PowerManager mPowerManager; + @NonNull private final Object mCancellationToken = new Object(); + @NonNull private final PacketLossCalculator mPacketLossCalculator; + + @Nullable private IpSecTransformWrapper mInboundTransform; + @Nullable private IpSecTransformState mLastIpSecTransformState; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public IpSecPacketLossDetector( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback, + @NonNull Dependencies deps) + throws IllegalAccessException { + super(vcnContext, network, carrierConfig, callback); + + Objects.requireNonNull(deps, "Missing deps"); + + if (!vcnContext.isFlagIpSecTransformStateEnabled()) { + // Caller error + logWtf("ipsecTransformState flag disabled"); + throw new IllegalAccessException("ipsecTransformState flag disabled"); + } + + mHandler = new Handler(getVcnContext().getLooper()); + + mPowerManager = getVcnContext().getContext().getSystemService(PowerManager.class); + + mPacketLossCalculator = deps.getPacketLossCalculator(); + + mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); + mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig); + + // Register for system broadcasts to monitor idle mode change + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + getVcnContext() + .getContext() + .registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals( + intent.getAction()) + && mPowerManager.isDeviceIdleMode()) { + mLastIpSecTransformState = null; + } + } + }, + intentFilter, + null /* broadcastPermission not required */, + mHandler); + } + + public IpSecPacketLossDetector( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback) + throws IllegalAccessException { + this(vcnContext, network, carrierConfig, callback, new Dependencies()); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + public PacketLossCalculator getPacketLossCalculator() { + return new PacketLossCalculator(); + } + } + + private static long getPollIpSecStateIntervalMs( + @Nullable PersistableBundleWrapper carrierConfig) { + final int seconds; + + if (carrierConfig != null) { + seconds = + carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY, + POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT); + } else { + seconds = POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT; + } + + return TimeUnit.SECONDS.toMillis(seconds); + } + + private static int getPacketLossRatePercentThreshold( + @Nullable PersistableBundleWrapper carrierConfig) { + if (carrierConfig != null) { + return carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY, + IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT); + } + return IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT; + } + + @Override + protected void onSelectedUnderlyingNetworkChanged() { + if (!isSelectedUnderlyingNetwork()) { + mInboundTransform = null; + stop(); + } + + // No action when the underlying network got selected. Wait for the inbound transform to + // start the monitor + } + + @Override + public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inboundTransform) { + Objects.requireNonNull(inboundTransform, "inboundTransform is null"); + + if (Objects.equals(inboundTransform, mInboundTransform)) { + return; + } + + if (!isSelectedUnderlyingNetwork()) { + logWtf("setInboundTransform called but network not selected"); + return; + } + + // When multiple parallel inbound transforms are created, NetworkMetricMonitor will be + // enabled on the last one as a sample + mInboundTransform = inboundTransform; + start(); + } + + @Override + public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) { + // The already scheduled event will not be affected. The followup events will be scheduled + // with the new interval + mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); + } + + @Override + protected void start() { + super.start(); + clearTransformStateAndPollingEvents(); + mHandler.postDelayed(new PollIpSecStateRunnable(), mCancellationToken, 0L); + } + + @Override + public void stop() { + super.stop(); + clearTransformStateAndPollingEvents(); + } + + private void clearTransformStateAndPollingEvents() { + mHandler.removeCallbacksAndEqualMessages(mCancellationToken); + mLastIpSecTransformState = null; + } + + @Override + public void close() { + super.close(); + + if (mInboundTransform != null) { + mInboundTransform.close(); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + @Nullable + public IpSecTransformState getLastTransformState() { + return mLastIpSecTransformState; + } + + @VisibleForTesting(visibility = Visibility.PROTECTED) + @Nullable + public IpSecTransformWrapper getInboundTransformInternal() { + return mInboundTransform; + } + + private class PollIpSecStateRunnable implements Runnable { + @Override + public void run() { + if (!isStarted()) { + logWtf("Monitor stopped but PollIpSecStateRunnable not removed from Handler"); + return; + } + + getInboundTransformInternal() + .getIpSecTransformState( + new HandlerExecutor(mHandler), new IpSecTransformStateReceiver()); + + // Schedule for next poll + mHandler.postDelayed( + new PollIpSecStateRunnable(), mCancellationToken, mPollIpSecStateIntervalMs); + } + } + + private class IpSecTransformStateReceiver + implements OutcomeReceiver<IpSecTransformState, RuntimeException> { + @Override + public void onResult(@NonNull IpSecTransformState state) { + getVcnContext().ensureRunningOnLooperThread(); + + if (!isStarted()) { + return; + } + + onIpSecTransformStateReceived(state); + } + + @Override + public void onError(@NonNull RuntimeException error) { + getVcnContext().ensureRunningOnLooperThread(); + + // Nothing we can do here + logW("TransformStateReceiver#onError " + error.toString()); + } + } + + private void onIpSecTransformStateReceived(@NonNull IpSecTransformState state) { + if (mLastIpSecTransformState == null) { + // This is first time to poll the state + mLastIpSecTransformState = state; + return; + } + + final int packetLossRate = + mPacketLossCalculator.getPacketLossRatePercentage( + mLastIpSecTransformState, state, getLogPrefix()); + + if (packetLossRate == PACKET_LOSS_UNAVALAIBLE) { + return; + } + + final String logMsg = + "packetLossRate: " + + packetLossRate + + "% in the past " + + (state.getTimestamp() - mLastIpSecTransformState.getTimestamp()) + + "ms"; + + mLastIpSecTransformState = state; + if (packetLossRate < mPacketLossRatePercentThreshold) { + logV(logMsg); + onValidationResultReceivedInternal(false /* isFailed */); + } else { + logInfo(logMsg); + onValidationResultReceivedInternal(true /* isFailed */); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class PacketLossCalculator { + /** Calculate the packet loss rate between two timestamps */ + public int getPacketLossRatePercentage( + @NonNull IpSecTransformState oldState, + @NonNull IpSecTransformState newState, + String logPrefix) { + logVIpSecTransform("oldState", oldState, logPrefix); + logVIpSecTransform("newState", newState, logPrefix); + + final int replayWindowSize = oldState.getReplayBitmap().length * 8; + final long oldSeqHi = oldState.getRxHighestSequenceNumber(); + final long oldSeqLow = Math.max(0L, oldSeqHi - replayWindowSize + 1); + final long newSeqHi = newState.getRxHighestSequenceNumber(); + final long newSeqLow = Math.max(0L, newSeqHi - replayWindowSize + 1); + + if (oldSeqHi == newSeqHi || newSeqHi < replayWindowSize) { + // The replay window did not proceed and all packets might have been delivered out + // of order + return PACKET_LOSS_UNAVALAIBLE; + } + + // Get the expected packet count by assuming there is no packet loss. In this case, SA + // should receive all packets whose sequence numbers are smaller than the lower bound of + // the replay window AND the packets received within the window. + // When the lower bound is 0, it's not possible to tell whether packet with seqNo 0 is + // received or not. For simplicity just assume that packet is received. + final long newExpectedPktCnt = newSeqLow + getPacketCntInReplayWindow(newState); + final long oldExpectedPktCnt = oldSeqLow + getPacketCntInReplayWindow(oldState); + + final long expectedPktCntDiff = newExpectedPktCnt - oldExpectedPktCnt; + final long actualPktCntDiff = newState.getPacketCount() - oldState.getPacketCount(); + + logV( + TAG, + logPrefix + + " expectedPktCntDiff: " + + expectedPktCntDiff + + " actualPktCntDiff: " + + actualPktCntDiff); + + if (expectedPktCntDiff < 0 + || expectedPktCntDiff == 0 + || actualPktCntDiff < 0 + || actualPktCntDiff > expectedPktCntDiff) { + logWtf(TAG, "Impossible values for expectedPktCntDiff or" + " actualPktCntDiff"); + return PACKET_LOSS_UNAVALAIBLE; + } + + return 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff); + } + } + + private static void logVIpSecTransform( + String transformTag, IpSecTransformState state, String logPrefix) { + final String stateString = + " seqNo: " + + state.getRxHighestSequenceNumber() + + " | pktCnt: " + + state.getPacketCount() + + " | pktCntInWindow: " + + getPacketCntInReplayWindow(state); + logV(TAG, logPrefix + " " + transformTag + stateString); + } + + /** Get the number of received packets within the replay window */ + private static long getPacketCntInReplayWindow(@NonNull IpSecTransformState state) { + return BitSet.valueOf(state.getReplayBitmap()).cardinality(); + } +} diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java new file mode 100644 index 000000000000..a79f188713e1 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java @@ -0,0 +1,269 @@ +/* + * 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.server.vcn.routeselection; + +import static com.android.server.VcnManagementService.LOCAL_LOG; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.IpSecTransform; +import android.net.IpSecTransformState; +import android.net.Network; +import android.os.OutcomeReceiver; +import android.util.CloseGuard; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.VcnContext; + +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * NetworkMetricMonitor is responsible for managing metric monitoring and tracking validation + * results. + * + * <p>This class is flag gated by "network_metric_monitor" + */ +public abstract class NetworkMetricMonitor implements AutoCloseable { + private static final String TAG = NetworkMetricMonitor.class.getSimpleName(); + + private static final boolean VDBG = false; // STOPSHIP: if true + + @NonNull private final CloseGuard mCloseGuard = new CloseGuard(); + + @NonNull private final VcnContext mVcnContext; + @NonNull private final Network mNetwork; + @NonNull private final NetworkMetricMonitorCallback mCallback; + + private boolean mIsSelectedUnderlyingNetwork; + private boolean mIsStarted; + private boolean mIsValidationFailed; + + protected NetworkMetricMonitor( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback) + throws IllegalAccessException { + if (!vcnContext.isFlagNetworkMetricMonitorEnabled()) { + // Caller error + logWtf("networkMetricMonitor flag disabled"); + throw new IllegalAccessException("networkMetricMonitor flag disabled"); + } + + mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); + mNetwork = Objects.requireNonNull(network, "Missing network"); + mCallback = Objects.requireNonNull(callback, "Missing callback"); + + mIsSelectedUnderlyingNetwork = false; + mIsStarted = false; + mIsValidationFailed = false; + } + + /** Callback to notify caller of the validation result */ + public interface NetworkMetricMonitorCallback { + /** Called when there is a validation result is ready */ + void onValidationResultReceived(); + } + + /** + * Start monitoring + * + * <p>This method might be called on a an already started monitor for updating monitor + * properties (e.g. IpSecTransform, carrier config) + * + * <p>Subclasses MUST call super.start() when overriding this method + */ + protected void start() { + mIsStarted = true; + } + + /** + * Stop monitoring + * + * <p>Subclasses MUST call super.stop() when overriding this method + */ + public void stop() { + mIsValidationFailed = false; + mIsStarted = false; + } + + /** Called by the subclasses when the validation result is ready */ + protected void onValidationResultReceivedInternal(boolean isFailed) { + mIsValidationFailed = isFailed; + mCallback.onValidationResultReceived(); + } + + /** Called when the underlying network changes to selected or unselected */ + protected abstract void onSelectedUnderlyingNetworkChanged(); + + /** + * Mark the network being monitored selected or unselected + * + * <p>Subclasses MUST call super when overriding this method + */ + public void setIsSelectedUnderlyingNetwork(boolean isSelectedUnderlyingNetwork) { + if (mIsSelectedUnderlyingNetwork == isSelectedUnderlyingNetwork) { + return; + } + + mIsSelectedUnderlyingNetwork = isSelectedUnderlyingNetwork; + onSelectedUnderlyingNetworkChanged(); + } + + /** Wrapper that allows injection for testing purposes */ + @VisibleForTesting(visibility = Visibility.PROTECTED) + public static class IpSecTransformWrapper { + @NonNull public final IpSecTransform ipSecTransform; + + public IpSecTransformWrapper(@NonNull IpSecTransform ipSecTransform) { + this.ipSecTransform = ipSecTransform; + } + + /** Poll an IpSecTransformState */ + public void getIpSecTransformState( + @NonNull Executor executor, + @NonNull OutcomeReceiver<IpSecTransformState, RuntimeException> callback) { + ipSecTransform.getIpSecTransformState(executor, callback); + } + + /** Close this instance and release the underlying resources */ + public void close() { + ipSecTransform.close(); + } + + @Override + public int hashCode() { + return Objects.hash(ipSecTransform); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IpSecTransformWrapper)) { + return false; + } + + final IpSecTransformWrapper other = (IpSecTransformWrapper) o; + + return Objects.equals(ipSecTransform, other.ipSecTransform); + } + } + + /** Set the IpSecTransform that applied to the Network being monitored */ + public void setInboundTransform(@NonNull IpSecTransform inTransform) { + setInboundTransformInternal(new IpSecTransformWrapper(inTransform)); + } + + /** + * Set the IpSecTransform that applied to the Network being monitored * + * + * <p>Subclasses MUST call super when overriding this method + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inTransform) { + // Subclasses MUST override it if they care + } + + /** Update the carrierconfig */ + public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) { + // Subclasses MUST override it if they care + } + + public boolean isValidationFailed() { + return mIsValidationFailed; + } + + public boolean isSelectedUnderlyingNetwork() { + return mIsSelectedUnderlyingNetwork; + } + + public boolean isStarted() { + return mIsStarted; + } + + @NonNull + public VcnContext getVcnContext() { + return mVcnContext; + } + + // Override methods for AutoCloseable. Subclasses MUST call super when overriding this method + @Override + public void close() { + mCloseGuard.close(); + + stop(); + } + + // Override #finalize() to use closeGuard for flagging that #close() was not called + @SuppressWarnings("Finalize") + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } finally { + super.finalize(); + } + } + + private String getClassName() { + return this.getClass().getSimpleName(); + } + + protected String getLogPrefix() { + return " [Network " + mNetwork + "] "; + } + + protected void logV(String msg) { + if (VDBG) { + Slog.v(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[VERBOSE ] " + getClassName() + getLogPrefix() + msg); + } + } + + protected void logInfo(String msg) { + Slog.i(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[INFO ] " + getClassName() + getLogPrefix() + msg); + } + + protected void logW(String msg) { + Slog.w(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[WARN ] " + getClassName() + getLogPrefix() + msg); + } + + protected void logWtf(String msg) { + Slog.wtf(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[WTF ] " + getClassName() + getLogPrefix() + msg); + } + + protected static void logV(String className, String msgWithPrefix) { + if (VDBG) { + Slog.wtf(className, msgWithPrefix); + LOCAL_LOG.log("[VERBOSE ] " + className + msgWithPrefix); + } + } + + protected static void logWtf(String className, String msgWithPrefix) { + Slog.wtf(className, msgWithPrefix); + LOCAL_LOG.log("[WTF ] " + className + msgWithPrefix); + } +} diff --git a/services/core/java/com/android/server/wearable/OWNERS b/services/core/java/com/android/server/wearable/OWNERS index 073e2d79850b..eca48b742cef 100644 --- a/services/core/java/com/android/server/wearable/OWNERS +++ b/services/core/java/com/android/server/wearable/OWNERS @@ -1,3 +1 @@ -charliewang@google.com -oni@google.com -volnov@google.com
\ No newline at end of file +include /core/java/android/app/wearable/OWNERS
\ No newline at end of file diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java index cd48f5d527c1..106be5f124a0 100644 --- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java +++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java @@ -218,7 +218,7 @@ public class WearableSensingManagerService extends PersistableBundle data, SharedMemory sharedMemory, RemoteCallback callback) { - Slog.i(TAG, "WearableSensingManagerInternal provideData."); + Slog.d(TAG, "WearableSensingManagerInternal provideData."); Objects.requireNonNull(data); Objects.requireNonNull(callback); mContext.enforceCallingOrSelfPermission( diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index b94206dd700a..8aaf76a165ab 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -1311,7 +1311,7 @@ class BackNavigationController { Rect insets; if (mainWindow != null) { insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets( - mBounds, WindowInsets.Type.systemBars(), + mBounds, WindowInsets.Type.tappableElement(), false /* ignoreVisibility */).toRect(); InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets()); } else { @@ -1596,7 +1596,9 @@ class BackNavigationController { // skip commitVisibility call in setVisibility cause the activity won't visible here. // Call it again to make sure the activity could be visible while handling the pending // animation. - activity.commitVisibility(true, true); + // Do not performLayout during prepare animation, because it could cause focus window + // change. Let that happen after the BackNavigationInfo has returned to shell. + activity.commitVisibility(true, false /* performLayout */); activity.mTransitionController.mSnapshotController .mActivitySnapshotController.addOnBackPressedActivity(activity); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 6033220e260d..02b3f15979ce 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -808,7 +808,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mAtmService.mTaskFragmentOrganizerController.dispatchPendingEvents(); mWmService.mSyncEngine.onSurfacePlacement(); - mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml index 070bd4b1c5a9..2ccd1e4c00c7 100644 --- a/services/core/lint-baseline.xml +++ b/services/core/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 7.2.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -145,4 +145,4 @@ line="7158"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java index 69a5e5c3a901..db985fd16749 100644 --- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java @@ -50,7 +50,8 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta long startedTimestamp) { super(context, sessionCallback, lock, userId, callingUid, request, callback, RequestInfo.TYPE_UNDEFINED, - callingAppInfo, enabledProviders, cancellationSignal, startedTimestamp); + callingAppInfo, enabledProviders, cancellationSignal, startedTimestamp, + /*shouldBindClientToDeath=*/ true); } /** diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java index 31409ab1de4b..b24accbe3231 100644 --- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java @@ -63,7 +63,8 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR long startedTimestamp) { super(context, sessionCallback, lock, userId, callingUid, request, callback, RequestInfo.TYPE_CREATE, - callingAppInfo, enabledProviders, cancellationSignal, startedTimestamp); + callingAppInfo, enabledProviders, cancellationSignal, startedTimestamp, + /*shouldBindClientToDeath=*/ true); mRequestSessionMetric.collectCreateFlowInitialMetricInfo( /*origin=*/request.getOrigin() != null, request); mPrimaryProviders = primaryProviders; diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java index 0f914c32346d..0187ce8140f5 100644 --- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java @@ -65,10 +65,13 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ IAutoFillManagerClient autoFillCallback) { super(context, sessionCallback, lock, userId, callingUid, request, callback, RequestInfo.TYPE_GET, callingAppInfo, enabledProviders, - cancellationSignal, 0L); + cancellationSignal, 0L, /*shouldBindClientToDeath=*/ false); mAutoFillCallback = autoFillCallback; mAutofillSessionId = request.getData().getInt(SESSION_ID_KEY, -1); mAutofillRequestId = request.getData().getInt(REQUEST_ID_KEY, -1); + if (mAutoFillCallback != null) { + setUpClientCallbackListener(mAutoFillCallback.asBinder()); + } } /** @@ -144,17 +147,27 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ @Override public void onFinalErrorReceived(ComponentName componentName, String errorType, String message) { - // Not applicable for session without UI + respondToClientWithErrorAndFinish(errorType, message); } @Override public void onUiCancellation(boolean isUserCancellation) { - // Not applicable for session without UI + String exception = GetCandidateCredentialsException.TYPE_USER_CANCELED; + String message = "User cancelled the selector"; + if (!isUserCancellation) { + exception = GetCandidateCredentialsException.TYPE_INTERRUPTED; + message = "The UI was interrupted - please try again."; + } + mRequestSessionMetric.collectFrameworkException(exception); + respondToClientWithErrorAndFinish(exception, message); } @Override public void onUiSelectorInvocationFailure() { - // Not applicable for session without UI + String exception = GetCandidateCredentialsException.TYPE_NO_CREDENTIAL; + mRequestSessionMetric.collectFrameworkException(exception); + respondToClientWithErrorAndFinish(exception, + "No credentials available."); } @Override diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index 3f57c804cba0..49ea19a6f098 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -57,7 +57,7 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest, long startedTimestamp) { super(context, sessionCallback, lock, userId, callingUid, request, callback, getRequestInfoFromRequest(request), callingAppInfo, enabledProviders, - cancellationSignal, startedTimestamp); + cancellationSignal, startedTimestamp, /*shouldBindClientToDeath=*/ true); mRequestSessionMetric.collectGetFlowInitialMetricInfo(request); } diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index da44aac5826a..67c52e6e4719 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -122,7 +122,8 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential @NonNull String requestType, CallingAppInfo callingAppInfo, Set<ComponentName> enabledProviders, - CancellationSignal cancellationSignal, long timestampStarted) { + CancellationSignal cancellationSignal, long timestampStarted, + boolean shouldBindClientToDeath) { mContext = context; mLock = lock; mSessionCallback = sessionCallback; @@ -146,16 +147,18 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted, mCallingUid, ApiName.getMetricCodeFromRequestInfo(mRequestType)); setCancellationListener(); - if (Flags.clearSessionEnabled()) { - setUpClientCallbackListener(); + if (shouldBindClientToDeath && Flags.clearSessionEnabled()) { + if (mClientCallback != null && mClientCallback instanceof IInterface) { + setUpClientCallbackListener(((IInterface) mClientCallback).asBinder()); + } } } - private void setUpClientCallbackListener() { + protected void setUpClientCallbackListener(IBinder clientBinder) { if (mClientCallback != null && mClientCallback instanceof IInterface) { IInterface callback = (IInterface) mClientCallback; try { - callback.asBinder().linkToDeath(mDeathRecipient, 0); + clientBinder.linkToDeath(mDeathRecipient, 0); } catch (RemoteException e) { Slog.e(TAG, e.getMessage()); } diff --git a/services/lint-baseline.xml b/services/lint-baseline.xml index 8489c17dd878..a311d07e52fb 100644 --- a/services/lint-baseline.xml +++ b/services/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="SimpleManualPermissionEnforcement" @@ -56,4 +56,4 @@ column="13"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/print/lint-baseline.xml b/services/print/lint-baseline.xml index 1bf031a9e289..11c0cc8ea93c 100644 --- a/services/print/lint-baseline.xml +++ b/services/print/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="SimpleManualPermissionEnforcement" @@ -12,4 +12,4 @@ column="13"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp index e3954355491d..4fcdbfc21f6c 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp +++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp @@ -33,7 +33,6 @@ java_test_host { ":BackgroundInstallControlServiceTestApp", ":BackgroundInstallControlMockApp1", ":BackgroundInstallControlMockApp2", - ":BackgroundInstallControlMockApp3", ], test_suites: [ "general-tests", diff --git a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml index 031d57fbe182..1e7a78aa6f93 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml +++ b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml @@ -29,14 +29,11 @@ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="cleanup" value="true" /> <option name="push-file" - key="BackgroundInstallControlMockApp1.apk" - value="/data/local/tmp/BackgroundInstallControlMockApp1.apk" /> + key="BackgroundInstallControlMockApp1.apk" + value="/data/local/tmp/BackgroundInstallControlMockApp1.apk" /> <option name="push-file" - key="BackgroundInstallControlMockApp2.apk" - value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" /> - <option name="push-file" - key="BackgroundInstallControlMockApp3.apk" - value="/data/local/tmp/BackgroundInstallControlMockApp3.apk" /> + key="BackgroundInstallControlMockApp2.apk" + value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" /> </target_preparer> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > diff --git a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java index 5092a4659eb9..74506076d82f 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java +++ b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java @@ -41,26 +41,17 @@ public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit private static final String MOCK_APK_FILE_1 = "BackgroundInstallControlMockApp1.apk"; private static final String MOCK_APK_FILE_2 = "BackgroundInstallControlMockApp2.apk"; - // TODO: Move the silent installs to test-app using {@link - // BackgroundInstallControlServiceTest#installPackage(String, String)} and remove deviceConfig - // branch in BICS. - // b/310983905 @Test public void testGetMockBackgroundInstalledPackages() throws Exception { - installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1); + installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1); installPackage(TEST_DATA_DIR + MOCK_APK_FILE_2); assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_1)).isNotNull(); assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNotNull(); - assertThat( - getDevice() - .setProperty( - "debug.transparency.bg-install-apps", - MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2)) - .isTrue(); - runDeviceTest( - "BackgroundInstallControlServiceTest", "testGetMockBackgroundInstalledPackages"); + assertThat(getDevice().setProperty("debug.transparency.bg-install-apps", + MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2)).isTrue(); + runDeviceTest("testGetMockBackgroundInstalledPackages"); assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_1)).isNull(); assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_2)).isNull(); @@ -68,30 +59,16 @@ public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNull(); } - @Test - public void testRegisterCallback() throws Exception { - runDeviceTest( - "BackgroundInstallControlServiceTest", - "testRegisterBackgroundInstallControlCallback"); - } - - @Test - public void testUnregisterCallback() throws Exception { - runDeviceTest( - "BackgroundInstallControlServiceTest", - "testUnregisterBackgroundInstallControlCallback"); - } - private void installPackage(String path) throws DeviceNotAvailableException { String cmd = "pm install -t --force-queryable " + path; CommandResult result = getDevice().executeShellV2Command(cmd); assertThat(result.getStatus() == CommandStatus.SUCCESS).isTrue(); } - private void runDeviceTest(String testName, String method) throws DeviceNotAvailableException { + private void runDeviceTest(String method) throws DeviceNotAvailableException { var options = new DeviceTestRunOptions(PACKAGE_NAME); - options.setTestClassName(PACKAGE_NAME + "." + testName); + options.setTestClassName(PACKAGE_NAME + ".BackgroundInstallControlServiceTest"); options.setTestMethodName(method); runDeviceTests(options); } -}
\ No newline at end of file +} diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml index b5b8ea0f40c7..1fa1f84cd04e 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml +++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml @@ -21,9 +21,6 @@ <uses-library android:name="android.test.runner" /> </application> - <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> - <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:label="APCT tests for background install control service" android:targetPackage="com.android.server.pm.test.app" /> diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java index f033fed73b27..b74e5619fd0c 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java +++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java @@ -16,256 +16,54 @@ package com.android.server.pm.test.app; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.QUERY_ALL_PACKAGES; - -import static com.android.compatibility.common.util.SystemUtil.runShellCommand; - import static com.google.common.truth.Truth.assertThat; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.IBackgroundInstallControlService; import android.content.pm.PackageInfo; -import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; -import android.os.Bundle; -import android.os.IRemoteCallback; -import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.util.Pair; +import android.os.UserHandle; -import androidx.annotation.NonNull; -import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.compatibility.common.util.ShellIdentityUtils; -import com.android.compatibility.common.util.ThrowingRunnable; - -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.FileInputStream; -import java.io.OutputStream; -import java.util.ArrayList; import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import java.util.stream.Collectors; @RunWith(AndroidJUnit4.class) public class BackgroundInstallControlServiceTest { private static final String TAG = "BackgroundInstallControlServiceTest"; - private static final String ACTION_INSTALL_COMMIT = - "com.android.server.pm.test.app.BackgroundInstallControlServiceTest" - + ".ACTION_INSTALL_COMMIT"; - private static final String MOCK_PACKAGE_NAME = "com.android.servicestests.apps.bicmockapp3"; - - private static final String TEST_DATA_DIR = "/data/local/tmp/"; - private static final String MOCK_APK_FILE = "BackgroundInstallControlMockApp3.apk"; private IBackgroundInstallControlService mIBics; @Before public void setUp() { - mIBics = - IBackgroundInstallControlService.Stub.asInterface( - ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); + mIBics = IBackgroundInstallControlService.Stub.asInterface( + ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); assertThat(mIBics).isNotNull(); } - @After - public void tearDown() { - runShellCommand("pm uninstall " + MOCK_PACKAGE_NAME); - } - @Test public void testGetMockBackgroundInstalledPackages() throws RemoteException { - ParceledListSlice<PackageInfo> slice = - ShellIdentityUtils.invokeMethodWithShellPermissions( - mIBics, - (bics) -> { - try { - return bics.getBackgroundInstalledPackages( - PackageManager.MATCH_ALL, Process.myUserHandle() - .getIdentifier()); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - }, - QUERY_ALL_PACKAGES); + ParceledListSlice<PackageInfo> slice = mIBics.getBackgroundInstalledPackages( + PackageManager.MATCH_ALL, + UserHandle.USER_ALL); assertThat(slice).isNotNull(); var packageList = slice.getList(); assertThat(packageList).isNotNull(); assertThat(packageList).hasSize(2); - var expectedPackageNames = - Set.of( - "com.android.servicestests.apps.bicmockapp1", - "com.android.servicestests.apps.bicmockapp2"); - var actualPackageNames = - packageList.stream() - .map((packageInfo) -> packageInfo.packageName) - .collect(Collectors.toSet()); + var expectedPackageNames = Set.of("com.android.servicestests.apps.bicmockapp1", + "com.android.servicestests.apps.bicmockapp2"); + var actualPackageNames = packageList.stream().map((packageInfo) -> packageInfo.packageName) + .collect(Collectors.toSet()); assertThat(actualPackageNames).containsExactlyElementsIn(expectedPackageNames); } - - @Test - public void testRegisterBackgroundInstallControlCallback() - throws Exception { - String testPackageName = "test"; - int testUserId = 1; - ArrayList<Pair<String, Integer>> sharedResource = new ArrayList<>(); - IRemoteCallback testCallback = - new IRemoteCallback.Stub() { - private final ArrayList<Pair<String, Integer>> mArray = sharedResource; - - @Override - public void sendResult(Bundle data) throws RemoteException { - mArray.add(new Pair(testPackageName, testUserId)); - } - }; - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( - mIBics, - (bics) -> { - try { - bics.registerBackgroundInstallCallback(testCallback); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - }, - QUERY_ALL_PACKAGES, - INTERACT_ACROSS_USERS_FULL); - installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME); - - assertUntil(() -> sharedResource.size() == 1, 2000); - assertThat(sharedResource.get(0).first).isEqualTo(testPackageName); - assertThat(sharedResource.get(0).second).isEqualTo(testUserId); - } - - @Test - public void testUnregisterBackgroundInstallControlCallback() { - String testValue = "test"; - ArrayList<String> sharedResource = new ArrayList<>(); - IRemoteCallback testCallback = - new IRemoteCallback.Stub() { - private final ArrayList<String> mArray = sharedResource; - - @Override - public void sendResult(Bundle data) throws RemoteException { - mArray.add(testValue); - } - }; - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( - mIBics, - (bics) -> { - try { - bics.registerBackgroundInstallCallback(testCallback); - bics.unregisterBackgroundInstallCallback(testCallback); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - }, - QUERY_ALL_PACKAGES, - INTERACT_ACROSS_USERS_FULL); - installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME); - - assertUntil(() -> sharedResource.isEmpty(), 2000); - } - - private static boolean installPackage(String apkPath, String packageName) { - Context context = InstrumentationRegistry.getInstrumentation().getContext(); - final CountDownLatch installLatch = new CountDownLatch(1); - final BroadcastReceiver installReceiver = - new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - int packageInstallStatus = - intent.getIntExtra( - PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE_INVALID); - if (packageInstallStatus == PackageInstaller.STATUS_SUCCESS) { - installLatch.countDown(); - } - } - }; - final IntentFilter intentFilter = new IntentFilter(ACTION_INSTALL_COMMIT); - context.registerReceiver(installReceiver, intentFilter, Context.RECEIVER_EXPORTED); - - PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); - PackageInstaller.SessionParams params = - new PackageInstaller.SessionParams( - PackageInstaller.SessionParams.MODE_FULL_INSTALL); - params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED); - try { - int sessionId = packageInstaller.createSession(params); - PackageInstaller.Session session = packageInstaller.openSession(sessionId); - OutputStream out = session.openWrite(packageName, 0, -1); - FileInputStream fis = new FileInputStream(apkPath); - byte[] buffer = new byte[65536]; - int size; - while ((size = fis.read(buffer)) != -1) { - out.write(buffer, 0, size); - } - session.fsync(out); - fis.close(); - out.close(); - - runWithShellPermissionIdentity( - () -> { - session.commit(createPendingIntent(context).getIntentSender()); - installLatch.await(5, TimeUnit.SECONDS); - }); - return true; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static PendingIntent createPendingIntent(Context context) { - PendingIntent pendingIntent = - PendingIntent.getBroadcast( - context, - 1, - new Intent(ACTION_INSTALL_COMMIT) - .setPackage( - BackgroundInstallControlServiceTest.class.getPackageName()), - PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE); - return pendingIntent; - } - - private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command) - throws Exception { - InstrumentationRegistry.getInstrumentation() - .getUiAutomation() - .adoptShellPermissionIdentity(); - try { - command.run(); - } finally { - InstrumentationRegistry.getInstrumentation() - .getUiAutomation() - .dropShellPermissionIdentity(); - } - } - - private static void assertUntil(Supplier<Boolean> condition, int timeoutMs) { - long endTime = System.currentTimeMillis() + timeoutMs; - while (System.currentTimeMillis() <= endTime) { - if (condition.get()) return; - try { - Thread.sleep(10); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - assertThat(condition.get()).isTrue(); - } } diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp index 39b0ff782b72..7804f4ce9d02 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp +++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp @@ -50,11 +50,3 @@ android_test_helper_app { "--rename-manifest-package com.android.servicestests.apps.bicmockapp2", ], } - -android_test_helper_app { - name: "BackgroundInstallControlMockApp3", - defaults: ["bic-mock-app-defaults"], - aaptflags: [ - "--rename-manifest-package com.android.servicestests.apps.bicmockapp3", - ], -} diff --git a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java index 5c50acb13f30..a8af98f7a332 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java @@ -72,14 +72,18 @@ public class RefreshRateSettingsUtilsTest { @Test public void testFindHighestRefreshRateForDefaultDisplay() { - when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null); - assertEquals(DEFAULT_REFRESH_RATE, + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock); + assertEquals(120, RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), /* delta= */ 0); + } - when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock); - assertEquals(120, + @Test + public void testFindHighestRefreshRate_DisplayIsNull() { + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null); + assertEquals(DEFAULT_REFRESH_RATE, RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), /* delta= */ 0); + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index a0e5fd8e1b34..83479e28fe24 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -27,8 +27,6 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_R import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; - import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; @@ -290,6 +288,7 @@ public class DisplayModeDirectorTest { }; private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY; + private static final int DISPLAY_ID_2 = Display.DEFAULT_DISPLAY + 1; private static final int MODE_ID = 1; private static final float TRANSITION_POINT = 0.763f; @@ -1550,23 +1549,39 @@ public class DisplayModeDirectorTest { public void testPeakRefreshRate_FlagEnabled() { when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled()) .thenReturn(true); - float highestRefreshRate = 130; - doReturn(highestRefreshRate).when(() -> - RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext)); DisplayModeDirector director = - createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + Display.Mode[] modes1 = new Display.Mode[] { + new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 130), + }; + Display.Mode[] modes2 = new Display.Mode[] { + new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 140), + }; + SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>(); + supportedModesByDisplay.put(DISPLAY_ID, modes1); + supportedModesByDisplay.put(DISPLAY_ID_2, modes2); + Sensor lightSensor = createLightSensor(); SensorManager sensorManager = createMockSensorManager(lightSensor); director.start(sensorManager); + director.injectSupportedModesByDisplay(supportedModesByDisplay); setPeakRefreshRate(Float.POSITIVE_INFINITY); - Vote vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote vote1 = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - highestRefreshRate); + Vote vote2 = director.getVote(DISPLAY_ID_2, + Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 0, /* frameRateHigh= */ 130); + assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 0, /* frameRateHigh= */ 140); } @Test @@ -1584,32 +1599,85 @@ public class DisplayModeDirectorTest { setPeakRefreshRate(peakRefreshRate); - Vote vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, + /* frameRateHigh= */ peakRefreshRate); + } + + @Test + public void testPeakRefreshRate_DisplayChanged() { + when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled()) + .thenReturn(true); + DisplayModeDirector director = + new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + mInjector.mDisplayInfo.supportedModes = new Display.Mode[] { + new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 130), + }; + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + setPeakRefreshRate(Float.POSITIVE_INFINITY); + + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - peakRefreshRate); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ 130); + + // The highest refresh rate of the display changes + mInjector.mDisplayInfo.supportedModes = new Display.Mode[] { + new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 140), + }; + director.getDisplayObserver().onDisplayChanged(DISPLAY_ID); + + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ 140); } @Test public void testMinRefreshRate_FlagEnabled() { when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled()) .thenReturn(true); - float highestRefreshRate = 130; - doReturn(highestRefreshRate).when(() -> - RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext)); DisplayModeDirector director = - createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + Display.Mode[] modes1 = new Display.Mode[] { + new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 130), + }; + Display.Mode[] modes2 = new Display.Mode[] { + new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 140), + }; + SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>(); + supportedModesByDisplay.put(DISPLAY_ID, modes1); + supportedModesByDisplay.put(DISPLAY_ID_2, modes2); + Sensor lightSensor = createLightSensor(); SensorManager sensorManager = createMockSensorManager(lightSensor); director.start(sensorManager); + director.injectSupportedModesByDisplay(supportedModesByDisplay); setMinRefreshRate(Float.POSITIVE_INFINITY); - Vote vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote vote1 = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + Vote vote2 = director.getVote(DISPLAY_ID_2, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate, + assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 130, + /* frameRateHigh= */ Float.POSITIVE_INFINITY); + assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 140, /* frameRateHigh= */ Float.POSITIVE_INFINITY); } @@ -1628,13 +1696,50 @@ public class DisplayModeDirectorTest { setMinRefreshRate(minRefreshRate); - Vote vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ minRefreshRate, /* frameRateHigh= */ Float.POSITIVE_INFINITY); } @Test + public void testMinRefreshRate_DisplayChanged() { + when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled()) + .thenReturn(true); + DisplayModeDirector director = + new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + mInjector.mDisplayInfo.supportedModes = new Display.Mode[] { + new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 130), + }; + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + setMinRefreshRate(Float.POSITIVE_INFINITY); + + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 130, + /* frameRateHigh= */ Float.POSITIVE_INFINITY); + + // The highest refresh rate of the display changes + mInjector.mDisplayInfo.supportedModes = new Display.Mode[] { + new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720, + /* refreshRate= */ 140), + }; + director.getDisplayObserver().onDisplayChanged(DISPLAY_ID); + + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 140, + /* frameRateHigh= */ Float.POSITIVE_INFINITY); + } + + @Test public void testSensorRegistration() { // First, configure brightness zones or DMD won't register for sensor data. final FakeDeviceConfig config = mInjector.getDeviceConfig(); @@ -3329,7 +3434,7 @@ public class DisplayModeDirectorTest { public static class FakesInjector implements DisplayModeDirector.Injector { private final FakeDeviceConfig mDeviceConfig; private final DisplayInfo mDisplayInfo; - private final Display mDisplay; + private final Map<Integer, Display> mDisplays; private boolean mDisplayInfoValid = true; private final DisplayManagerInternal mDisplayManagerInternal; private final StatusBarManagerInternal mStatusBarManagerInternal; @@ -3350,7 +3455,8 @@ public class DisplayModeDirectorTest { mDisplayInfo.defaultModeId = MODE_ID; mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID, 800, 600, /* refreshRate= */ 60)}; - mDisplay = createDisplay(DISPLAY_ID); + mDisplays = Map.of(DISPLAY_ID, createDisplay(DISPLAY_ID), + DISPLAY_ID_2, createDisplay(DISPLAY_ID_2)); mDisplayManagerInternal = displayManagerInternal; mStatusBarManagerInternal = statusBarManagerInternal; mSensorManagerInternal = sensorManagerInternal; @@ -3381,12 +3487,12 @@ public class DisplayModeDirectorTest { @Override public Display getDisplay(int displayId) { - return mDisplay; + return mDisplays.get(displayId); } @Override public Display[] getDisplays() { - return new Display[] { mDisplay }; + return mDisplays.values().toArray(new Display[0]); } @Override diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java deleted file mode 100644 index e1fce9b75906..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java +++ /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.server.pm; - -import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY; -import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_USER_ID_KEY; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.after; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import android.os.Bundle; -import android.os.IRemoteCallback; -import android.os.RemoteException; -import android.platform.test.annotations.Presubmit; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.ArgumentCaptor; - -/** Unit tests for {@link BackgroundInstallControlCallbackHelperTest} */ -@Presubmit -@RunWith(JUnit4.class) -public class BackgroundInstallControlCallbackHelperTest { - - private final IRemoteCallback mCallback = - spy( - new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle extras) {} - }); - - private BackgroundInstallControlCallbackHelper mCallbackHelper; - - @Before - public void setup() { - mCallbackHelper = new BackgroundInstallControlCallbackHelper(); - } - - @Test - public void registerBackgroundInstallControlCallback_registers_successfully() { - mCallbackHelper.registerBackgroundInstallCallback(mCallback); - - synchronized (mCallbackHelper.mCallbacks) { - assertEquals(1, mCallbackHelper.mCallbacks.getRegisteredCallbackCount()); - assertEquals(mCallback, mCallbackHelper.mCallbacks.getRegisteredCallbackItem(0)); - } - } - - @Test - public void unregisterBackgroundInstallControlCallback_unregisters_successfully() { - synchronized (mCallbackHelper.mCallbacks) { - mCallbackHelper.mCallbacks.register(mCallback); - } - - mCallbackHelper.unregisterBackgroundInstallCallback(mCallback); - - synchronized (mCallbackHelper.mCallbacks) { - assertEquals(0, mCallbackHelper.mCallbacks.getRegisteredCallbackCount()); - } - } - - @Test - public void notifyAllCallbacks_broadcastsToCallbacks() - throws RemoteException { - String testPackageName = "testname"; - int testUserId = 1; - mCallbackHelper.registerBackgroundInstallCallback(mCallback); - - mCallbackHelper.notifyAllCallbacks(testUserId, testPackageName); - - ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); - verify(mCallback, after(1000).times(1)).sendResult(bundleCaptor.capture()); - Bundle receivedBundle = bundleCaptor.getValue(); - assertEquals(testPackageName, receivedBundle.getString(FLAGGED_PACKAGE_NAME_KEY)); - assertEquals(testUserId, receivedBundle.getInt(FLAGGED_USER_ID_KEY)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java index c0051c6c9e17..eee37525ee37 100644 --- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java @@ -133,7 +133,8 @@ public class AnrHelperTest { verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding( eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName), eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mAuxExecutorService), - eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, eq(mEarlyDumpFuture)); + anyBoolean() /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, + eq(mEarlyDumpFuture)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java index 3069d25e39d7..daf18edaf2de 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java @@ -16,10 +16,6 @@ package com.android.server.pm; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.QUERY_ALL_PACKAGES; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -31,7 +27,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -102,6 +97,7 @@ public final class BackgroundInstallControlServiceTest { private Looper mLooper; private File mFile; + @Mock private Context mContext; @Mock @@ -112,12 +108,8 @@ public final class BackgroundInstallControlServiceTest { private UsageStatsManagerInternal mUsageStatsManagerInternal; @Mock private PermissionManagerServiceInternal mPermissionManager; - @Mock - private BackgroundInstallControlCallbackHelper mCallbackHelper; - @Captor private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor; - @Captor private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor; @@ -127,12 +119,11 @@ public final class BackgroundInstallControlServiceTest { mTestLooper = new TestLooper(); mLooper = mTestLooper.getLooper(); - mFile = - new File( - InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(), - "test"); - mBackgroundInstallControlService = - new BackgroundInstallControlService(new MockInjector(mContext)); + mFile = new File( + InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(), + "test"); + mBackgroundInstallControlService = new BackgroundInstallControlService( + new MockInjector(mContext)); verify(mUsageStatsManagerInternal).registerListener(mUsageEventListenerCaptor.capture()); mUsageEventListener = mUsageEventListenerCaptor.getValue(); @@ -152,7 +143,8 @@ public final class BackgroundInstallControlServiceTest { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); mBackgroundInstallControlService.initBackgroundInstalledPackages(); assertNotNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - assertEquals(0, mBackgroundInstallControlService.getBackgroundInstalledPackages().size()); + assertEquals(0, + mBackgroundInstallControlService.getBackgroundInstalledPackages().size()); } @Test @@ -169,9 +161,12 @@ public final class BackgroundInstallControlServiceTest { // Write test data to the file on the disk. try { ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream); - long token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); - protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); - protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); + long token = protoOutputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + protoOutputStream.write( + BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); + protoOutputStream.write( + BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); protoOutputStream.end(token); protoOutputStream.flush(); atomicFile.finishWrite(fileOutputStream); @@ -203,14 +198,20 @@ public final class BackgroundInstallControlServiceTest { try { ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream); - long token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); - protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); - protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); + long token = protoOutputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + protoOutputStream.write( + BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); + protoOutputStream.write( + BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); protoOutputStream.end(token); - token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); - protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_2); - protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_2 + 1); + token = protoOutputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + protoOutputStream.write( + BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_2); + protoOutputStream.write( + BackgroundInstalledPackageProto.USER_ID, USER_ID_2 + 1); protoOutputStream.end(token); protoOutputStream.flush(); @@ -240,7 +241,7 @@ public final class BackgroundInstallControlServiceTest { // Read the file on the disk to verify var packagesInDisk = new SparseSetArray<>(); AtomicFile atomicFile = new AtomicFile(mFile); - try (FileInputStream fileInputStream = atomicFile.openRead()) { + try (FileInputStream fileInputStream = atomicFile.openRead()) { ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream); while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { @@ -248,25 +249,23 @@ public final class BackgroundInstallControlServiceTest { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = - protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoInputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = - protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = - protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - - 1; + userId = protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) - 1; break; default: - fail("Undefined field in proto: " + protoInputStream.getFieldNumber()); + fail("Undefined field in proto: " + + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -297,7 +296,7 @@ public final class BackgroundInstallControlServiceTest { // Read the file on the disk to verify var packagesInDisk = new SparseSetArray<>(); AtomicFile atomicFile = new AtomicFile(mFile); - try (FileInputStream fileInputStream = atomicFile.openRead()) { + try (FileInputStream fileInputStream = atomicFile.openRead()) { ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream); while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { @@ -305,25 +304,23 @@ public final class BackgroundInstallControlServiceTest { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = - protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoInputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = - protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = - protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - - 1; + userId = protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) - 1; break; default: - fail("Undefined field in proto: " + protoInputStream.getFieldNumber()); + fail("Undefined field in proto: " + + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -356,7 +353,7 @@ public final class BackgroundInstallControlServiceTest { // Read the file on the disk to verify var packagesInDisk = new SparseSetArray<>(); AtomicFile atomicFile = new AtomicFile(mFile); - try (FileInputStream fileInputStream = atomicFile.openRead()) { + try (FileInputStream fileInputStream = atomicFile.openRead()) { ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream); while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { @@ -364,25 +361,23 @@ public final class BackgroundInstallControlServiceTest { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = - protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoInputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = - protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = - protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - - 1; + userId = protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) - 1; break; default: - fail("Undefined field in proto: " + protoInputStream.getFieldNumber()); + fail("Undefined field in proto: " + + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -404,55 +399,51 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_permissionDenied() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PackageManager.PERMISSION_DENIED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_DENIED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); } @Test public void testHandleUsageEvent_permissionGranted() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); - assertEquals( - 1, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + assertEquals(1, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); } @Test public void testHandleUsageEvent_ignoredEvent() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.USER_INTERACTION, USER_ID_1, INSTALLER_NAME_1, 0); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.USER_INTERACTION, + USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); } @Test public void testHandleUsageEvent_firstActivityResumedHalfTimeFrame() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_1); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -470,18 +461,14 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_firstActivityResumedOneTimeFrame() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_1); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -499,23 +486,16 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_firstActivityResumedOneAndHalfTimeFrame() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_1); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_3); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -537,13 +517,12 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_firstNoneActivityResumed() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -556,26 +535,27 @@ public final class BackgroundInstallControlServiceTest { } @Test - public void testHandleUsageEvent_packageAddedNoUsageEvent() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedNoUsageEvent() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -592,26 +572,27 @@ public final class BackgroundInstallControlServiceTest { } @Test - public void testHandleUsageEvent_packageAddedInsideTimeFrame() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedInsideTimeFrame() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -623,16 +604,12 @@ public final class BackgroundInstallControlServiceTest { // The 2 usage events make the package adding inside a time frame. // So it's not a background install. Thus, it's null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_1); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -640,26 +617,27 @@ public final class BackgroundInstallControlServiceTest { } @Test - public void testHandleUsageEvent_packageAddedOutsideTimeFrame1() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedOutsideTimeFrame1() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -672,16 +650,12 @@ public final class BackgroundInstallControlServiceTest { // Compared to testHandleUsageEvent_packageAddedInsideTimeFrame, // it's a background install. Thus, it's not null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -691,28 +665,28 @@ public final class BackgroundInstallControlServiceTest { assertEquals(1, packages.size()); assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1)); } - @Test - public void testHandleUsageEvent_packageAddedOutsideTimeFrame2() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedOutsideTimeFrame2() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -726,16 +700,12 @@ public final class BackgroundInstallControlServiceTest { // Compared to testHandleUsageEvent_packageAddedInsideTimeFrame, // it's a background install. Thus, it's not null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_2, - INSTALLER_NAME_2, - USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_3); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -745,31 +715,31 @@ public final class BackgroundInstallControlServiceTest { assertEquals(1, packages.size()); assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1)); } - @Test - public void testHandleUsageEvent_packageAddedThroughAdb() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedThroughAdb() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); // This test is a duplicate of testHandleUsageEvent_packageAddedThroughAdb except the // initiatingPackageName used to be null but is now "com.android.shell". This test ensures // that the behavior is still the same for when the initiatingPackageName is null. - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ null, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ null, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); // b/265203007 when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -781,16 +751,12 @@ public final class BackgroundInstallControlServiceTest { // for ADB installs the initiatingPackageName used to be null, despite being detected // as a background install. Since we do not want to treat side-loaded apps as background // install getBackgroundInstalledPackages() is expected to return null - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -798,31 +764,31 @@ public final class BackgroundInstallControlServiceTest { var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages(); assertNull(packages); } - @Test - public void testHandleUsageEvent_packageAddedThroughAdb2() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedThroughAdb2() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); // This test is a duplicate of testHandleUsageEvent_packageAddedThroughAdb except the // initiatingPackageName used to be null but is now "com.android.shell". This test ensures // that the behavior is still the same after this change. - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ "com.android.shell", - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ "com.android.shell", + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); // b/265203007 when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -834,16 +800,12 @@ public final class BackgroundInstallControlServiceTest { // for ADB installs the initiatingPackageName is com.android.shell, despite being detected // as a background install. Since we do not want to treat side-loaded apps as background // install getBackgroundInstalledPackages() is expected to return null - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -851,7 +813,6 @@ public final class BackgroundInstallControlServiceTest { var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages(); assertNull(packages); } - @Test public void testPackageRemoved() { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); @@ -898,7 +859,8 @@ public final class BackgroundInstallControlServiceTest { packages.add(packageInfo2); var packageInfo3 = makePackageInfo(PACKAGE_NAME_3); packages.add(packageInfo3); - doReturn(packages).when(mPackageManager).getInstalledPackagesAsUser(any(), anyInt()); + doReturn(packages).when(mPackageManager).getInstalledPackagesAsUser( + any(), anyInt()); var resultPackages = mBackgroundInstallControlService.getBackgroundInstalledPackages(0L, USER_ID_1); @@ -908,44 +870,18 @@ public final class BackgroundInstallControlServiceTest { assertFalse(resultPackages.getList().contains(packageInfo3)); } - @Test(expected = SecurityException.class) - public void enforceCallerQueryPackagesPermissionsThrowsSecurityException() { - doThrow(new SecurityException("test")).when(mContext) - .enforceCallingPermission(eq(QUERY_ALL_PACKAGES), anyString()); - - mBackgroundInstallControlService.enforceCallerQueryPackagesPermissions(); - } - - @Test - public void enforceCallerQueryPackagesPermissionsDoesNotThrowSecurityException() { - //enforceCallerQueryPackagesPermissions do not throw - - mBackgroundInstallControlService.enforceCallerQueryPackagesPermissions(); - } - - @Test(expected = SecurityException.class) - public void enforceCallerInteractCrossUserPermissionsThrowsSecurityException() { - doThrow(new SecurityException("test")).when(mContext) - .enforceCallingPermission(eq(INTERACT_ACROSS_USERS_FULL), anyString()); - - mBackgroundInstallControlService.enforceCallerInteractCrossUserPermissions(); - } - @Test - public void enforceCallerInteractCrossUserPermissionsDoesNotThrowSecurityException() { - //enforceCallerQueryPackagesPermissions do not throw - - mBackgroundInstallControlService.enforceCallerInteractCrossUserPermissions(); - } - /** * Mock a usage event occurring. * * @param usageEventId id of a usage event - * @param userId user id of a usage event - * @param pkgName package name of a usage event - * @param timestamp timestamp of a usage event + * @param userId user id of a usage event + * @param pkgName package name of a usage event + * @param timestamp timestamp of a usage event */ - private void generateUsageEvent(int usageEventId, int userId, String pkgName, long timestamp) { + private void generateUsageEvent(int usageEventId, + int userId, + String pkgName, + long timestamp) { Event event = new Event(usageEventId, timestamp); event.mPackage = pkgName; mUsageEventListener.onUsageEvent(userId, event); @@ -999,10 +935,5 @@ public final class BackgroundInstallControlServiceTest { public File getDiskFile() { return mFile; } - - @Override - public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() { - return mCallbackHelper; - } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java index c10c3c28e9dd..9b25f58acc96 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java @@ -183,7 +183,6 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { assertThat(mHistoryManager.doesHistoryExistForUser(mProfileId)).isFalse(); verify(mDb, times(2)).disableHistory(); } - @Test public void testAddProfile_historyEnabledInPrimary() { // create a history @@ -610,4 +609,14 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { assertThat(mHistoryManager.isHistoryEnabled(USER_SYSTEM)).isFalse(); } + @Test + public void testDelayedPackageRemoval_userLocked() { + String pkg = "pkg"; + mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg); + mHistoryManager.onUserUnlocked(USER_SYSTEM); + mHistoryManager.onUserStopped(USER_SYSTEM); + mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg); + + // no exception, yay + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index 53ca704b6d86..bf850cfe04db 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -44,8 +44,10 @@ import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; import android.graphics.drawable.Icon; +import android.os.BadParcelableException; import android.os.Binder; import android.os.Build; +import android.os.DeadObjectException; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -99,6 +101,20 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { } @Test + public void testGetActiveNotifications_handlesBinderErrors() throws RemoteException { + TestListenerService service = new TestListenerService(); + INotificationManager noMan = service.getNoMan(); + when(noMan.getActiveNotificationsFromListener(any(), any(), anyInt())) + .thenThrow(new BadParcelableException("oops", new DeadObjectException(""))); + + assertNotNull(service.getActiveNotifications()); + assertNotNull(service.getActiveNotifications(NotificationListenerService.TRIM_FULL)); + assertNotNull(service.getActiveNotifications(new String[0])); + assertNull(service.getActiveNotifications( + new String[0], NotificationListenerService.TRIM_LIGHT)); + } + + @Test public void testGetActiveNotifications_preP_mapsExtraPeople() throws RemoteException { TestListenerService service = new TestListenerService(); service.attachBaseContext(mContext); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 177d64555899..dd252f3ffd20 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -60,6 +60,7 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.time.Instant; @SmallTest @RunWith(AndroidJUnit4.class) @@ -407,6 +408,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.userModifiedFields = 16; rule.iconResName = ICON_RES_NAME; rule.triggerDescription = TRIGGER_DESC; + rule.deletionInstant = Instant.ofEpochMilli(1701790147000L); Parcel parcel = Parcel.obtain(); rule.writeToParcel(parcel, 0); @@ -432,9 +434,10 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(rule.userModifiedFields, parceled.userModifiedFields); assertEquals(rule.triggerDescription, parceled.triggerDescription); assertEquals(rule.zenPolicy, parceled.zenPolicy); + assertEquals(rule.deletionInstant, parceled.deletionInstant); + assertEquals(rule, parceled); assertEquals(rule.hashCode(), parceled.hashCode()); - } @Test @@ -510,6 +513,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.userModifiedFields = 4; rule.iconResName = ICON_RES_NAME; rule.triggerDescription = TRIGGER_DESC; + rule.deletionInstant = Instant.ofEpochMilli(1701790147000L); ByteArrayOutputStream baos = new ByteArrayOutputStream(); writeRuleXml(rule, baos); @@ -539,6 +543,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(rule.userModifiedFields, fromXml.userModifiedFields); assertEquals(rule.triggerDescription, fromXml.triggerDescription); assertEquals(rule.iconResName, fromXml.iconResName); + assertEquals(rule.deletionInstant, fromXml.deletionInstant); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java index 7e92e427b9a4..9d7cf53e62db 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java @@ -65,18 +65,22 @@ import java.util.Set; @TestableLooper.RunWithLooper public class ZenModeDiffTest extends UiServiceTestCase { // Base set of exempt fields independent of fields that are enabled/disabled via flags. - // version is not included in the diff; manual & automatic rules have special handling + // version is not included in the diff; manual & automatic rules have special handling; + // deleted rules are not included in the diff. public static final Set<String> ZEN_MODE_CONFIG_EXEMPT_FIELDS = - Set.of("version", "manualRule", "automaticRules"); + android.app.Flags.modesApi() + ? Set.of("version", "manualRule", "automaticRules", "deletedRules") + : Set.of("version", "manualRule", "automaticRules"); // Differences for flagged fields are only generated if the flag is enabled. - // TODO: b/310620812 - Remove this exempt list when flag is inlined. + // "Metadata" fields (userModifiedFields, deletionInstant) are not compared. private static final Set<String> ZEN_RULE_EXEMPT_FIELDS = android.app.Flags.modesApi() - ? Set.of() + ? Set.of("userModifiedFields", "deletionInstant") : Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION, RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL, - RuleDiff.FIELD_ZEN_DEVICE_EFFECTS, RuleDiff.FIELD_USER_MODIFIED_FIELDS); + RuleDiff.FIELD_ZEN_DEVICE_EFFECTS, "userModifiedFields", + "deletionInstant"); // allowPriorityChannels is flagged by android.app.modes_api public static final Set<String> ZEN_MODE_CONFIG_FLAGGED_FIELDS = diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 0224ff35219b..9e3e336fa12f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -43,6 +43,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; import static android.provider.Settings.Global.ZEN_MODE_OFF; @@ -92,6 +93,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; +import android.Manifest; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.AppGlobals; @@ -104,6 +106,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; @@ -116,6 +119,7 @@ import android.media.VolumePolicy; import android.net.Uri; import android.os.Parcel; import android.os.Process; +import android.os.SimpleClock; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -172,12 +176,16 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; @SmallTest @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service. @@ -232,6 +240,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Mock PackageManager mPackageManager; private Resources mResources; private TestableLooper mTestableLooper; + private final TestClock mTestClock = new TestClock(); private ZenModeHelper mZenModeHelper; private ContentResolver mContentResolver; @Mock DeviceEffectsApplier mDeviceEffectsApplier; @@ -269,7 +278,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mConditionProviders.addSystemProvider(new CountdownConditionProvider()); mConditionProviders.addSystemProvider(new ScheduleConditionProvider()); mZenModeEventLogger = new ZenModeEventLoggerFake(mPackageManager); - mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(), + mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(), mTestClock, mConditionProviders, mTestFlagResolver, mZenModeEventLogger); ResolveInfo ri = new ResolveInfo(); @@ -1198,7 +1207,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test public void ruleUidAutomaticZenRuleRemovedUpdatesCache() throws Exception { when(mContext.checkCallingPermission(anyString())) - .thenReturn(PackageManager.PERMISSION_GRANTED); + .thenReturn(PERMISSION_GRANTED); setupZenConfig(); // one enabled automatic rule @@ -1780,7 +1789,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testDoNotUpdateModifiedDefaultAutoRule() { // mDefaultConfig is set to default config in setup by getDefaultConfigParser when(mContext.checkCallingPermission(anyString())) - .thenReturn(PackageManager.PERMISSION_GRANTED); + .thenReturn(PERMISSION_GRANTED); // shouldn't update rule that's been modified ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule(); @@ -1806,7 +1815,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testDoNotUpdateEnabledDefaultAutoRule() { // mDefaultConfig is set to default config in setup by getDefaultConfigParser when(mContext.checkCallingPermission(anyString())) - .thenReturn(PackageManager.PERMISSION_GRANTED); + .thenReturn(PERMISSION_GRANTED); // shouldn't update the rule that's enabled ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule(); @@ -1833,7 +1842,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // mDefaultConfig is set to default config in setup by getDefaultConfigParser final String defaultRuleName = "rule name test"; when(mContext.checkCallingPermission(anyString())) - .thenReturn(PackageManager.PERMISSION_GRANTED); + .thenReturn(PERMISSION_GRANTED); // will update rule that is not enabled and modified ZenModeConfig.ZenRule customDefaultRule = new ZenModeConfig.ZenRule(); @@ -4318,6 +4327,324 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + public void removeAndAddAutomaticZenRule_wasCustomized_isRestored() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + // Start with a rule. + mZenModeHelper.mConfig.automaticRules.clear(); + mTestClock.setNowMillis(1000); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build()) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).canUpdate()).isTrue(); + + // User customizes it. + AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule) + .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build()) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate", + Process.SYSTEM_UID); + + // App deletes it. + mTestClock.advanceByMillis(1000); + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1); + + // App adds it again. + mTestClock.advanceByMillis(1000); + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID); + + // Verify that the rule was restored: + // - id and creation time is the same as the original one. + // - ZenPolicy is the one that the user had set. + // - rule still has the user-modified fields. + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getCreationTime()).isEqualTo(1000); // And not 3000. + assertThat(newRuleId).isEqualTo(ruleId); + assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); + assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo( + ZenPolicy.STATE_ALLOW); + assertThat(finalRule.getUserModifiedFields()).isEqualTo( + AutomaticZenRule.FIELD_INTERRUPTION_FILTER); + assertThat(finalRule.getZenPolicy().getUserModifiedFields()).isEqualTo( + ZenPolicy.FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS); + + // Also, we discarded the "deleted rule" since we already used it for restoration. + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); + } + + @Test + public void removeAndAddAutomaticZenRule_wasNotCustomized_isNotRestored() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + // Start with a single rule. + mZenModeHelper.mConfig.automaticRules.clear(); + mTestClock.setNowMillis(1000); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build()) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000); + + // App deletes it. + mTestClock.advanceByMillis(1000); + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); + + // App adds it again. + mTestClock.advanceByMillis(1000); + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID); + + // Verify that the rule was recreated. This means id and creation time are new. + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getCreationTime()).isEqualTo(3000); + assertThat(newRuleId).isNotEqualTo(ruleId); + } + + @Test + public void removeAndAddAutomaticZenRule_recreatedButNotByApp_isNotRestored() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + // Start with a single rule. + mZenModeHelper.mConfig.automaticRules.clear(); + mTestClock.setNowMillis(1000); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build()) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000); + + // User customizes it. + mTestClock.advanceByMillis(1000); + AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule) + .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build()) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate", + Process.SYSTEM_UID); + + // App deletes it. + mTestClock.advanceByMillis(1000); + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1); + + // User creates it again (unusual case, but ok). + mTestClock.advanceByMillis(1000); + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_USER, "add it anew", CUSTOM_PKG_UID); + + // Verify that the rule was recreated. This means id and creation time are new, and the rule + // matches the latest data supplied to addAZR. + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getCreationTime()).isEqualTo(4000); + assertThat(newRuleId).isNotEqualTo(ruleId); + assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY); + assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo( + ZenPolicy.STATE_DISALLOW); + + // Also, we discarded the "deleted rule" since we're not interested in recreating it. + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); + } + + @Test + public void removeAndAddAutomaticZenRule_removedByUser_isNotRestored() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + // Start with a single rule. + mZenModeHelper.mConfig.automaticRules.clear(); + mTestClock.setNowMillis(1000); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build()) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000); + + // User customizes it. + mTestClock.advanceByMillis(1000); + AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule) + .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build()) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate", + Process.SYSTEM_UID); + + // User deletes it. + mTestClock.advanceByMillis(1000); + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_USER, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); + + // App creates it again. + mTestClock.advanceByMillis(1000); + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID); + + // Verify that the rule was recreated. This means id and creation time are new. + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getCreationTime()).isEqualTo(4000); + assertThat(newRuleId).isNotEqualTo(ruleId); + } + + @Test + public void removeAutomaticZenRule_preservedForRestoringByPackageAndConditionId() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS, + PERMISSION_GRANTED); // So that canManageAZR passes although packages don't match. + mZenModeHelper.mConfig.automaticRules.clear(); + + // Start with a bunch of customized rules where conditionUris are not unique. + String id1 = mZenModeHelper.addAutomaticZenRule("pkg1", + new AutomaticZenRule.Builder("Test1", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + String id2 = mZenModeHelper.addAutomaticZenRule("pkg1", + new AutomaticZenRule.Builder("Test2", Uri.parse("uri2")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + String id3 = mZenModeHelper.addAutomaticZenRule("pkg1", + new AutomaticZenRule.Builder("Test3", Uri.parse("uri2")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + String id4 = mZenModeHelper.addAutomaticZenRule("pkg2", + new AutomaticZenRule.Builder("Test4", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + String id5 = mZenModeHelper.addAutomaticZenRule("pkg2", + new AutomaticZenRule.Builder("Test5", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + for (ZenRule zenRule : mZenModeHelper.mConfig.automaticRules.values()) { + zenRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER; + } + + mZenModeHelper.removeAutomaticZenRule(id1, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + mZenModeHelper.removeAutomaticZenRule(id2, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + mZenModeHelper.removeAutomaticZenRule(id3, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + mZenModeHelper.removeAutomaticZenRule(id4, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + mZenModeHelper.removeAutomaticZenRule(id5, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + + assertThat(mZenModeHelper.mConfig.deletedRules.keySet()) + .containsExactly("pkg1|uri1", "pkg1|uri2", "pkg2|uri1"); + assertThat(mZenModeHelper.mConfig.deletedRules.values().stream().map(zr -> zr.name) + .collect(Collectors.toList())) + .containsExactly("Test1", "Test3", "Test5"); + } + + @Test + public void removeAllZenRules_preservedForRestoring() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + mZenModeHelper.mConfig.automaticRules.clear(); + + mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + new AutomaticZenRule.Builder("Test1", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + new AutomaticZenRule.Builder("Test2", Uri.parse("uri2")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + + for (ZenRule zenRule : mZenModeHelper.mConfig.automaticRules.values()) { + zenRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER; + } + + mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), UPDATE_ORIGIN_APP, + "begone", CUSTOM_PKG_UID); + + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(2); + } + + @Test + public void removeAllZenRules_fromSystem_deletesPreservedRulesToo() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + mZenModeHelper.mConfig.automaticRules.clear(); + + // Start with deleted rules from 2 different packages. + Instant now = Instant.ofEpochMilli(1701796461000L); + ZenRule pkg1Rule = newZenRule("pkg1", now.minus(1, ChronoUnit.DAYS), now); + ZenRule pkg2Rule = newZenRule("pkg2", now.minus(2, ChronoUnit.DAYS), now); + mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg1Rule), pkg1Rule); + mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg2Rule), pkg2Rule); + + mZenModeHelper.removeAutomaticZenRules("pkg1", + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "goodbye pkg1", Process.SYSTEM_UID); + + // Preserved rules from pkg1 are gone; those from pkg2 are still there. + assertThat(mZenModeHelper.mConfig.deletedRules.values().stream().map(r -> r.pkg) + .collect(Collectors.toSet())).containsExactly("pkg2"); + } + + @Test + public void testRuleCleanup() throws Exception { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + Instant now = Instant.ofEpochMilli(1701796461000L); + Instant yesterday = now.minus(1, ChronoUnit.DAYS); + Instant aWeekAgo = now.minus(7, ChronoUnit.DAYS); + Instant twoMonthsAgo = now.minus(60, ChronoUnit.DAYS); + mTestClock.setNowMillis(now.toEpochMilli()); + + when(mPackageManager.getPackageInfo(eq("good_pkg"), anyInt())) + .thenReturn(new PackageInfo()); + when(mPackageManager.getPackageInfo(eq("bad_pkg"), anyInt())) + .thenThrow(new PackageManager.NameNotFoundException("bad_pkg is not here")); + + // Set up a config for another user containing: + ZenModeConfig config = new ZenModeConfig(); + config.user = 42; + mZenModeHelper.mConfigs.put(42, config); + // okay rules (not deleted, package exists, with a range of creation dates). + config.automaticRules.put("ar1", newZenRule("good_pkg", now, null)); + config.automaticRules.put("ar2", newZenRule("good_pkg", yesterday, null)); + config.automaticRules.put("ar3", newZenRule("good_pkg", twoMonthsAgo, null)); + // newish rules for a missing package + config.automaticRules.put("ar4", newZenRule("bad_pkg", yesterday, null)); + // oldish rules belonging to a missing package + config.automaticRules.put("ar5", newZenRule("bad_pkg", aWeekAgo, null)); + // rules deleted recently + config.deletedRules.put("del1", newZenRule("good_pkg", twoMonthsAgo, yesterday)); + config.deletedRules.put("del2", newZenRule("good_pkg", twoMonthsAgo, aWeekAgo)); + // rules deleted a long time ago + config.deletedRules.put("del3", newZenRule("good_pkg", twoMonthsAgo, twoMonthsAgo)); + // rules for a missing package, created recently and deleted recently + config.deletedRules.put("del4", newZenRule("bad_pkg", yesterday, now)); + // rules for a missing package, created a long time ago and deleted recently + config.deletedRules.put("del5", newZenRule("bad_pkg", twoMonthsAgo, now)); + // rules for a missing package, created a long time ago and deleted a long time ago + config.deletedRules.put("del6", newZenRule("bad_pkg", twoMonthsAgo, twoMonthsAgo)); + + mZenModeHelper.onUserUnlocked(42); // copies config and cleans it up. + + assertThat(mZenModeHelper.mConfig.automaticRules.keySet()) + .containsExactly("ar1", "ar2", "ar3", "ar4"); + assertThat(mZenModeHelper.mConfig.deletedRules.keySet()) + .containsExactly("del1", "del2", "del4"); + } + + private static ZenRule newZenRule(String pkg, Instant createdAt, @Nullable Instant deletedAt) { + ZenRule rule = new ZenRule(); + rule.pkg = pkg; + rule.creationTime = createdAt.toEpochMilli(); + rule.deletionInstant = deletedAt; + // Plus stuff so that isValidAutomaticRule() passes + rule.name = "A rule from " + pkg + " created on " + createdAt; + rule.conditionId = Uri.parse(rule.name); + return rule; + } + + @Test public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); @@ -4919,4 +5246,25 @@ public class ZenModeHelperTest extends UiServiceTestCase { return parser.nextTag(); } } + + private static class TestClock extends SimpleClock { + private long mNowMillis = 441644400000L; + + private TestClock() { + super(ZoneOffset.UTC); + } + + @Override + public long millis() { + return mNowMillis; + } + + private void setNowMillis(long millis) { + mNowMillis = millis; + } + + private void advanceByMillis(long millis) { + mNowMillis += millis; + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index f5282cb492f0..9bb2da0ff70c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -3309,7 +3309,7 @@ public class ActivityRecordTests extends WindowTestsBase { // keyguard to back to the app, expect IME insets is not frozen app.mActivityRecord.commitVisibility(true, false); mDisplayContent.updateImeInputAndControlTarget(app); - mDisplayContent.mWmService.mRoot.performSurfacePlacement(); + performSurfacePlacementAndWaitForWindowAnimator(); assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); @@ -3358,7 +3358,7 @@ public class ActivityRecordTests extends WindowTestsBase { mDisplayContent.setImeLayeringTarget(app2); app2.mActivityRecord.commitVisibility(true, false); mDisplayContent.updateImeInputAndControlTarget(app2); - mDisplayContent.mWmService.mRoot.performSurfacePlacement(); + performSurfacePlacementAndWaitForWindowAnimator(); // Verify after unfreezing app2's IME insets state, we won't dispatch visible IME insets // to client if the app didn't request IME visible. @@ -3412,7 +3412,7 @@ public class ActivityRecordTests extends WindowTestsBase { // frozen until the input started. mDisplayContent.setImeLayeringTarget(app1); mDisplayContent.updateImeInputAndControlTarget(app1); - mDisplayContent.mWmService.mRoot.performSurfacePlacement(); + performSurfacePlacementAndWaitForWindowAnimator(); assertEquals(app1, mDisplayContent.getImeInputTarget()); assertFalse(activity1.mImeInsetsFrozenUntilStartInput); diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java index 8bd54731c7c2..2d3c4bbe8bdc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java @@ -28,6 +28,7 @@ import android.os.HandlerExecutor; import android.os.Looper; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; +import android.util.Log; import androidx.test.filters.MediumTest; @@ -147,6 +148,8 @@ public class SplashScreenExceptionListTest { private void setExceptionListAndWaitForCallback(String commaSeparatedList) { CountDownLatch latch = new CountDownLatch(1); mOnUpdateDeviceConfig = rawList -> { + Log.i(getClass().getSimpleName(), "updateDeviceConfig expected=" + + commaSeparatedList + " actual=" + rawList); if (commaSeparatedList.equals(rawList)) { latch.countDown(); } @@ -155,7 +158,7 @@ public class SplashScreenExceptionListTest { KEY_SPLASH_SCREEN_EXCEPTION_LIST, commaSeparatedList, false); try { assertTrue("Timed out waiting for DeviceConfig to be updated.", - latch.await(1, TimeUnit.SECONDS)); + latch.await(5, TimeUnit.SECONDS)); } catch (InterruptedException e) { Assert.fail(e.getMessage()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 60e84b03ec89..9c421ba29796 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1054,6 +1054,19 @@ class WindowTestsBase extends SystemServiceTestsBase { } /** + * Performs surface placement and waits for WindowAnimator to complete the frame. It is used + * to execute the callbacks if the surface placement is expected to add some callbacks via + * {@link WindowAnimator#addAfterPrepareSurfacesRunnable}. + */ + void performSurfacePlacementAndWaitForWindowAnimator() { + mWm.mAnimator.ready(); + if (!mWm.mWindowPlacerLocked.isTraversalScheduled()) { + mRootWindowContainer.performSurfacePlacement(); + } + waitUntilWindowAnimatorIdle(); + } + + /** * Avoids rotating screen disturbed by some conditions. It is usually used for the default * display that is not the instance of {@link TestDisplayContent} (it bypasses the conditions). * diff --git a/services/usb/lint-baseline.xml b/services/usb/lint-baseline.xml index c2c0a350d5ad..62a2ee56a0cf 100644 --- a/services/usb/lint-baseline.xml +++ b/services/usb/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -12,4 +12,4 @@ column="42"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 250c3a54c928..2a6ac98b4d98 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -196,7 +196,7 @@ public final class TelephonyPermissions { // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, + return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, callingFeatureId, null) == AppOpsManager.MODE_ALLOWED; } @@ -249,7 +249,7 @@ public final class TelephonyPermissions { // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, + return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, callingFeatureId, null) == AppOpsManager.MODE_ALLOWED; } @@ -521,7 +521,7 @@ public final class TelephonyPermissions { // We have READ_CALL_LOG permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return appOps.noteOp(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage, + return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage, callingPackageName, null) == AppOpsManager.MODE_ALLOWED; } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 56156024fbab..a5c6d57aed82 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1315,7 +1315,7 @@ public class SubscriptionManager { * A source of phone number: the EF-MSISDN (see 3GPP TS 31.102), * or EF-MDN for CDMA (see 3GPP2 C.P0065-B), from UICC application. * - * <p>The availability and a of the number depends on the carrier. + * <p>The availability and accuracy of the number depends on the carrier. * The number may be updated by over-the-air update to UICC applications * from the carrier, or by other means with physical access to the SIM. */ @@ -1557,12 +1557,21 @@ public class SubscriptionManager { * caller can see all subscription across user profiles as it does today today even if it's * {@code false}. */ - private boolean mIsForAllUserProfiles = false; + private final boolean mIsForAllUserProfiles; /** @hide */ @UnsupportedAppUsage public SubscriptionManager(Context context) { - if (DBG) logd("SubscriptionManager created"); + this(context, false /*isForAllUserProfiles*/); + } + + /** Constructor */ + private SubscriptionManager(Context context, boolean isForAllUserProfiles) { + if (DBG) { + logd("SubscriptionManager created " + + (isForAllUserProfiles ? "for all user profile" : "")); + } + mIsForAllUserProfiles = isForAllUserProfiles; mContext = context; } @@ -1998,7 +2007,7 @@ public class SubscriptionManager { } /** - * Convert this subscription manager instance into one that can see all subscriptions across + * Create a new subscription manager instance that can see all subscriptions across * user profiles. * * @return a SubscriptionManager that can see all subscriptions regardless its user profile @@ -2008,13 +2017,12 @@ public class SubscriptionManager { * @see #getActiveSubscriptionInfoCount * @see UserHandle */ - @FlaggedApi(Flags.FLAG_WORK_PROFILE_API_SPLIT) + @FlaggedApi(Flags.FLAG_ENFORCE_SUBSCRIPTION_USER_FILTER) // @RequiresPermission(TODO(b/308809058)) // The permission check for accessing all subscriptions will be enforced upon calling the // individual APIs linked above. @NonNull public SubscriptionManager createForAllUserProfiles() { - mIsForAllUserProfiles = true; - return this; + return new SubscriptionManager(mContext, true/*isForAllUserProfiles*/); } /** diff --git a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java index fee1b25f04e5..2bc056ee743f 100644 --- a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java +++ b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java @@ -16,9 +16,6 @@ package android.transparency.test.app; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.QUERY_ALL_PACKAGES; - import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -30,7 +27,6 @@ import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.compatibility.common.util.ShellIdentityUtils; import com.android.internal.os.IBinaryTransparencyService.AppInfo; import org.junit.Before; @@ -120,13 +116,7 @@ public class BinaryTransparencyTest { @Test public void testCollectAllSilentInstalledMbaInfo() { // Action - var appInfoList = - ShellIdentityUtils.invokeMethodWithShellPermissions( - mBt, - (Bt) -> - mBt.collectAllSilentInstalledMbaInfo(new Bundle()), - QUERY_ALL_PACKAGES, - INTERACT_ACROSS_USERS_FULL); + var appInfoList = mBt.collectAllSilentInstalledMbaInfo(new Bundle()); // Verify assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java index 29cbf01dc6da..e3988cd20199 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java @@ -24,8 +24,8 @@ import android.os.Bundle; import android.view.View; import android.widget.ToggleButton; +import androidx.window.embedding.ActivityEmbeddingController; import androidx.window.embedding.SplitAttributes; -import androidx.window.embedding.SplitAttributesCalculatorParams; import androidx.window.embedding.SplitController; /** @@ -66,7 +66,9 @@ public class ActivityEmbeddingSecondaryActivity extends Activity { @Override public void onClick(View v) { // This triggers a recalcuation of splitatributes. - mSplitController.invalidateTopVisibleSplitAttributes(); + ActivityEmbeddingController + .getInstance(ActivityEmbeddingSecondaryActivity.this) + .invalidateTopVisibleActivityStacks(); } }); findViewById(R.id.secondary_enter_pip_button).setOnClickListener( diff --git a/tests/SurfaceControlViewHostTest/AndroidManifest.xml b/tests/SurfaceControlViewHostTest/AndroidManifest.xml index e50cbc52a5b8..71f01ac5ded1 100644 --- a/tests/SurfaceControlViewHostTest/AndroidManifest.xml +++ b/tests/SurfaceControlViewHostTest/AndroidManifest.xml @@ -32,6 +32,16 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + + <activity android:name="SurfaceInputTestActivity" + android:label="Surface Input Test" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + <service android:name=".EmbeddedWindowService" android:process="com.android.test.viewembed.embedded_process"/> </application> diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java index abc15b49ad98..5aaf30a5b3a7 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java @@ -23,15 +23,21 @@ import android.annotation.Nullable; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Paint; import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.util.Log; +import android.view.Choreographer; import android.view.Display; import android.view.Gravity; +import android.view.Surface; +import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.WindowManager; import android.widget.FrameLayout; @@ -43,6 +49,9 @@ public class EmbeddedWindowService extends Service { private Handler mHandler; + private IBinder mInputToken; + private SurfaceControl mSurfaceControl; + @Override public void onCreate() { super.onCreate(); @@ -101,9 +110,49 @@ public class EmbeddedWindowService extends Service { } }); } + @Override public void relayout(WindowManager.LayoutParams lp) { mHandler.post(() -> mVr.relayout(lp)); } + + @Override + public void attachEmbeddedSurfaceControl(SurfaceControl parentSc, int displayId, + IBinder hostToken) { + mHandler.post(() -> { + Paint paint = new Paint(); + paint.setTextSize(40); + paint.setColor(Color.WHITE); + + mSurfaceControl = new SurfaceControl.Builder().setName("Child SurfaceControl") + .setParent(parentSc).setBufferSize(500, 500).build(); + new SurfaceControl.Transaction().show(mSurfaceControl).apply(); + + Surface surface = new Surface(mSurfaceControl); + Canvas c = surface.lockCanvas(null); + c.drawColor(Color.BLUE); + c.drawText("Remote", 250, 250, paint); + surface.unlockCanvasAndPost(c); + WindowManager wm = getSystemService(WindowManager.class); + mInputToken = wm.registerBatchedSurfaceControlInputReceiver(displayId, hostToken, + mSurfaceControl, + Choreographer.getInstance(), event -> { + Log.d(TAG, "onInputEvent-remote " + event); + return false; + }); + + }); + } + + @Override + public void tearDownEmbeddedSurfaceControl() { + if (mSurfaceControl != null) { + new SurfaceControl.Transaction().remove(mSurfaceControl); + } + if (mInputToken != null) { + WindowManager wm = getSystemService(WindowManager.class); + wm.unregisterSurfaceControlInputReceiver(mInputToken); + } + } } } diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl index 9e9faf03ba1c..6b65b40ef8c6 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl @@ -19,8 +19,11 @@ package com.android.test.viewembed; import android.os.IBinder; import com.android.test.viewembed.IAttachEmbeddedWindowCallback; import android.view.WindowManager.LayoutParams; +import android.view.SurfaceControl; interface IAttachEmbeddedWindow { void attachEmbedded(IBinder hostToken, int width, int height, in IAttachEmbeddedWindowCallback callback); void relayout(in LayoutParams lp); + oneway void attachEmbeddedSurfaceControl(in SurfaceControl parentSurfaceControl, int displayId, IBinder hostToken); + oneway void tearDownEmbeddedSurfaceControl(); }
\ No newline at end of file diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java new file mode 100644 index 000000000000..e5f8f47aeecd --- /dev/null +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java @@ -0,0 +1,217 @@ +/* + * Copyright 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.test.viewembed; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.AttachedSurfaceControl; +import android.view.Choreographer; +import android.view.Gravity; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.widget.LinearLayout; + +/** + * Used to manually test that {@link android.view.SurfaceControlInputReceiver} API works. + */ +public class SurfaceInputTestActivity extends Activity { + + private static final String TAG = "SurfaceInputTestActivity"; + private SurfaceView mLocalSurfaceView; + private SurfaceView mRemoteSurfaceView; + private IBinder mInputToken; + private IAttachEmbeddedWindow mIAttachEmbeddedWindow; + private SurfaceControl mParentSurfaceControl; + + private final ServiceConnection mConnection = new ServiceConnection() { + // Called when the connection with the service is established + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "Service Connected"); + mIAttachEmbeddedWindow = IAttachEmbeddedWindow.Stub.asInterface(service); + loadEmbedded(); + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "Service Disconnected"); + mIAttachEmbeddedWindow = null; + } + }; + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + ViewTreeObserver viewTreeObserver = getWindow().getDecorView().getViewTreeObserver(); + viewTreeObserver.addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + addLocalChildSurfaceControl(getWindow().getRootSurfaceControl()); + viewTreeObserver.removeOnPreDrawListener(this); + return true; + } + }); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LinearLayout content = new LinearLayout(this); + mLocalSurfaceView = new SurfaceView(this); + content.addView(mLocalSurfaceView, new LinearLayout.LayoutParams( + 500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP)); + + mRemoteSurfaceView = new SurfaceView(this); + content.addView(mRemoteSurfaceView, new LinearLayout.LayoutParams( + 500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP)); + + setContentView(content); + + mLocalSurfaceView.setZOrderOnTop(true); + mLocalSurfaceView.getHolder().addCallback(mLocalSurfaceViewCallback); + + mRemoteSurfaceView.setZOrderOnTop(true); + mRemoteSurfaceView.getHolder().addCallback(mRemoteSurfaceViewHolder); + + Intent intent = new Intent(this, EmbeddedWindowService.class); + intent.setAction(IAttachEmbeddedWindow.class.getName()); + Log.d(TAG, "bindService"); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + getWindowManager().unregisterSurfaceControlInputReceiver(mInputToken); + } + + private void addLocalChildSurfaceControl(AttachedSurfaceControl attachedSurfaceControl) { + SurfaceControl surfaceControl = new SurfaceControl.Builder().setName("LocalSC") + .setBufferSize(100, 100).build(); + attachedSurfaceControl.buildReparentTransaction(surfaceControl) + .setVisibility(surfaceControl, true) + .setCrop(surfaceControl, new Rect(0, 0, 100, 100)) + .setPosition(surfaceControl, 250, 1000) + .setLayer(surfaceControl, 1).apply(); + + Paint paint = new Paint(); + paint.setColor(Color.WHITE); + paint.setTextSize(20); + + Surface surface = new Surface(surfaceControl); + Canvas c = surface.lockCanvas(null); + c.drawColor(Color.GREEN); + c.drawText("Local SC", 0, 0, paint); + surface.unlockCanvasAndPost(c); + WindowManager wm = getSystemService(WindowManager.class); + mInputToken = wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), + attachedSurfaceControl.getHostToken(), surfaceControl, + Choreographer.getInstance(), event -> { + Log.d(TAG, "onInputEvent-sc " + event); + return false; + }); + } + + private final SurfaceHolder.Callback mLocalSurfaceViewCallback = new SurfaceHolder.Callback() { + private IBinder mInputToken; + + @Override + public void surfaceCreated(@NonNull SurfaceHolder holder) { + Paint paint = new Paint(); + paint.setColor(Color.WHITE); + paint.setTextSize(40); + + Canvas c = holder.lockCanvas(); + c.drawColor(Color.RED); + c.drawText("Local", 250, 250, paint); + holder.unlockCanvasAndPost(c); + + WindowManager wm = getSystemService(WindowManager.class); + mInputToken = wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), + mLocalSurfaceView.getHostToken(), mLocalSurfaceView.getSurfaceControl(), + Choreographer.getInstance(), event -> { + Log.d(TAG, "onInputEvent-local " + event); + return false; + }); + } + + @Override + public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, + int height) { + + } + + @Override + public void surfaceDestroyed(@NonNull SurfaceHolder holder) { + if (mInputToken != null) { + getWindowManager().unregisterSurfaceControlInputReceiver(mInputToken); + } + } + }; + + private final SurfaceHolder.Callback mRemoteSurfaceViewHolder = new SurfaceHolder.Callback() { + @Override + public void surfaceCreated(@NonNull SurfaceHolder holder) { + mParentSurfaceControl = mRemoteSurfaceView.getSurfaceControl(); + loadEmbedded(); + } + + @Override + public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, + int height) { + } + + @Override + public void surfaceDestroyed(@NonNull SurfaceHolder holder) { + if (mIAttachEmbeddedWindow != null) { + try { + mIAttachEmbeddedWindow.tearDownEmbeddedSurfaceControl(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to tear down embedded SurfaceControl", e); + } + } + } + }; + + private void loadEmbedded() { + if (mParentSurfaceControl == null || mIAttachEmbeddedWindow == null) { + return; + } + try { + mIAttachEmbeddedWindow.attachEmbeddedSurfaceControl(mParentSurfaceControl, + getDisplayId(), mRemoteSurfaceView.getHostToken()); + } catch (RemoteException e) { + Log.e(TAG, "Failed to load embedded SurfaceControl", e); + } + } +} diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java new file mode 100644 index 000000000000..9daba6a79a27 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java @@ -0,0 +1,419 @@ +/* + * 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.server.vcn.routeselection; + +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY; +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY; + +import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.PACKET_LOSS_UNAVALAIBLE; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.net.IpSecTransformState; +import android.os.OutcomeReceiver; +import android.os.PowerManager; + +import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator; +import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper; +import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.concurrent.TimeUnit; + +public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { + private static final String TAG = IpSecPacketLossDetectorTest.class.getSimpleName(); + + private static final int REPLAY_BITMAP_LEN_BYTE = 512; + private static final int REPLAY_BITMAP_LEN_BIT = REPLAY_BITMAP_LEN_BYTE * 8; + private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD = 5; + private static final long POLL_IPSEC_STATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(30L); + + @Mock private IpSecTransformWrapper mIpSecTransform; + @Mock private NetworkMetricMonitorCallback mMetricMonitorCallback; + @Mock private PersistableBundleWrapper mCarrierConfig; + @Mock private IpSecPacketLossDetector.Dependencies mDependencies; + @Spy private PacketLossCalculator mPacketLossCalculator = new PacketLossCalculator(); + + @Captor private ArgumentCaptor<OutcomeReceiver> mTransformStateReceiverCaptor; + @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; + + private IpSecPacketLossDetector mIpSecPacketLossDetector; + private IpSecTransformState mTransformStateInitial; + + @Before + public void setUp() throws Exception { + super.setUp(); + mTransformStateInitial = newTransformState(0, 0, newReplayBitmap(0)); + + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt())) + .thenReturn((int) TimeUnit.MILLISECONDS.toSeconds(POLL_IPSEC_STATE_INTERVAL_MS)); + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY), + anyInt())) + .thenReturn(IPSEC_PACKET_LOSS_PERCENT_THRESHOLD); + + when(mDependencies.getPacketLossCalculator()).thenReturn(mPacketLossCalculator); + + mIpSecPacketLossDetector = + new IpSecPacketLossDetector( + mVcnContext, + mNetwork, + mCarrierConfig, + mMetricMonitorCallback, + mDependencies); + } + + private static IpSecTransformState newTransformState( + long rxSeqNo, long packtCount, byte[] replayBitmap) { + return new IpSecTransformState.Builder() + .setRxHighestSequenceNumber(rxSeqNo) + .setPacketCount(packtCount) + .setReplayBitmap(replayBitmap) + .build(); + } + + private static byte[] newReplayBitmap(int receivedPktCnt) { + final BitSet bitSet = new BitSet(REPLAY_BITMAP_LEN_BIT); + for (int i = 0; i < receivedPktCnt; i++) { + bitSet.set(i); + } + return Arrays.copyOf(bitSet.toByteArray(), REPLAY_BITMAP_LEN_BYTE); + } + + private void verifyStopped() { + assertFalse(mIpSecPacketLossDetector.isStarted()); + assertFalse(mIpSecPacketLossDetector.isValidationFailed()); + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + + // No event scheduled + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNull(mTestLooper.nextMessage()); + } + + @Test + public void testInitialization() throws Exception { + assertFalse(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork()); + verifyStopped(); + } + + private OutcomeReceiver<IpSecTransformState, RuntimeException> + startMonitorAndCaptureStateReceiver() { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + // Trigger the runnable + mTestLooper.dispatchAll(); + + verify(mIpSecTransform) + .getIpSecTransformState(any(), mTransformStateReceiverCaptor.capture()); + return mTransformStateReceiverCaptor.getValue(); + } + + @Test + public void testStartMonitor() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertFalse(mIpSecPacketLossDetector.isValidationFailed()); + assertTrue(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork()); + assertEquals(mIpSecTransform, mIpSecPacketLossDetector.getInboundTransformInternal()); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + + // Verify the first polled state is stored + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + + // Verify next poll is scheduled + assertNull(mTestLooper.nextMessage()); + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNotNull(mTestLooper.nextMessage()); + } + + @Test + public void testStartedMonitor_enterDozeMoze() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + + // Mock entering doze mode + final Intent intent = mock(Intent.class); + when(intent.getAction()).thenReturn(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + when(mPowerManagerService.isDeviceIdleMode()).thenReturn(true); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); + final BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue(); + broadcastReceiver.onReceive(mContext, intent); + + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + } + + @Test + public void testStartedMonitor_updateInboundTransform() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + + // Update the inbound transform + final IpSecTransformWrapper newTransform = mock(IpSecTransformWrapper.class); + mIpSecPacketLossDetector.setInboundTransformInternal(newTransform); + + // Verifications + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + mTestLooper.dispatchAll(); + verify(newTransform).getIpSecTransformState(any(), any()); + } + + @Test + public void testStartedMonitor_updateCarrierConfig() throws Exception { + startMonitorAndCaptureStateReceiver(); + + final int additionalPollIntervalMs = (int) TimeUnit.SECONDS.toMillis(10L); + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt())) + .thenReturn( + (int) + TimeUnit.MILLISECONDS.toSeconds( + POLL_IPSEC_STATE_INTERVAL_MS + additionalPollIntervalMs)); + mIpSecPacketLossDetector.setCarrierConfig(mCarrierConfig); + mTestLooper.dispatchAll(); + + // The already scheduled event is still fired with the old timeout + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + mTestLooper.dispatchAll(); + + // The next scheduled event will take 10 more seconds to fire + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNull(mTestLooper.nextMessage()); + mTestLooper.moveTimeForward(additionalPollIntervalMs); + assertNotNull(mTestLooper.nextMessage()); + } + + @Test + public void testStopMonitor() throws Exception { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertNotNull(mTestLooper.nextMessage()); + + // Unselect the monitor + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */); + verifyStopped(); + } + + @Test + public void testClose() throws Exception { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertNotNull(mTestLooper.nextMessage()); + + // Stop the monitor + mIpSecPacketLossDetector.close(); + verifyStopped(); + verify(mIpSecTransform).close(); + } + + @Test + public void testTransformStateReceiverOnResultWhenStopped() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + xfrmStateReceiver.onResult(mTransformStateInitial); + + // Unselect the monitor + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */); + verifyStopped(); + + xfrmStateReceiver.onResult(newTransformState(1, 1, newReplayBitmap(1))); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + } + + @Test + public void testTransformStateReceiverOnError() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + xfrmStateReceiver.onResult(mTransformStateInitial); + + xfrmStateReceiver.onError(new RuntimeException("Test")); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + } + + private void checkHandleLossRate( + int mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected) + throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + doReturn(mockPacketLossRate) + .when(mPacketLossCalculator) + .getPacketLossRatePercentage(any(), any(), anyString()); + + // Mock receiving two states with mTransformStateInitial and an arbitrary transformNew + final IpSecTransformState transformNew = newTransformState(1, 1, newReplayBitmap(1)); + xfrmStateReceiver.onResult(mTransformStateInitial); + xfrmStateReceiver.onResult(transformNew); + + // Verifications + verify(mPacketLossCalculator) + .getPacketLossRatePercentage( + eq(mTransformStateInitial), eq(transformNew), anyString()); + + if (isLastStateExpectedToUpdate) { + assertEquals(transformNew, mIpSecPacketLossDetector.getLastTransformState()); + } else { + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + } + + if (isCallbackExpected) { + verify(mMetricMonitorCallback).onValidationResultReceived(); + } else { + verify(mMetricMonitorCallback, never()).onValidationResultReceived(); + } + } + + @Test + public void testHandleLossRate_validationPass() throws Exception { + checkHandleLossRate( + 2, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); + } + + @Test + public void testHandleLossRate_validationFail() throws Exception { + checkHandleLossRate( + 22, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); + } + + @Test + public void testHandleLossRate_resultUnavalaible() throws Exception { + checkHandleLossRate( + PACKET_LOSS_UNAVALAIBLE, + false /* isLastStateExpectedToUpdate */, + false /* isCallbackExpected */); + } + + private void checkGetPacketLossRate( + IpSecTransformState oldState, IpSecTransformState newState, int expectedLossRate) + throws Exception { + assertEquals( + expectedLossRate, + mPacketLossCalculator.getPacketLossRatePercentage(oldState, newState, TAG)); + } + + private void checkGetPacketLossRate( + IpSecTransformState oldState, + int rxSeqNo, + int packetCount, + int packetInWin, + int expectedDataLossRate) + throws Exception { + final IpSecTransformState newState = + newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin)); + checkGetPacketLossRate(oldState, newState, expectedDataLossRate); + } + + @Test + public void testGetPacketLossRate_replayWindowUnchanged() throws Exception { + checkGetPacketLossRate( + mTransformStateInitial, mTransformStateInitial, PACKET_LOSS_UNAVALAIBLE); + checkGetPacketLossRate(mTransformStateInitial, 3000, 2000, 2000, PACKET_LOSS_UNAVALAIBLE); + } + + @Test + public void testGetPacketLossRate_againstInitialState() throws Exception { + checkGetPacketLossRate(mTransformStateInitial, 7000, 7001, 4096, 0); + checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4096, 15); + checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4000, 14); + } + + @Test + public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_overlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500)); + + checkGetPacketLossRate(oldState, 5000, 5001, 4096, 0); + checkGetPacketLossRate(oldState, 5000, 4000, 4096, 29); + checkGetPacketLossRate(oldState, 5000, 4000, 4000, 27); + } + + @Test + public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_notOverlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500)); + + checkGetPacketLossRate(oldState, 7000, 7001, 4096, 0); + checkGetPacketLossRate(oldState, 7000, 5000, 4096, 37); + checkGetPacketLossRate(oldState, 7000, 5000, 3000, 21); + } + + @Test + public void testGetPktLossRate_oldHiSeqLargerThanWinSize_overlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000)); + + checkGetPacketLossRate(oldState, 12000, 8096, 4096, 0); + checkGetPacketLossRate(oldState, 12000, 7000, 4096, 36); + checkGetPacketLossRate(oldState, 12000, 7000, 3000, 0); + } + + @Test + public void testGetPktLossRate_oldHiSeqLargerThanWinSize_notOverlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000)); + + checkGetPacketLossRate(oldState, 20000, 16096, 4096, 0); + checkGetPacketLossRate(oldState, 20000, 14000, 4096, 19); + checkGetPacketLossRate(oldState, 20000, 14000, 3000, 10); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java index bf84bbeeedad..355c22156a78 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java @@ -20,6 +20,7 @@ import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -29,7 +30,12 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; +import android.net.vcn.FeatureFlags; +import android.os.Handler; +import android.os.IPowerManager; +import android.os.IThermalService; import android.os.ParcelUuid; +import android.os.PowerManager; import android.os.test.TestLooper; import android.telephony.TelephonyManager; @@ -90,32 +96,49 @@ public abstract class NetworkEvaluationTestBase { protected static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface"); + @Mock protected Context mContext; @Mock protected Network mNetwork; + @Mock protected FeatureFlags mFeatureFlags; + @Mock protected com.android.net.flags.FeatureFlags mCoreNetFeatureFlags; @Mock protected TelephonySubscriptionSnapshot mSubscriptionSnapshot; @Mock protected TelephonyManager mTelephonyManager; + @Mock protected IPowerManager mPowerManagerService; protected TestLooper mTestLooper; protected VcnContext mVcnContext; + protected PowerManager mPowerManager; @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - final Context mockContext = mock(Context.class); + when(mNetwork.getNetId()).thenReturn(-1); + mTestLooper = new TestLooper(); mVcnContext = spy( new VcnContext( - mockContext, + mContext, mTestLooper.getLooper(), mock(VcnNetworkProvider.class), false /* isInTestMode */)); doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + doReturn(true).when(mVcnContext).isFlagNetworkMetricMonitorEnabled(); + doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled(); + setupSystemService( - mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); + mContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID); when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID); + + mPowerManager = + new PowerManager( + mContext, + mPowerManagerService, + mock(IThermalService.class), + mock(Handler.class)); + setupSystemService(mContext, mPowerManager, Context.POWER_SERVICE, PowerManager.class); } } diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index dbf2f514fe89..d85c5150f53f 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -57,7 +57,7 @@ public class NetworkPriorityClassifierTest extends NetworkEvaluationTestBase { private UnderlyingNetworkRecord mCellNetworkRecord; @Before - public void setUp() { + public void setUp() throws Exception { super.setUp(); mWifiNetworkRecord = getTestNetworkRecord(WIFI_NETWORK_CAPABILITIES); diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java index a4567ddc20a1..985e70c9771e 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java @@ -34,7 +34,7 @@ public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase { private PersistableBundleWrapper mCarrierConfig; @Before - public void setUp() { + public void setUp() throws Exception { super.setUp(); mCarrierConfig = new PersistableBundleWrapper(new PersistableBundle()); } |