Merge "Fix small issue where overflow expanded view wouldn't show" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index bd1d3e2..e184704 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -373,6 +373,11 @@
name: "android.security.flags-aconfig-java-export",
aconfig_declarations: "android.security.flags-aconfig",
mode: "exported",
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.wifi",
+ ],
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 45f6a3f..a42a8de 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -24166,6 +24166,8 @@
field public static final String KEY_OPERATING_RATE = "operating-rate";
field public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth";
field public static final String KEY_PCM_ENCODING = "pcm-encoding";
+ field @FlaggedApi("android.media.tv.flags.apply_picture_profiles") public static final String KEY_PICTURE_PROFILE_ID = "picture-profile-id";
+ field @FlaggedApi("android.media.tv.flags.apply_picture_profiles") public static final String KEY_PICTURE_PROFILE_INSTANCE = "picture-profile-instance";
field public static final String KEY_PICTURE_TYPE = "picture-type";
field public static final String KEY_PIXEL_ASPECT_RATIO_HEIGHT = "sar-height";
field public static final String KEY_PIXEL_ASPECT_RATIO_WIDTH = "sar-width";
@@ -25613,6 +25615,7 @@
method public void addOnSpatializerStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerStateChangedListener);
method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat);
method public int getImmersiveAudioLevel();
+ method @FlaggedApi("android.media.audio.spatializer_capabilities") @NonNull public java.util.List<java.lang.Integer> getSpatializedChannelMasks();
method public boolean isAvailable();
method public boolean isEnabled();
method public boolean isHeadTrackerAvailable();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index e225bb4..d971f8b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -16196,6 +16196,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_id_from_carrier_identifier") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getCarrierIdFromCarrierIdentifier(@NonNull android.service.carrier.CarrierIdentifier);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e766ae2..42fa9e7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -14453,7 +14453,7 @@
* </ul>
* <p>
* The following methods are supported for the parent instance but can only be called by the
- * profile owner of a managed profile that was created during the device provisioning flow:
+ * profile owner on an <a href="#organization-owned">organization owned</a> managed profile:
* <ul>
* <li>{@link #getPasswordComplexity}</li>
* <li>{@link #setCameraDisabled}</li>
@@ -14461,11 +14461,6 @@
* <li>{@link #setAccountManagementDisabled(ComponentName, String, boolean)}</li>
* <li>{@link #setPermittedInputMethods}</li>
* <li>{@link #getPermittedInputMethods}</li>
- * </ul>
- *
- * <p>The following methods can be called by the profile owner of a managed profile
- * on an organization-owned device:
- * <ul>
* <li>{@link #wipeData}</li>
* </ul>
*
@@ -18177,4 +18172,4 @@
}
return HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3d2d487..02eed1a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7720,6 +7720,7 @@
@IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = {
EXTENDED_FLAG_FILTER_MISMATCH,
EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN,
+ EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ExtendedFlags {}
@@ -7740,6 +7741,13 @@
*/
public static final int EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN = 1 << 1;
+ /**
+ * This flag indicates this intent called {@link #collectExtraIntentKeys()}.
+ *
+ * @hide
+ */
+ public static final int EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED = 1 << 2;
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// toUri() and parseUri() options.
@@ -12328,7 +12336,8 @@
}
private void collectNestedIntentKeysRecur(Set<Intent> visited) {
- if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
+ addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
+ if (mExtras != null && !mExtras.isEmpty()) {
for (String key : mExtras.keySet()) {
Object value = mExtras.get(key);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 23f7629..d5b5258 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10999,6 +10999,25 @@
"emergency_gesture_ui_last_started_millis";
/**
+ * Whether double tap the power button gesture is enabled.
+ *
+ * @hide
+ */
+ @Readable
+ public static final String DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED =
+ "double_tap_power_button_gesture_enabled";
+
+ /**
+ * Double tap power button gesture behavior.
+ * 0 = Camera launch
+ * 1 = Wallet launch
+ * @hide
+ */
+ @Readable
+ public static final String DOUBLE_TAP_POWER_BUTTON_GESTURE =
+ "double_tap_power_button_gesture";
+
+ /**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
*
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 6c92991..357aba3 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -96,6 +96,21 @@
}
flag {
+ name: "prevent_intent_redirect_throw_exception_if_nested_keys_not_collected"
+ namespace: "responsible_apis"
+ description: "Prevent intent redirect attacks by throwing exception if the intent does not collect nested keys"
+ bug: "361143368"
+}
+
+flag {
+ name: "prevent_intent_redirect_collect_nested_keys_on_server_if_not_collected"
+ namespace: "responsible_apis"
+ description: "Prevent intent redirect attacks by collecting nested keys on server if not yet collected"
+ bug: "361143368"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_intent_matching_flags"
is_exported: true
namespace: "permissions"
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6af742f..2e0fe9e 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -256,6 +256,12 @@
}
optional Display display = 100;
+ message DoubleTapPowerButton {
+ optional SettingProto gesture_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto gesture = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional DoubleTapPowerButton double_tap_power_button = 103;
+
message Doze {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -737,5 +743,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 103;
+ // Next tag = 104;
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7f2c816..38ebda7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2731,6 +2731,8 @@
</string-array>
<!-- The list of supported dream complications -->
<integer-array name="config_supportedDreamComplications">
+ <!-- COMPLICATION_TYPE_TIME -->
+ <item>1</item>
</integer-array>
<!-- Are we allowed to dream while not plugged in? -->
diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd
index 0ec8f7d..0a0ca7c 100644
--- a/core/xsd/permission.xsd
+++ b/core/xsd/permission.xsd
@@ -43,6 +43,7 @@
<xs:element name="disabled-until-used-preinstalled-carrier-app" type="disabled-until-used-preinstalled-carrier-app"/>
<xs:element name="privapp-permissions" type="privapp-permissions"/>
<xs:element name="oem-permissions" type="oem-permissions"/>
+ <xs:element name="signature-permissions" type="signature-permissions"/>
<xs:element name="hidden-api-whitelisted-app" type="hidden-api-whitelisted-app"/>
<xs:element name="allow-association" type="allow-association"/>
<xs:element name="bugreport-whitelisted" type="bugreport-whitelisted"/>
@@ -156,6 +157,21 @@
</xs:sequence>
<xs:attribute name="package" type="xs:string"/>
</xs:complexType>
+ <xs:complexType name="signature-permissions">
+ <xs:sequence>
+ <xs:element name="permission" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="deny-permission" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="package" type="xs:string"/>
+ </xs:complexType>
<xs:complexType name="hidden-api-whitelisted-app">
<xs:attribute name="package" type="xs:string"/>
</xs:complexType>
diff --git a/core/xsd/schema/current.txt b/core/xsd/schema/current.txt
index f3beea1..cdec6ab 100644
--- a/core/xsd/schema/current.txt
+++ b/core/xsd/schema/current.txt
@@ -183,6 +183,7 @@
method public java.util.List<com.android.xml.permission.configfile.OemPermissions> getOemPermissions_optional();
method public java.util.List<com.android.xml.permission.configfile.Permission> getPermission_optional();
method public java.util.List<com.android.xml.permission.configfile.PrivappPermissions> getPrivappPermissions_optional();
+ method public java.util.List<com.android.xml.permission.configfile.SignaturePermissions> getSignaturePermissions_optional();
method public java.util.List<com.android.xml.permission.configfile.SplitPermission> getSplitPermission_optional();
method public java.util.List<com.android.xml.permission.configfile.SystemUserBlacklistedApp> getSystemUserBlacklistedApp_optional();
method public java.util.List<com.android.xml.permission.configfile.SystemUserWhitelistedApp> getSystemUserWhitelistedApp_optional();
@@ -209,6 +210,26 @@
method public void setName(String);
}
+ public class SignaturePermissions {
+ ctor public SignaturePermissions();
+ method public java.util.List<com.android.xml.permission.configfile.SignaturePermissions.DenyPermission> getDenyPermission();
+ method public java.util.List<com.android.xml.permission.configfile.SignaturePermissions.Permission> getPermission();
+ method public String get_package();
+ method public void set_package(String);
+ }
+
+ public static class SignaturePermissions.DenyPermission {
+ ctor public SignaturePermissions.DenyPermission();
+ method public String getName();
+ method public void setName(String);
+ }
+
+ public static class SignaturePermissions.Permission {
+ ctor public SignaturePermissions.Permission();
+ method public String getName();
+ method public void setName(String);
+ }
+
public class SplitPermission {
ctor public SplitPermission();
method public java.util.List<com.android.xml.permission.configfile.SplitPermission.Library> getLibrary();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 2945691..dc2025b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -898,7 +898,10 @@
Bubble oldest = mOverflowBubbles.get(mOverflowBubbles.size() - 1);
ProtoLog.d(WM_SHELL_BUBBLES, "overflow full, remove=%s", oldest.getKey());
mStateChange.bubbleRemoved(oldest, Bubbles.DISMISS_OVERFLOW_MAX_REACHED);
- mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED);
+ if (!mPositioner.isShowingInBubbleBar()) {
+ // Only logged for bubbles in stack view
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED);
+ }
mOverflowBubbles.remove(oldest);
mStateChange.removedOverflowBubble = oldest;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index b6fa059..d57044a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -1417,7 +1417,6 @@
mHandleMenu.show(
/* onToDesktopClickListener= */ () -> {
mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON);
- mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON);
return Unit.INSTANCE;
},
/* onToFullscreenClickListener= */ mOnToFullscreenClickListener,
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
index 176020f..6d12b00 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
@@ -49,6 +49,7 @@
"wm-flicker-common-assertions",
"launcher-helper-lib",
"launcher-aosp-tapl",
+ "com_android_wm_shell_flags_lib",
],
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 6d396ea..9c71510 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -17,12 +17,16 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByGoHomeBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
import com.android.wm.shell.flicker.utils.appWindowBecomesInvisible
@@ -30,6 +34,7 @@
import com.android.wm.shell.flicker.utils.splitAppLayerBoundsBecomesInvisible
import com.android.wm.shell.flicker.utils.splitScreenDividerBecomesInvisible
import org.junit.FixMethodOrder
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -44,8 +49,13 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class DismissSplitScreenByGoHome(override val flicker: LegacyFlickerTest) :
DismissSplitScreenByGoHomeBenchmark(flicker), ICommonAssertions {
+ @JvmField
+ @Rule
+ val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
override val transition: FlickerBuilder.() -> Unit
get() = {
defaultSetup(this)
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 08b0dd3..54a87ad 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -640,6 +640,9 @@
boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
+ /* Returns a List<Integer> */
+ List getSpatializedChannelMasks();
+
void registerSpatializerCallback(in ISpatializerCallback cb);
void unregisterSpatializerCallback(in ISpatializerCallback cb);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 4f94c3e..5038754 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -16,12 +16,12 @@
package android.media;
-
import static android.media.audio.Flags.FLAG_IAMF_DEFINITIONS_API;
+import static android.media.codec.Flags.FLAG_APV_SUPPORT;
import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
import static android.media.codec.Flags.FLAG_NUM_INPUT_SLOTS;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
-import static android.media.codec.Flags.FLAG_APV_SUPPORT;
+import static android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES;
import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE;
import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
@@ -1796,6 +1796,27 @@
public static final String KEY_NUM_SLOTS = "num-slots";
/**
+ * A key describing the picture profile ID to be applied to {@link MediaCodec}.
+ * <p>
+ * The associated value is a string.
+ * <p>
+ * @see {@link android.media.quality.PictureProfile}
+ * @see {@link android.media.quality.PictureProfile#getProfileId}
+ */
+ @FlaggedApi(FLAG_APPLY_PICTURE_PROFILES)
+ public static final String KEY_PICTURE_PROFILE_ID = "picture-profile-id";
+
+ /**
+ * A key describing the picture profile instance to be applied to {@link MediaCodec}.
+ * <p>
+ * The associated value is an instance of {@link android.media.quality.PictureProfile}.
+ * <p>
+ * @see {@link android.media.quality.PictureProfile}
+ */
+ @FlaggedApi(FLAG_APPLY_PICTURE_PROFILES)
+ public static final String KEY_PICTURE_PROFILE_INSTANCE = "picture-profile-instance";
+
+ /**
* QpOffsetRect constitutes the metadata required for encoding a region of interest in an
* image or a video frame. The region of interest is represented by a rectangle. The four
* integer coordinates of the rectangle are stored in fields left, top, right, bottom.
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index 99fcaf2..95c4ad3 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -16,7 +16,10 @@
package android.media;
+import static android.media.audio.Flags.FLAG_SPATIALIZER_CAPABILITIES;
+
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -35,6 +38,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -527,6 +531,28 @@
}
/**
+ * Returns a list of channel masks that represent the widest channel masks the spatializer
+ * is capable of rendering with individual channel positions.
+ * For instance a spatializer may only support virtual speaker positions for 5.1, it would
+ * therefore return {@link AudioFormat#CHANNEL_OUT_5POINT1}. But it would still return
+ * <code>true</code> when querying {@link #canBeSpatialized(AudioAttributes, AudioFormat)} it
+ * with a channel mask of {@link AudioFormat#CHANNEL_OUT_7POINT1POINT2}: the sound present
+ * in each channel would still be heard, but the sounds from the rear, side and top pairs would
+ * be mixed together, and be spatialized at the same location.
+ * @return a list of channel masks following the <code>CHANNEL_OUT_*</code> output channel
+ * definitions found in {@link AudioFormat}.
+ */
+ @FlaggedApi(FLAG_SPATIALIZER_CAPABILITIES)
+ public @NonNull List<Integer> getSpatializedChannelMasks() {
+ try {
+ return mAm.getService().getSpatializedChannelMasks();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying getSpatializedChannelMasks", e);
+ return Collections.emptyList();
+ }
+ }
+
+ /**
* Adds a listener to be notified of changes to the enabled state of the
* {@code Spatializer}.
* @param executor the {@code Executor} handling the callback
diff --git a/media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl b/media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl
new file mode 100644
index 0000000..550acba
--- /dev/null
+++ b/media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.analog;
+
+/**
+ * @hide
+ */
+interface IAnalogAttributeInterface {
+ int getVersion();
+ void setColorSystemCapability(in String[] list);
+ String[] getColorSystemCapability();
+}
diff --git a/media/java/android/media/tv/extension/event/IEventDownload.aidl b/media/java/android/media/tv/extension/event/IEventDownload.aidl
new file mode 100644
index 0000000..29c7553
--- /dev/null
+++ b/media/java/android/media/tv/extension/event/IEventDownload.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.event;
+
+import android.media.tv.extension.event.IEventDownloadListener;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * @hide
+ */
+interface IEventDownload {
+ // Create an event download session and return it as a Ibinder for DVB/DTMB
+ IBinder createSession(in Bundle eventDownloadParams, in IEventDownloadListener listener);
+}
diff --git a/media/java/android/media/tv/extension/event/IEventDownloadListener.aidl b/media/java/android/media/tv/extension/event/IEventDownloadListener.aidl
new file mode 100644
index 0000000..6d7d61f
--- /dev/null
+++ b/media/java/android/media/tv/extension/event/IEventDownloadListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.event;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface IEventDownloadListener {
+ void onCompleted(in Bundle status);
+}
diff --git a/media/java/android/media/tv/extension/event/IEventDownloadSession.aidl b/media/java/android/media/tv/extension/event/IEventDownloadSession.aidl
new file mode 100644
index 0000000..fe7ee37
--- /dev/null
+++ b/media/java/android/media/tv/extension/event/IEventDownloadSession.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.event;
+
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IEventDownloadSession {
+ // Determine to execute barker channel or silent tune flow for related service type
+ int isBarkerOrSequentialDownloadByServiceType(in Bundle eventDownloadParams);
+ // Determine whether to start barker channel or silent tune flow.
+ int isBarkerOrSequentialDownloadByServiceRecord(in Bundle eventDownloadParams);
+ // Start event download.
+ void startTuningMultiplex(in Uri channelUri);
+ // Set active window channels.
+ void setActiveWindowChannelInfo(in Uri[] activeWinChannelInfos);
+ // Cancel barker channel or silent tune flow.
+ void cancel();
+ // Release barker channel or silent tune flow.
+ void release();
+}
diff --git a/media/java/android/media/tv/extension/event/IEventMonitor.aidl b/media/java/android/media/tv/extension/event/IEventMonitor.aidl
new file mode 100644
index 0000000..f6e7bd1
--- /dev/null
+++ b/media/java/android/media/tv/extension/event/IEventMonitor.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.event;
+
+import android.media.tv.extension.event.IEventMonitorListener;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IEventMonitor {
+ // Get present event information.
+ Bundle getPresentEventInfo(long channelDbId);
+ // Add present event information listener.
+ void addPresentEventInfoListener(in IEventMonitorListener listener);
+ // Remove present event information listener.
+ void removePresentEventInfoListener(in IEventMonitorListener listener);
+ // Get following event information.
+ Bundle getFollowingEventInfo(long channelDbId);
+ // Add following event information listener.
+ void addFollowingEventInfoListener(in IEventMonitorListener listener);
+ // Remove following event information listener.
+ void removeFollowingEventInfoListener(in IEventMonitorListener listener);
+ // Get SDT guidance information.
+ Bundle getSdtGuidanceInfo(long channelDbId);
+ // Set Event Background channel list info.
+ void setBgmTuneChannelInfo(in Uri[] tuneChannelInfos);
+}
diff --git a/media/java/android/media/tv/extension/event/IEventMonitorListener.aidl b/media/java/android/media/tv/extension/event/IEventMonitorListener.aidl
new file mode 100644
index 0000000..a00e542
--- /dev/null
+++ b/media/java/android/media/tv/extension/event/IEventMonitorListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.event;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface IEventMonitorListener {
+ void onInfoChanged(long channelDbId, in Bundle eventinfo);
+}
diff --git a/media/java/android/media/tv/extension/scan/IFavoriteNetwork.aidl b/media/java/android/media/tv/extension/scan/IFavoriteNetwork.aidl
new file mode 100644
index 0000000..ff78aa4
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IFavoriteNetwork.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.IFavoriteNetworkListener;
+import android.os.Bundle;
+
+/**
+ * Country: Norway
+ * Broadcast Type: BROADCAST_TYPE_DVB_T
+ * (Operator: RiksTV)
+ *
+ * @hide
+ */
+interface IFavoriteNetwork {
+ // Get the favorite network information,If there are no conflicts, the array of Bundle is empty.
+ Bundle[] getFavoriteNetworks();
+ // Select and set one of two or more favorite networks detected by the service scan.
+ int setFavoriteNetwork(in Bundle favoriteNetworkSettings);
+ // Set the listener to be invoked when two or more favorite networks are detected.
+ int setListener(in IFavoriteNetworkListener listener);
+}
diff --git a/media/java/android/media/tv/extension/scan/IFavoriteNetworkListener.aidl b/media/java/android/media/tv/extension/scan/IFavoriteNetworkListener.aidl
new file mode 100644
index 0000000..6994224
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IFavoriteNetworkListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface IFavoriteNetworkListener {
+ void onDetectFavoriteNetwork(in Bundle detectFavoriteNetworks);
+}
diff --git a/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl b/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl
new file mode 100644
index 0000000..cdf6e23
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+/**
+ * @hide
+ */
+interface IHDPlusInfo {
+ // Specifying a HDPlusInfo and start a network scan.
+ int setHDPlusInfo(String isBlindScanContinue, String isHDMode);
+}
diff --git a/media/java/android/media/tv/extension/scan/ILcnConflict.aidl b/media/java/android/media/tv/extension/scan/ILcnConflict.aidl
new file mode 100644
index 0000000..5dff39e
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ILcnConflict.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.ILcnConflictListener;
+import android.os.Bundle;
+
+/**
+ * Country: Italy, France
+ * Broadcast Type: BROADCAST_TYPE_DVB_T
+ *
+ * @hide
+ */
+interface ILcnConflict {
+ // Get the LCN conflict groups information, If there are no conflicts, the array of Bundle is empty.
+ Bundle[] getLcnConflictGroups();
+ // Resolve LCN conflicts caused by service scans.
+ int resolveLcnConflict(in Bundle[] lcnConflictSettings);
+ // Set the listener to be invoked the LCN conflict event.
+ int setListener(in ILcnConflictListener listener);
+}
diff --git a/media/java/android/media/tv/extension/scan/ILcnConflictListener.aidl b/media/java/android/media/tv/extension/scan/ILcnConflictListener.aidl
new file mode 100644
index 0000000..6bbbeb8
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ILcnConflictListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface ILcnConflictListener {
+ void onDetectLcnConflict(in Bundle detectLcnConflicts);
+}
diff --git a/media/java/android/media/tv/extension/scan/ILcnV2ChannelList.aidl b/media/java/android/media/tv/extension/scan/ILcnV2ChannelList.aidl
new file mode 100644
index 0000000..f9a9d34
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ILcnV2ChannelList.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.ILcnV2ChannelListListener;
+import android.os.Bundle;
+
+/**
+ * Country: (NorDig etc.)
+ * Broadcast Type: BROADCAST_TYPE_DVB_T, BROADCAST_TYPE_DVB_C
+ *
+ * @hide
+ */
+interface ILcnV2ChannelList {
+ // Get the LCN V2 channel list information. If there are no conflicts, the array of Bundle is empty.
+ Bundle[] getLcnV2ChannelLists();
+ // Select and set one of two or more LCN V2 channel list detected by the service scan.
+ int setLcnV2ChannelList(in Bundle lcnV2ChannelListSettings);
+ // Set the listener to be invoked when two or more LCN V2 channel list are detected.
+ int setListener(in ILcnV2ChannelListListener listener);
+}
diff --git a/media/java/android/media/tv/extension/scan/ILcnV2ChannelListListener.aidl b/media/java/android/media/tv/extension/scan/ILcnV2ChannelListListener.aidl
new file mode 100644
index 0000000..cbdb83c
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ILcnV2ChannelListListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface ILcnV2ChannelListListener {
+ void onDetectLcnV2ChannelList(in Bundle detectLcnV2ChannelList);
+}
diff --git a/media/java/android/media/tv/extension/scan/IOperatorDetection.aidl b/media/java/android/media/tv/extension/scan/IOperatorDetection.aidl
new file mode 100644
index 0000000..770f866
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IOperatorDetection.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.IOperatorDetectionListener;
+import android.os.Bundle;
+
+/**
+ * Country: Any
+ * Broadcast Type: BROADCAST_TYPE_DVB_S
+ * (Operator: M7)
+ *
+ * @hide
+ */
+interface IOperatorDetection {
+ // Set the operator selected info for scanning.
+ int setOperatorDetection(in Bundle operatorSelected);
+ // Set the listener to be invoked when one or more operator detection has been detected by
+ // operator detection searches.
+ int setListener(in IOperatorDetectionListener listener);
+}
diff --git a/media/java/android/media/tv/extension/scan/IOperatorDetectionListener.aidl b/media/java/android/media/tv/extension/scan/IOperatorDetectionListener.aidl
new file mode 100644
index 0000000..7dcd461
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IOperatorDetectionListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
+
+/**
+ * @hide
+ */
+oneway interface IOperatorDetectionListener {
+ void onDetectOperatorDetectionList(in Bundle[] detectOperatorDetectionList);
+}
diff --git a/media/java/android/media/tv/extension/scan/IRegionChannelList.aidl b/media/java/android/media/tv/extension/scan/IRegionChannelList.aidl
new file mode 100644
index 0000000..fe755f8
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IRegionChannelList.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.IRegionChannelListListener;
+
+/**
+ * @hide
+ */
+interface IRegionChannelList {
+ // Set the region channel list for scanning.
+ int setRegionChannelList(String regionChannelList);
+ // Set the listener to be invoked when one or more region channel list has been detected by
+ // region channel list searches.
+ int setListener(in IRegionChannelListListener listener);
+}
diff --git a/media/java/android/media/tv/extension/scan/IRegionChannelListListener.aidl b/media/java/android/media/tv/extension/scan/IRegionChannelListListener.aidl
new file mode 100644
index 0000000..06b0eb5
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IRegionChannelListListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+/**
+ * @hide
+ */
+oneway interface IRegionChannelListListener {
+ void onDetectRegionChannelList(in String[] detectRegionChannelList);
+}
diff --git a/media/java/android/media/tv/extension/scan/IScanInterface.aidl b/media/java/android/media/tv/extension/scan/IScanInterface.aidl
new file mode 100644
index 0000000..b44d1d2
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IScanInterface.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.IScanListener;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IScanInterface {
+ IBinder createSession(int broadcastType, String countryCode, String operator,
+ in IScanListener listener);
+ Bundle getParameters(int broadcastType, String countryCode, String operator,
+ in Bundle params);
+}
diff --git a/media/java/android/media/tv/extension/scan/IScanListener.aidl b/media/java/android/media/tv/extension/scan/IScanListener.aidl
new file mode 100644
index 0000000..2c4807f
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IScanListener.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface IScanListener {
+ // notify events during scan.
+ void onEvent(in Bundle eventArgs);
+ // notify the scan progress.
+ void onScanProgress(String scanProgress, in Bundle scanProgressInfo);
+ // notify the scan completion.
+ void onScanCompleted(int scanResult);
+ // notify that the temporaily held channel list is stored.
+ void onStoreCompleted(int storeResult);
+}
diff --git a/media/java/android/media/tv/extension/scan/IScanSatSearch.aidl b/media/java/android/media/tv/extension/scan/IScanSatSearch.aidl
new file mode 100644
index 0000000..b8074fc
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IScanSatSearch.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+/**
+ * For satellite search function.
+ * @hide
+ */
+interface IScanSatSearch {
+ // Set currecnt LNB as customized LNB, default LNB is universal LNB
+ int setCustomizedLnb(String customizedLnb);
+}
diff --git a/media/java/android/media/tv/extension/scan/IScanSession.aidl b/media/java/android/media/tv/extension/scan/IScanSession.aidl
new file mode 100644
index 0000000..d42eca1
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/IScanSession.aidl
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IScanSession {
+ // Start a service scan.
+ int startScan(int broadcastType, String countryCode, String operator, in int[] frequency,
+ String scanType, String languageCode);
+ // Reset the scan information held in TIS.
+ int resetScan();
+ // Cancel scan.
+ int cancelScan();
+
+ // Get available interface for created ScanExtension interface.
+ String[] getAvailableExtensionInterfaceNames();
+ // Get extension interface for Scan.
+ IBinder getExtensionInterface(String name);
+
+ // Clear the results of the service scan from the service database.
+ int clearServiceList(in Bundle optionalClearParams);
+ // Store the results of the service scan from the service database.
+ int storeServiceList();
+ // Get a service information specified by the service information ID.
+ Bundle getServiceInfo(String serviceInfoId, in String[] keys);
+ // Get a service information ID list.
+ String[] getServiceInfoIdList();
+ // Get a list of service info by the filter.
+ Bundle getServiceInfoList(in Bundle filterInfo, in String[] keys);
+ // Update the service information.
+ int updateServiceInfo(in Bundle serviceInfo);
+ // Updates the service information for the specified service information ID in array list.
+ int updateServiceInfoByList(in Bundle[] serviceInfo);
+
+ /* DVBI specific functions */
+ // Get all of the serviceLists, parsed from Local TV storage, Broadcast, USB file discovery.
+ Bundle getServiceLists();
+ // Users choose one serviceList from the serviceLists, and install the services.
+ int setServiceList(int serviceListRecId);
+ // Get all of the packageData, parsed from the selected serviceList XML.
+ Bundle getPackageData();
+ // Choose the package using package id and install the corresponding services.
+ int setPackage(String packageId);
+ // Get all of the countryRegionData, parsed from the selected serviceList XML.
+ Bundle getCountryRegionData();
+ // Choose the countryRegion using countryRegion id, and install the corresponding services.
+ int setCountryRegion(String regionId);
+ // Get all of the regionData, parsed from the selected serviceList XML.
+ Bundle getRegionData();
+ // Choose the region using the regionData id, and install the corresponding services.
+ int setRegion(String regionId);
+
+ // Get unique session token for the scan.
+ String getSessionToken();
+ // Release scan resource, the register listener will be released.
+ int release();
+}
diff --git a/media/java/android/media/tv/extension/scan/ITargetRegion.aidl b/media/java/android/media/tv/extension/scan/ITargetRegion.aidl
new file mode 100644
index 0000000..417e122
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ITargetRegion.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.ITargetRegionListener;
+
+import android.os.Bundle;
+
+/**
+ * Country: U.K.
+ * Broadcast Type: BROADCAST_TYPE_DVB_T
+ *
+ * @hide
+ */
+interface ITargetRegion {
+ // Get the target regions information. If there are no conflicts, the array of Bundle is empty.
+ Bundle[] getTargetRegions();
+ // Select and set one of two or more target region detected by the service scan.
+ int setTargetRegion(in Bundle targetRegionSettings);
+ // Set the listener to be invoked when two or more regions are detected.
+ int setListener(in ITargetRegionListener listener);
+}
diff --git a/media/java/android/media/tv/extension/scan/ITargetRegionListener.aidl b/media/java/android/media/tv/extension/scan/ITargetRegionListener.aidl
new file mode 100644
index 0000000..9d6aa8e
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ITargetRegionListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface ITargetRegionListener {
+ void onDetectTargetRegion(in Bundle detectTargetRegions);
+}
diff --git a/media/java/android/media/tv/extension/scan/ITkgsInfo.aidl b/media/java/android/media/tv/extension/scan/ITkgsInfo.aidl
new file mode 100644
index 0000000..f25952c
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ITkgsInfo.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+import android.media.tv.extension.scan.ITkgsInfoListener;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface ITkgsInfo {
+ int setPrefServiceList(String prefServiceList);
+ int setTkgsInfoListener(in ITkgsInfoListener listener);
+}
diff --git a/media/java/android/media/tv/extension/scan/ITkgsInfoListener.aidl b/media/java/android/media/tv/extension/scan/ITkgsInfoListener.aidl
new file mode 100644
index 0000000..e3dcf2d
--- /dev/null
+++ b/media/java/android/media/tv/extension/scan/ITkgsInfoListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scan;
+
+/**
+ * @hide
+ */
+oneway interface ITkgsInfoListener {
+ void onServiceList(in String[] serviceList);
+ void onTableVersionUpdate(int tableVersion);
+ void onUserMessage(String strMessage);
+}
diff --git a/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl
new file mode 100644
index 0000000..bda60ed
--- /dev/null
+++ b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scanbsu;
+
+import android.media.tv.extension.scanbsu.IScanBackgroundServiceUpdateListener;
+
+/**
+ * @hide
+ */
+interface IScanBackgroundServiceUpdate {
+ // Set the listener for background service update
+ // receives notifications for svl/tsl/nwl update during background service update.
+ void addBackgroundServiceUpdateListener(String clientToken,
+ in IScanBackgroundServiceUpdateListener listener);
+ // Remove the listener for background service update to stop receiving notifications
+ // for svl/tsl/nwl update during background service update.
+ void removeBackgroundServiceUpdateListener(in IScanBackgroundServiceUpdateListener listener);
+}
diff --git a/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl
new file mode 100644
index 0000000..d9bcbed
--- /dev/null
+++ b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.scanbsu;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IScanBackgroundServiceUpdateListener {
+ // On background service update add/delete/update svl records.
+ void onChannelListUpdate(String sessionToken, out Bundle[] updateInfos);
+ // On background service update add/delete/update nwl records.
+ void onNetworkListUpdate(String sessionToken, out Bundle[] updateInfos);
+ // On background service update add/delete/update tsl records.
+ void onTransportStreamingListUpdate(String sessionToken, out Bundle[] updateInfos);
+}
diff --git a/media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl b/media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl
new file mode 100644
index 0000000..57f3b4a
--- /dev/null
+++ b/media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.screenmode;
+
+/**
+ * @hide
+ */
+interface IScreenModeSettings {
+ // Set screen mode information using a JSON string.
+ void setScreenModeSettings(String sessionToken, String setting);
+ // Get the overscan index which TIS session is applied.
+ int getOverScanIndex(String sessionToken);
+ // Get status that TIS session is support overscan or not.
+ boolean getSupportApplyOverScan(String sessionToken);
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl b/media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl
new file mode 100644
index 0000000..cb6aecc
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * @hide
+ */
+interface IChannelListTransfer {
+ // Parse XML file and import Channels information.
+ void importChannelList(in ParcelFileDescriptor pfd);
+ // Get Channels information for export and create XML file.
+ void exportChannelList(in ParcelFileDescriptor pfd);
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceList.aidl b/media/java/android/media/tv/extension/servicedb/IServiceList.aidl
new file mode 100644
index 0000000..51daa80
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceList.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IServiceList {
+ // Get a list of the Service list IDs quivalent to COLUMN_CHANNEL_LIST_ID
+ // in the Channels table of TvProvider.
+ String[] getServiceListIds();
+ // Get the information associated with the Service list.
+ Bundle getServiceListInfo(String serviceListId, in String[] keys);
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl
new file mode 100644
index 0000000..1b1577f
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+import android.media.tv.extension.servicedb.IServiceListEditListener;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IServiceListEdit {
+ // Open in edit mode. Must call close() after edit is done.
+ int open(IServiceListEditListener listener);
+ // Method to close in edit mode.
+ int close();
+ // Method to commit changes made to service database.
+ int commit();
+ // Method to commit and close the changes.
+ int userEditCommit();
+
+ // Get a service/transportStream/Network/Satellite record information specified by
+ // serviceInfoId and keys from tvdb.
+ Bundle getServiceInfoFromDatabase(String serviceInfoId, in String[] keys);
+ // Get a list of all service records' information specified by serviceListId and keys from tvdb.
+ Bundle getServiceInfoListFromDatabase(String serviceListId, in String[] keys);
+ // Get a list of all service info IDs in the service list of serviceListId from tvdb.
+ String[] getServiceInfoIdsFromDatabase(String inServiceListId);
+ // Update a service information by the contents of serviceInfo;
+ int updateServiceInfoFromDatabase(in Bundle updateServiceInfo);
+ // Update all service information by the contents of serviceInfoList.
+ int updateServiceInfoByListFromDatabase(in Bundle[] updateServiceInfoList);
+ // Remove a service information of the serviceInfoId from the service list.
+ int removeServiceInfoFromDatabase(String serviceInfoId);
+ // Remove all service information of the serviceInfoId from the service list.
+ int removeServiceInfoByListFromDatabase(in String[] serviceInfoIdList);
+ // Get a list of the Service list IDs which is equivalent to COLUMN_CHANNEL_LIST_ID
+ // in Channels table from tv db.
+ String[] getServiceListChannelIds();
+ // Get the information associated with the Service list Channel id.
+ Bundle getServiceListInfoByChannelId(String serviceListChannelId, in String[] keys);
+
+ // Get a list of transportStream records' information specified by serviceListId and keys.
+ Bundle getTransportStreamInfoList(String serviceListId, in String[] keys);
+ // Get a list of transportStream records' information specified by serviceListId and keys
+ // from work db.
+ Bundle getTransportStreamInfoListForce(String serviceListId, in String[] keys);
+
+ // Get a list of network records' information specified by serviceListId and keys.
+ Bundle getNetworkInfoList(String serviceListId, in String[] keys);
+ // Get a list of satellite records' information specified by serviceListId and keys.
+ Bundle getSatelliteInfoList(String serviceListId, in String[] keys);
+
+ // Decompress whole bundle value of single service/transportStream/Network/Satellite record.
+ // RecordInfoBundle:a single record got from database by getServiceInfoFromDatabase()
+ String toRecordInfoByType(in Bundle recordInfoBundle, String recordType);
+ // Set channels(tv.db) modified result to middleware database(SVL/TSL/NWL/SATL).
+ int putRecordIdList(String serviceListId, in Bundle recordIdListBundle, int optType);
+
+ // Add predefined ServiceListInfo of Hotbird 13E in scan two satellite scene EU region
+ // following by commit().
+ String addPredefinedServiceListInfo(int broadcastType, String serviceListType,
+ String serviceListPrefix, String countryCode, int operatorId);
+ // Add predefined channels of Hotbird 13E in scan two satellite scene EU region.
+ int addPredefinedChannelList(String serviceListId, in Bundle[] predefinedListBundle);
+ // Add predefined satellite info of Hotbird 13E in scan two satellite scene EU region.
+ int addPredefinedSatInfo(String serviceListId, in Bundle predefinedSatInfoBundle);
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl
new file mode 100644
index 0000000..e227eda
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+/**
+ * @hide
+ */
+oneway interface IServiceListEditListener {
+ void onCompleted(int requestId, int result);
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl
new file mode 100644
index 0000000..c57e8f9
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+/**
+ * @hide
+ */
+oneway interface IServiceListExportListener {
+ void onExported(int exportResult);
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl
new file mode 100644
index 0000000..fcde581
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * @hide
+ */
+interface IServiceListExportSession {
+ // Start export service list with reserved parameters.
+ int exportServiceList(in ParcelFileDescriptor pfd, in Bundle exportParams);
+ // Release export resources.
+ int release();
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl
new file mode 100644
index 0000000..abd8320
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+/**
+ * @hide
+ */
+interface IServiceListImportListener {
+ void onImported(int importResult);
+ void onPreloaded(int preloadResult);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl
new file mode 100644
index 0000000..1f1ae01
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * @hide
+ */
+interface IServiceListImportSession {
+ // Start import service list. Should call after preload and before release.
+ int importServiceList(in ParcelFileDescriptor pfd, in Bundle importParams);
+ // Preparing for import.
+ int preload(in ParcelFileDescriptor pfd);
+ // Release import resources.
+ int release();
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl
new file mode 100644
index 0000000..7c9c5c8
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+/**
+ * @hide
+ */
+oneway interface IServiceListSetChannelListListener {
+ void onCompleted(int setChannelListResult);
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl
new file mode 100644
index 0000000..b0527b3
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface IServiceListSetChannelListSession {
+ // Set channelList with channelinfo bundles, serviceListInfo, and operation type.
+ int setChannelList(in Bundle[] channelsInfo, in Bundle ServiceListInfoBundle, int optType);
+ // Release set channellist resources.
+ int release();
+}
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl
new file mode 100644
index 0000000..91fb157
--- /dev/null
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.servicedb;
+
+import android.media.tv.extension.servicedb.IServiceListExportListener;
+import android.media.tv.extension.servicedb.IServiceListImportListener;
+import android.media.tv.extension.servicedb.IServiceListSetChannelListListener;
+import android.os.IBinder;
+
+/**
+ * @hide
+ */
+interface IServiceListTransferInterface {
+ IBinder createExportSession(in IServiceListExportListener listener);
+ IBinder createImportSession(in IServiceListImportListener listener);
+ IBinder createSetChannelListSession(in IServiceListSetChannelListListener listener);
+}
diff --git a/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.aidl b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.aidl
new file mode 100644
index 0000000..a3725e4
--- /dev/null
+++ b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.teletext;
+
+import android.media.tv.extension.teletext.IDataServiceSignalInfoListener;
+import android.os.Bundle;
+
+
+/**
+ * @hide
+ */
+interface IDataServiceSignalInfo {
+ // Get Teletext data service signal information.
+ Bundle getDataServiceSignalInfo(String sessionToken);
+ // Add a listener that receives notifications of teletext running information.
+ void addDataServiceSignalInfoListener(String clientToken,
+ IDataServiceSignalInfoListener listener);
+ // Remove a listener that receives notifications of Teletext running information.
+ void removeDataServiceSignalInfoListener(String clientToken,
+ IDataServiceSignalInfoListener listener);
+}
diff --git a/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl
new file mode 100644
index 0000000..0e99bf5
--- /dev/null
+++ b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.teletext;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface IDataServiceSignalInfoListener {
+ void onDataServiceSignalInfoChanged (String sessionToken, in Bundle changedSignalInfo);
+}
diff --git a/media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl b/media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl
new file mode 100644
index 0000000..c96ffc0
--- /dev/null
+++ b/media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.teletext;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+interface ITeletextPageSubCode {
+ // Get Teletext page number
+ Bundle getTeletextPageNumber(String sessionToken);
+ // Set Teletext page number.
+ void setTeleltextPageNumber(String sessionToken, int pageNumber);
+ // Get Teletext sub page number.
+ Bundle getTeletextPageSubCode(String sessionToken);
+ // Set Teletext sub page number.
+ void setTeletextPageSubCode(String sessionToken, int pageSubCode);
+ // Get Teletext TopInfo.
+ Bundle getTeletextHasTopInfo(String sessionToken);
+ // Get Teletext TopBlockList.
+ Bundle getTeletextTopBlockList(String sessionToken);
+ // Get Teletext TopGroupList.
+ Bundle getTeletextTopGroupList(String sessionToken, int indexGroup);
+ // Get Teletext TopPageList.
+ Bundle getTeletextTopPageList(String sessionToken, int indexPage);
+}
diff --git a/media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl b/media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl
new file mode 100644
index 0000000..88e5084
--- /dev/null
+++ b/media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.tune;
+
+import android.media.tv.extension.tune.IChannelTunedListener;
+
+/*
+* @hide
+*/
+interface IChannelTunedInterface {
+ void addChannelTunedListener(in IChannelTunedListener listener);
+ void removeChannelTunedListener(in IChannelTunedListener listener);
+}
diff --git a/media/java/android/media/tv/extension/tune/IChannelTunedListener.aidl b/media/java/android/media/tv/extension/tune/IChannelTunedListener.aidl
new file mode 100644
index 0000000..4687546
--- /dev/null
+++ b/media/java/android/media/tv/extension/tune/IChannelTunedListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.tune;
+
+import android.os.Bundle;
+
+/*
+* @hide
+*/
+oneway interface IChannelTunedListener {
+ void onChannelTuned(String sessionToken, in Bundle channelTunedInfo);
+}
diff --git a/media/java/android/media/tv/extension/tune/IMuxTune.aidl b/media/java/android/media/tv/extension/tune/IMuxTune.aidl
new file mode 100644
index 0000000..4e9dbda
--- /dev/null
+++ b/media/java/android/media/tv/extension/tune/IMuxTune.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.tune;
+
+import android.media.tv.extension.tune.IMuxTuneSession;
+
+/**
+* @hide
+*/
+interface IMuxTune {
+ IMuxTuneSession createSession(int broadcastType, String clientToken);
+}
diff --git a/media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl b/media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl
new file mode 100644
index 0000000..5ad72b4
--- /dev/null
+++ b/media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.extension.tune;
+
+import android.os.Bundle;
+
+/**
+* @hide
+*/
+interface IMuxTuneSession {
+ // Start mux tune with tune params.
+ void start(int broadcastType, int frequency, int brandwith, in Bundle muxTuneParams);
+ // Stop mux tune.
+ void stop();
+ // Release muxtune resources.
+ void release();
+ // Get the session token created by TIS to identify different sessions.
+ String getSessionToken();
+}
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 9ada14b..1019a10 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -60,14 +60,15 @@
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.nfc.RoutingStatus getRoutingStatus();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.List<android.nfc.NfcRoutingTableEntry> getRoutingTable();
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public android.nfc.T4tNdefNfcee getT4tNdefNfcee();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean hasUserEnabledNfc();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAutoChangeEnabled();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagPresent();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overwriteRoutingTable(int, int, int, int);
- method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void pausePolling(int);
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int pausePolling(int);
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback);
- method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void resumePolling();
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int resumePolling();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAutoChangeEnabled(boolean);
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void setControllerAlwaysOnMode(int);
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState();
@@ -83,6 +84,8 @@
field public static final int HCE_ACTIVATE = 1; // 0x1
field public static final int HCE_DATA_TRANSFERRED = 2; // 0x2
field public static final int HCE_DEACTIVATE = 3; // 0x3
+ field public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; // 0x2
+ field public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; // 0x1
field public static final int STATUS_OK = 0; // 0x0
field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
}
@@ -120,6 +123,11 @@
@FlaggedApi("android.nfc.nfc_oem_extension") public abstract class NfcRoutingTableEntry {
method public int getNfceeId();
+ method public int getType();
+ field public static final int TYPE_AID = 0; // 0x0
+ field public static final int TYPE_PROTOCOL = 1; // 0x1
+ field public static final int TYPE_SYSTEM_CODE = 3; // 0x3
+ field public static final int TYPE_TECHNOLOGY = 2; // 0x2
}
@FlaggedApi("android.nfc.nfc_oem_extension") public final class OemLogItems implements android.os.Parcelable {
@@ -179,6 +187,47 @@
field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_V = 3; // 0x3
}
+ @FlaggedApi("android.nfc.nfc_oem_extension") public final class T4tNdefNfcee {
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public int clearData();
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isOperationOngoing();
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isSupported();
+ method @Nullable @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public android.nfc.T4tNdefNfceeCcFileInfo readCcfile();
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public byte[] readData(@IntRange(from=0, to=65535) int);
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public int writeData(@IntRange(from=0, to=65535) int, @NonNull byte[]);
+ field public static final int CLEAR_DATA_FAILED_INTERNAL = 0; // 0x0
+ field public static final int CLEAR_DATA_SUCCESS = 1; // 0x1
+ field public static final int WRITE_DATA_ERROR_CONNECTION_FAILED = -6; // 0xfffffffa
+ field public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7; // 0xfffffff9
+ field public static final int WRITE_DATA_ERROR_INTERNAL = -1; // 0xffffffff
+ field public static final int WRITE_DATA_ERROR_INVALID_FILE_ID = -4; // 0xfffffffc
+ field public static final int WRITE_DATA_ERROR_INVALID_LENGTH = -5; // 0xfffffffb
+ field public static final int WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED = -8; // 0xfffffff8
+ field public static final int WRITE_DATA_ERROR_NFC_NOT_ON = -3; // 0xfffffffd
+ field public static final int WRITE_DATA_ERROR_RF_ACTIVATED = -2; // 0xfffffffe
+ field public static final int WRITE_DATA_SUCCESS = 0; // 0x0
+ }
+
+ @FlaggedApi("android.nfc.nfc_oem_extension") public final class T4tNdefNfceeCcFileInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=15, to=32767) public int getCcFileLength();
+ method @IntRange(from=0xffffffff, to=65535) public int getFileId();
+ method @IntRange(from=15, to=65535) public int getMaxReadLength();
+ method @IntRange(from=5, to=32767) public int getMaxSize();
+ method @IntRange(from=13, to=65535) public int getMaxWriteLength();
+ method public int getReadAccess();
+ method public int getVersion();
+ method public int getWriteAccess();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.nfc.T4tNdefNfceeCcFileInfo> CREATOR;
+ field public static final int READ_ACCESS_GRANTED_RESTRICTED = 128; // 0x80
+ field public static final int READ_ACCESS_GRANTED_UNRESTRICTED = 0; // 0x0
+ field public static final int VERSION_2_0 = 32; // 0x20
+ field public static final int VERSION_3_0 = 48; // 0x30
+ field public static final int WRITE_ACCESS_GRANTED_RESTRICTED = 128; // 0x80
+ field public static final int WRITE_ACCESS_GRANTED_UNRESTRICTED = 0; // 0x0
+ field public static final int WRITE_ACCESS_NOT_GRANTED = 255; // 0xff
+ }
+
}
package android.nfc.cardemulation {
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index a08b55f..13e6734 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -31,6 +31,7 @@
import android.nfc.INfcFCardEmulation;
import android.nfc.INfcOemExtensionCallback;
import android.nfc.INfcUnlockHandler;
+import android.nfc.IT4tNdefNfcee;
import android.nfc.ITagRemovedCallback;
import android.nfc.INfcDta;
import android.nfc.INfcWlcStateListener;
@@ -52,8 +53,8 @@
int getState();
boolean disable(boolean saveState, in String pkg);
boolean enable(in String pkg);
- void pausePolling(int timeoutInMs);
- void resumePolling();
+ int pausePolling(int timeoutInMs);
+ int resumePolling();
void setForegroundDispatch(in PendingIntent intent,
in IntentFilter[] filters, in TechListParcel techLists);
@@ -122,4 +123,5 @@
void indicateDataMigration(boolean inProgress, String pkg);
int commitRouting();
boolean isTagIntentAllowed(in String pkg, in int Userid);
+ IT4tNdefNfcee getT4tNdefNfceeInterface();
}
diff --git a/nfc/java/android/nfc/IT4tNdefNfcee.aidl b/nfc/java/android/nfc/IT4tNdefNfcee.aidl
new file mode 100644
index 0000000..b4cda5b
--- /dev/null
+++ b/nfc/java/android/nfc/IT4tNdefNfcee.aidl
@@ -0,0 +1,33 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2024 The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+package android.nfc;
+
+import android.nfc.T4tNdefNfceeCcFileInfo;
+
+/**
+ * @hide
+ */
+interface IT4tNdefNfcee {
+ int writeData(in int fileId, in byte[] data);
+ byte[] readData(in int fileId);
+ int clearNdefData();
+ boolean isNdefOperationOngoing();
+ boolean isNdefNfceeEmulationSupported();
+ T4tNdefNfceeCcFileInfo readCcfile();
+}
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 056844f..89ce423 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -589,6 +589,7 @@
static INfcTag sTagService;
static INfcCardEmulation sCardEmulationService;
static INfcFCardEmulation sNfcFCardEmulationService;
+ static IT4tNdefNfcee sNdefNfceeService;
/**
* The NfcAdapter object for each application context.
@@ -827,7 +828,13 @@
throw new UnsupportedOperationException();
}
}
-
+ try {
+ sNdefNfceeService = sService.getT4tNdefNfceeInterface();
+ } catch (RemoteException e) {
+ sNdefNfceeService = null;
+ Log.e(TAG, "could not retrieve NDEF NFCEE service");
+ throw new UnsupportedOperationException();
+ }
sIsInitialized = true;
}
NfcAdapter adapter = sNfcAdapters.get(context);
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 9ed678f..ac7b8bb 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -173,6 +173,31 @@
public @interface HostCardEmulationAction {}
/**
+ * Status code returned when the polling state change request succeeded.
+ * @see #pausePolling()
+ * @see #resumePolling()
+ */
+ public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1;
+ /**
+ * Status code returned when the polling state change request is already in
+ * required state.
+ * @see #pausePolling()
+ * @see #resumePolling()
+ */
+ public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2;
+ /**
+ * Possible status codes for {@link #pausePolling()} and
+ * {@link #resumePolling()}.
+ * @hide
+ */
+ @IntDef(value = {
+ POLLING_STATE_CHANGE_SUCCEEDED,
+ POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PollingStateChangeStatusCode {}
+
+ /**
* Status OK
*/
public static final int STATUS_OK = 0;
@@ -467,6 +492,28 @@
}
/**
+ * Get an instance of {@link T4tNdefNfcee} object for performing T4T (Type-4 Tag)
+ * NDEF (NFC Data Exchange Format) NFCEE (NFC Execution Environment) operations.
+ * This can be used to write NDEF data to emulate a T4T tag in an NFCEE
+ * (NFC Execution Environment - eSE, SIM, etc). Refer to the NFC forum specification
+ * "NFCForum-TS-NCI-2.3 section 10.4" and "NFCForum-TS-T4T-1.1 section 4.2" for more details.
+ *
+ * This is a singleton object which shall be used by OEM extension module to do NDEF-NFCEE
+ * read/write operations.
+ *
+ * <p>Returns {@link T4tNdefNfcee}
+ * <p>Does not cause any RF activity and does not block.
+ * @return NFC Data Exchange Format (NDEF) NFC Execution Environment (NFCEE) object
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public T4tNdefNfcee getT4tNdefNfcee() {
+ return T4tNdefNfcee.getInstance();
+ }
+
+ /**
* Register an {@link Callback} to listen for NFC oem extension callbacks
* Multiple clients can register and callbacks will be invoked asynchronously.
*
@@ -653,24 +700,32 @@
/**
* Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond.
- * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely
- * use {@link #resumePolling()} to resume the polling.
+ * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely.
+ * Use {@link #resumePolling() to resume the polling.
* @param timeoutInMs the pause polling duration in millisecond, ranging from 0 to 40000.
+ * @return status of the operation
+ * @throws IllegalArgumentException if timeoutInMs value is invalid
+ * (0 < timeoutInMs < max).
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public void pausePolling(@DurationMillisLong int timeoutInMs) {
- NfcAdapter.callService(() -> NfcAdapter.sService.pausePolling(timeoutInMs));
+ public @PollingStateChangeStatusCode int pausePolling(@DurationMillisLong int timeoutInMs) {
+ return NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sService.pausePolling(timeoutInMs),
+ POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE);
}
/**
* Resumes default NFC tag reader mode polling for the current device state if polling is
* paused. Calling this while already in polling is a no-op.
+ * @return status of the operation
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public void resumePolling() {
- NfcAdapter.callService(() -> NfcAdapter.sService.resumePolling());
+ public @PollingStateChangeStatusCode int resumePolling() {
+ return NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sService.resumePolling(),
+ POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE);
}
/**
diff --git a/nfc/java/android/nfc/NfcRoutingTableEntry.java b/nfc/java/android/nfc/NfcRoutingTableEntry.java
index 4e91377..c2cbbed 100644
--- a/nfc/java/android/nfc/NfcRoutingTableEntry.java
+++ b/nfc/java/android/nfc/NfcRoutingTableEntry.java
@@ -17,8 +17,12 @@
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Class to represent an entry of routing table. This class is abstract and extended by
* {@link RoutingTableTechnologyEntry}, {@link RoutingTableProtocolEntry},
@@ -30,10 +34,42 @@
@SystemApi
public abstract class NfcRoutingTableEntry {
private final int mNfceeId;
+ private final int mType;
+
+ /**
+ * AID routing table type.
+ */
+ public static final int TYPE_AID = 0;
+ /**
+ * Protocol routing table type.
+ */
+ public static final int TYPE_PROTOCOL = 1;
+ /**
+ * Technology routing table type.
+ */
+ public static final int TYPE_TECHNOLOGY = 2;
+ /**
+ * System Code routing table type.
+ */
+ public static final int TYPE_SYSTEM_CODE = 3;
+
+ /**
+ * Possible type of this routing table entry.
+ * @hide
+ */
+ @IntDef(prefix = "TYPE_", value = {
+ TYPE_AID,
+ TYPE_PROTOCOL,
+ TYPE_TECHNOLOGY,
+ TYPE_SYSTEM_CODE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RoutingTableType {}
/** @hide */
- protected NfcRoutingTableEntry(int nfceeId) {
+ protected NfcRoutingTableEntry(int nfceeId, @RoutingTableType int type) {
mNfceeId = nfceeId;
+ mType = type;
}
/**
@@ -43,4 +79,13 @@
public int getNfceeId() {
return mNfceeId;
}
+
+ /**
+ * Get the type of this entry.
+ * @return an integer defined in {@link RoutingTableType}
+ */
+ @RoutingTableType
+ public int getType() {
+ return mType;
+ }
}
diff --git a/nfc/java/android/nfc/RoutingTableAidEntry.java b/nfc/java/android/nfc/RoutingTableAidEntry.java
index 7634fe3..bf697d6 100644
--- a/nfc/java/android/nfc/RoutingTableAidEntry.java
+++ b/nfc/java/android/nfc/RoutingTableAidEntry.java
@@ -20,7 +20,7 @@
import android.annotation.SystemApi;
/**
- * Represents an AID entry in current routing table.
+ * Represents an Application ID (AID) entry in current routing table.
* @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
@@ -30,7 +30,7 @@
/** @hide */
public RoutingTableAidEntry(int nfceeId, String value) {
- super(nfceeId);
+ super(nfceeId, TYPE_AID);
this.mValue = value;
}
diff --git a/nfc/java/android/nfc/RoutingTableProtocolEntry.java b/nfc/java/android/nfc/RoutingTableProtocolEntry.java
index 0c5be7d..536de4d 100644
--- a/nfc/java/android/nfc/RoutingTableProtocolEntry.java
+++ b/nfc/java/android/nfc/RoutingTableProtocolEntry.java
@@ -97,7 +97,7 @@
/** @hide */
public RoutingTableProtocolEntry(int nfceeId, @ProtocolValue int value) {
- super(nfceeId);
+ super(nfceeId, TYPE_PROTOCOL);
this.mValue = value;
}
diff --git a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java
index f87ad5f..f61892d 100644
--- a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java
+++ b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java
@@ -20,7 +20,9 @@
import android.annotation.SystemApi;
/**
- * Represents a system code entry in current routing table.
+ * Represents a system code entry in current routing table, where system codes are two-byte values
+ * used in NFC-F technology (a type of NFC communication) to identify specific
+ * device configurations.
* @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
@@ -30,7 +32,7 @@
/** @hide */
public RoutingTableSystemCodeEntry(int nfceeId, byte[] value) {
- super(nfceeId);
+ super(nfceeId, TYPE_SYSTEM_CODE);
this.mValue = value;
}
diff --git a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java
index f51a529..2dbc942 100644
--- a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java
+++ b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java
@@ -30,22 +30,27 @@
@SystemApi
public class RoutingTableTechnologyEntry extends NfcRoutingTableEntry {
/**
- * Technology-A
+ * Technology-A.
+ * <p>Tech-A is mostly used for payment and ticketing applications. It supports various
+ * Tag platforms including Type 1, Type 2 and Type 4A tags. </p>
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int TECHNOLOGY_A = 0;
/**
- * Technology-B
+ * Technology-B which is based on ISO/IEC 14443-3 standard.
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int TECHNOLOGY_B = 1;
/**
- * Technology-F
+ * Technology-F.
+ * <p>Tech-F is a standard which supports Type 3 Tags and NFC-DEP protocol etc.</p>
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int TECHNOLOGY_F = 2;
/**
- * Technology-V
+ * Technology-V.
+ * <p>Tech-V is an NFC technology used for communication with passive tags that operate
+ * at a longer range than other NFC technologies. </p>
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
public static final int TECHNOLOGY_V = 3;
@@ -73,7 +78,7 @@
/** @hide */
public RoutingTableTechnologyEntry(int nfceeId, @TechnologyValue int value) {
- super(nfceeId);
+ super(nfceeId, TYPE_TECHNOLOGY);
this.mValue = value;
}
diff --git a/nfc/java/android/nfc/T4tNdefNfcee.java b/nfc/java/android/nfc/T4tNdefNfcee.java
new file mode 100644
index 0000000..06d02c5
--- /dev/null
+++ b/nfc/java/android/nfc/T4tNdefNfcee.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class is used for performing T4T (Type-4 Tag) NDEF (NFC Data Exchange Format)
+ * NFCEE (NFC Execution Environment) operations.
+ * This can be used to write NDEF data to emulate a T4T tag in an NFCEE
+ * (NFC Execution Environment - eSE, SIM, etc). Refer to the NFC forum specification
+ * "NFCForum-TS-NCI-2.3 section 10.4" and "NFCForum-TS-T4T-1.1 section 4.2" for more details.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public final class T4tNdefNfcee {
+ private static final String TAG = "NdefNfcee";
+ static T4tNdefNfcee sNdefNfcee;
+
+ private T4tNdefNfcee() {
+ }
+
+ /**
+ * Helper to get an instance of this class.
+ *
+ * @return
+ * @hide
+ */
+ @NonNull
+ public static T4tNdefNfcee getInstance() {
+ if (sNdefNfcee == null) {
+ sNdefNfcee = new T4tNdefNfcee();
+ }
+ return sNdefNfcee;
+ }
+
+ /**
+ * Return flag for {@link #writeData(int, byte[])}.
+ * It indicates write data is successful.
+ */
+ public static final int WRITE_DATA_SUCCESS = 0;
+ /**
+ * Return flag for {@link #writeData(int, byte[])}.
+ * It indicates write data fail due to unknown reasons.
+ */
+ public static final int WRITE_DATA_ERROR_INTERNAL = -1;
+ /**
+ * Return flag for {@link #writeData(int, byte[])}.
+ * It indicates write data fail due to ongoing rf activity.
+ */
+ public static final int WRITE_DATA_ERROR_RF_ACTIVATED = -2;
+ /**
+ * Return flag for {@link #writeData(int, byte[])}.
+ * It indicates write data fail due to Nfc off.
+ */
+ public static final int WRITE_DATA_ERROR_NFC_NOT_ON = -3;
+ /**
+ * Return flag for {@link #writeData(int, byte[])}.
+ * It indicates write data fail due to invalid file id.
+ */
+ public static final int WRITE_DATA_ERROR_INVALID_FILE_ID = -4;
+ /**
+ * Return flag for {@link #writeData(int, byte[])}.
+ * It indicates write data fail due to invalid length.
+ */
+ public static final int WRITE_DATA_ERROR_INVALID_LENGTH = -5;
+ /**
+ * Return flag for {@link #writeData(int, byte[])}.
+ * It indicates write data fail due to core connection create failure.
+ */
+ public static final int WRITE_DATA_ERROR_CONNECTION_FAILED = -6;
+ /**
+ * Return flag for {@link #writeData(int, byte[])}.
+ * It indicates write data fail due to empty payload.
+ */
+ public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7;
+ /**
+ * Returns flag for {@link #writeData(int, byte[])}.
+ * It idicates write data fail due to invalid ndef format.
+ */
+ public static final int WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED = -8;
+
+ /**
+ * Possible return values for {@link #writeData(int, byte[])}.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "WRITE_DATA_" }, value = {
+ WRITE_DATA_SUCCESS,
+ WRITE_DATA_ERROR_INTERNAL,
+ WRITE_DATA_ERROR_RF_ACTIVATED,
+ WRITE_DATA_ERROR_NFC_NOT_ON,
+ WRITE_DATA_ERROR_INVALID_FILE_ID,
+ WRITE_DATA_ERROR_INVALID_LENGTH,
+ WRITE_DATA_ERROR_CONNECTION_FAILED,
+ WRITE_DATA_ERROR_EMPTY_PAYLOAD,
+ WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WriteDataStatus{}
+
+ /**
+ * This API performs writes of T4T data to NFCEE.
+ *
+ * <p>This is an I/O operation and will block until complete. It must
+ * not be called from the main application thread.</p>
+ *
+ * @param fileId File id (Refer NFC Forum Type 4 Tag Specification
+ * Section 4.2 File Identifiers and Access Conditions
+ * for more information) to which to write.
+ * @param data This should be valid Ndef Message format.
+ * Refer to Nfc forum NDEF specification NDEF Message section
+ * @return status of the operation.
+ * @hide
+ */
+ @SystemApi
+ @WorkerThread
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @WriteDataStatus int writeData(@IntRange(from = 0, to = 65535) int fileId,
+ @NonNull byte[] data) {
+ return NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sNdefNfceeService.writeData(fileId, data), WRITE_DATA_ERROR_INTERNAL);
+ }
+
+ /**
+ * This API performs reading of T4T content of Nfcee.
+ *
+ * <p>This is an I/O operation and will block until complete. It must
+ * not be called from the main application thread.</p>
+ *
+ * @param fileId File Id (Refer
+ * Section 4.2 File Identifiers and Access Conditions
+ * for more information) from which to read.
+ * @return - Returns Ndef message if success
+ * Refer to Nfc forum NDEF specification NDEF Message section
+ * @throws IllegalStateException if read fails because the fileId is invalid.
+ * @hide
+ */
+ @SystemApi
+ @WorkerThread
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public byte[] readData(@IntRange(from = 0, to = 65535) int fileId) {
+ return NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sNdefNfceeService.readData(fileId), null);
+ }
+
+ /**
+ * Return flag for {@link #clearNdefData()}.
+ * It indicates clear data is successful.
+ */
+ public static final int CLEAR_DATA_SUCCESS = 1;
+ /**
+ * Return flag for {@link #clearNdefData()}.
+ * It indicates clear data failed due to internal error while processing the clear.
+ */
+ public static final int CLEAR_DATA_FAILED_INTERNAL = 0;
+
+ /**
+ * Possible return values for {@link #clearNdefData()}.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "CLEAR_DATA_" }, value = {
+ CLEAR_DATA_SUCCESS,
+ CLEAR_DATA_FAILED_INTERNAL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ClearDataStatus{}
+
+ /**
+ * This API will set all the T4T NDEF NFCEE data to zero.
+ *
+ * <p>This is an I/O operation and will block until complete. It must
+ * not be called from the main application thread.
+ *
+ * <p>This API can be called regardless of NDEF file lock state.
+ * </p>
+ * @return status of the operation
+ *
+ * @hide
+ */
+ @SystemApi
+ @WorkerThread
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @ClearDataStatus int clearData() {
+ return NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sNdefNfceeService.clearNdefData(), CLEAR_DATA_FAILED_INTERNAL);
+ }
+
+ /**
+ * Returns whether NDEF NFCEE operation is ongoing or not.
+ *
+ * @return true if NDEF NFCEE operation is ongoing, else false.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public boolean isOperationOngoing() {
+ return NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sNdefNfceeService.isNdefOperationOngoing(), false);
+ }
+
+ /**
+ * This Api is to check the status of NDEF NFCEE emulation feature is
+ * supported or not.
+ *
+ * @return true if NDEF NFCEE emulation feature is supported, else false.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public boolean isSupported() {
+ return NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sNdefNfceeService.isNdefNfceeEmulationSupported(), false);
+ }
+
+ /**
+ * This API performs reading of T4T NDEF NFCEE CC file content.
+ *
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details.
+ *
+ * @return Returns CC file content if success or null if failed to read.
+ * @hide
+ */
+ @SystemApi
+ @WorkerThread
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ @Nullable
+ public T4tNdefNfceeCcFileInfo readCcfile() {
+ return NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sNdefNfceeService.readCcfile(), null);
+ }
+}
diff --git a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl
new file mode 100644
index 0000000..f72f74e
--- /dev/null
+++ b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+parcelable T4tNdefNfceeCcFileInfo;
+
diff --git a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java
new file mode 100644
index 0000000..5fca052
--- /dev/null
+++ b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class is used to represence T4T (Type-4 Tag) NDEF (NFC Data Exchange Format)
+ * NFCEE (NFC Execution Environment) CC (Capability Container) File data.
+ * The CC file stores metadata about the T4T tag being emulated.
+ *
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public final class T4tNdefNfceeCcFileInfo implements Parcelable {
+ /**
+ * Indicates the size of this capability container (called “CC File”)<p>
+ */
+ private int mCcLength;
+ /**
+ * Indicates the mapping specification version<p>
+ */
+ private int mVersion;
+ /**
+ * Indicates the max data size by a single ReadBinary<p>
+ */
+ private int mMaxReadLength;
+ /**
+ * Indicates the max data size by a single UpdateBinary<p>
+ */
+ private int mMaxWriteLength;
+ /**
+ * Indicates the NDEF File Identifier<p>
+ */
+ private int mFileId;
+ /**
+ * Indicates the maximum Max NDEF file size<p>
+ */
+ private int mMaxSize;
+ /**
+ * Indicates the read access condition<p>
+ */
+ private int mReadAccess;
+ /**
+ * Indicates the write access condition<p>
+ */
+ private int mWriteAccess;
+
+ /**
+ * Constructor to be used by NFC service and internal classes.
+ * @hide
+ */
+ public T4tNdefNfceeCcFileInfo(int cclen, int version, int maxLe, int maxLc,
+ int ndefFileId, int ndefMaxSize,
+ int ndefReadAccess, int ndefWriteAccess) {
+ mCcLength = cclen;
+ mVersion = version;
+ mMaxWriteLength = maxLc;
+ mMaxReadLength = maxLe;
+ mFileId = ndefFileId;
+ mMaxSize = ndefMaxSize;
+ mReadAccess = ndefReadAccess;
+ mWriteAccess = ndefWriteAccess;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+
+ dest.writeInt(mCcLength);
+ dest.writeInt(mVersion);
+ dest.writeInt(mMaxWriteLength);
+ dest.writeInt(mMaxReadLength);
+ dest.writeInt(mFileId);
+ dest.writeInt(mMaxSize);
+ dest.writeInt(mReadAccess);
+ dest.writeInt(mWriteAccess);
+ }
+
+ /**
+ * Indicates the size of this capability container (called “CC File”).
+ *
+ * @return length of the CC file.
+ */
+ @IntRange(from = 0xf, to = 0x7fff)
+ public int getCcFileLength() {
+ return mCcLength;
+ }
+
+ /**
+ * T4T tag mapping version 2.0.
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details.
+ */
+ public static final int VERSION_2_0 = 0x20;
+ /**
+ * T4T tag mapping version 2.0.
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details.
+ */
+ public static final int VERSION_3_0 = 0x30;
+
+ /**
+ * Possible return values for {@link #getVersion()}.
+ * @hide
+ */
+ @IntDef(prefix = { "VERSION_" }, value = {
+ VERSION_2_0,
+ VERSION_3_0,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Version{}
+
+ /**
+ * Indicates the mapping version of the T4T tag supported.
+ *
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.5" for more details.
+ *
+ * @return version of the specification
+ */
+ @Version
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Indicates the max data size that can be read by a single invocation of
+ * {@link T4tNdefNfcee#readData(int)}.
+ *
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" MLe.
+ * @return max size of read (in bytes).
+ */
+ @IntRange(from = 0xf, to = 0xffff)
+ public int getMaxReadLength() {
+ return mMaxReadLength;
+ }
+
+ /**
+ * Indicates the max data size that can be written by a single invocation of
+ * {@link T4tNdefNfcee#writeData(int, byte[])}
+ *
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" MLc.
+ * @return max size of write (in bytes).
+ */
+ @IntRange(from = 0xd, to = 0xffff)
+ public int getMaxWriteLength() {
+ return mMaxWriteLength;
+ }
+
+ /**
+ * Indicates the NDEF File Identifier. This is the identifier used in the last invocation of
+ * {@link T4tNdefNfcee#writeData(int, byte[])}
+ *
+ * @return FileId of the data stored or -1 if no data is present.
+ */
+ @IntRange(from = -1, to = 65535)
+ public int getFileId() {
+ return mFileId;
+ }
+
+ /**
+ * Indicates the maximum size of T4T NDEF data that can be written to the NFCEE.
+ *
+ * @return max size of the contents.
+ */
+ @IntRange(from = 0x5, to = 0x7fff)
+ public int getMaxSize() {
+ return mMaxSize;
+ }
+
+ /**
+ * T4T tag read access granted without any security.
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
+ */
+ public static final int READ_ACCESS_GRANTED_UNRESTRICTED = 0x0;
+ /**
+ * T4T tag read access granted with limited proprietary access only.
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
+ */
+ public static final int READ_ACCESS_GRANTED_RESTRICTED = 0x80;
+
+ /**
+ * Possible return values for {@link #getVersion()}.
+ * @hide
+ */
+ @IntDef(prefix = { "READ_ACCESS_GRANTED_" }, value = {
+ READ_ACCESS_GRANTED_RESTRICTED,
+ READ_ACCESS_GRANTED_UNRESTRICTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReadAccess {}
+
+ /**
+ * Indicates the read access condition.
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
+ * @return read access restriction
+ */
+ @ReadAccess
+ public int getReadAccess() {
+ return mReadAccess;
+ }
+
+ /**
+ * T4T tag write access granted without any security.
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
+ */
+ public static final int WRITE_ACCESS_GRANTED_UNRESTRICTED = 0x0;
+ /**
+ * T4T tag write access granted with limited proprietary access only.
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
+ */
+ public static final int WRITE_ACCESS_GRANTED_RESTRICTED = 0x80;
+ /**
+ * T4T tag write access not granted.
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
+ */
+ public static final int WRITE_ACCESS_NOT_GRANTED = 0xFF;
+
+ /**
+ * Possible return values for {@link #getVersion()}.
+ * @hide
+ */
+ @IntDef(prefix = { "READ_ACCESS_GRANTED_" }, value = {
+ WRITE_ACCESS_GRANTED_RESTRICTED,
+ WRITE_ACCESS_GRANTED_UNRESTRICTED,
+ WRITE_ACCESS_NOT_GRANTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WriteAccess {}
+
+ /**
+ * Indicates the write access condition.
+ * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details.
+ * @return write access restriction
+ */
+ @WriteAccess
+ public int getWriteAccess() {
+ return mWriteAccess;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Parcelable.Creator<T4tNdefNfceeCcFileInfo> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public T4tNdefNfceeCcFileInfo createFromParcel(Parcel in) {
+
+ // NdefNfceeCcFileInfo fields
+ int cclen = in.readInt();
+ int version = in.readInt();
+ int maxLe = in.readInt();
+ int maxLc = in.readInt();
+ int ndefFileId = in.readInt();
+ int ndefMaxSize = in.readInt();
+ int ndefReadAccess = in.readInt();
+ int ndefWriteAccess = in.readInt();
+
+ return new T4tNdefNfceeCcFileInfo(cclen, version, maxLe, maxLc,
+ ndefFileId, ndefMaxSize,
+ ndefReadAccess, ndefWriteAccess);
+ }
+
+ @Override
+ public T4tNdefNfceeCcFileInfo[] newArray(int size) {
+ return new T4tNdefNfceeCcFileInfo[size];
+ }
+ };
+}
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index fc48217..2ba93f1 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -456,7 +456,7 @@
*
* <p>This method could be called frequently if there is a severe problem on the device.
*/
- public void onPackageFailure(@NonNull List<VersionedPackage> packages,
+ public void notifyPackageFailure(@NonNull List<VersionedPackage> packages,
@FailureReasons int failureReason) {
if (packages == null) {
Slog.w(TAG, "Could not resolve a list of failing packages");
@@ -467,7 +467,7 @@
if (Flags.recoverabilityDetection()) {
if (now >= mLastMitigation
&& (now - mLastMitigation) < getMitigationWindowMs()) {
- Slog.i(TAG, "Skipping onPackageFailure mitigation");
+ Slog.i(TAG, "Skipping notifyPackageFailure mitigation");
return;
}
}
@@ -494,7 +494,7 @@
ObserverInternal observer = mAllObservers.valueAt(oIndex);
PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null
- && observer.onPackageFailureLocked(
+ && observer.notifyPackageFailureLocked(
versionedPackage.getPackageName())) {
MonitoredPackage p = observer.getMonitoredPackage(
versionedPackage.getPackageName());
@@ -693,7 +693,7 @@
// Check if native watchdog reported a crash
if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
// We rollback all available low impact rollbacks when crash is unattributable
- onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH);
+ notifyPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH);
// we stop polling after an attempt to execute rollback, regardless of whether the
// attempt succeeds or not
} else {
@@ -731,6 +731,25 @@
}
/**
+ * The minimum value that can be returned by any observer.
+ * It represents that no mitigations were available.
+ */
+ public static final int LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT =
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+
+ /**
+ * The mitigation impact beyond which the user will start noticing the mitigations.
+ */
+ public static final int MEDIUM_USER_IMPACT_THRESHOLD =
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_20;
+
+ /**
+ * The mitigation impact beyond which the user impact is severely high.
+ */
+ public static final int HIGH_USER_IMPACT_THRESHOLD =
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_71;
+
+ /**
* Possible severity values of the user impact of a
* {@link PackageHealthObserver#onExecuteHealthCheckMitigation}.
* @hide
@@ -773,6 +792,11 @@
/**
* Called when health check fails for the {@code versionedPackage}.
*
+ * Note: if the returned user impact is higher than
+ * {@link #DEFAULT_HIGH_USER_IMPACT_THRESHOLD}, then
+ * {@link #onExecuteHealthCheckMitigation} would be called only in severe device conditions
+ * like boot-loop or network failure.
+ *
* @param versionedPackage the package that is failing. This may be null if a native
* service is crashing.
* @param failureReason the type of failure that is occurring.
@@ -780,8 +804,8 @@
* (including this time).
*
*
- * @return any one of {@link PackageHealthObserverImpact} to express the impact
- * to the user on {@link #onExecuteHealthCheckMitigation}
+ * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express
+ * the impact of mitigation on the user in {@link #onExecuteHealthCheckMitigation}
*/
@PackageHealthObserverImpact int onHealthCheckFailed(
@Nullable VersionedPackage versionedPackage,
@@ -790,9 +814,8 @@
/**
* This would be called after {@link #onHealthCheckFailed}.
- * This is called only if current observer returned least
- * {@link PackageHealthObserverImpact} mitigation for failed health
- * check.
+ * This is called only if current observer returned least impact mitigation for failed
+ * health check.
*
* @param versionedPackage the package that is failing. This may be null if a native
* service is crashing.
@@ -811,6 +834,9 @@
*
* @param mitigationCount the number of times mitigation has been attempted for this
* boot loop (including this time).
+ *
+ * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express
+ * the impact of mitigation on the user in {@link #onExecuteBootLoopMitigation}
*/
default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) {
return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
@@ -818,11 +844,13 @@
/**
* This would be called after {@link #onBootLoop}.
- * This is called only if current observer returned least
- * {@link PackageHealthObserverImpact} mitigation for fixing boot loop
+ * This is called only if current observer returned least impact mitigation for fixing
+ * boot loop.
*
* @param mitigationCount the number of times mitigation has been attempted for this
* boot loop (including this time).
+ *
+ * @return {@code true} if action was executed successfully, {@code false} otherwise
*/
default boolean onExecuteBootLoopMitigation(int mitigationCount) {
return false;
@@ -916,7 +944,7 @@
* effectively behave as if the explicit health check hasn't passed for {@code packageName}.
*
* <p> {@code packageName} can still be considered failed if reported by
- * {@link #onPackageFailureLocked} before the package expires.
+ * {@link #notifyPackageFailureLocked} before the package expires.
*
* <p> Triggered by components outside the system server when they are fully functional after an
* update.
@@ -1460,7 +1488,7 @@
* @hide
*/
@GuardedBy("sLock")
- public boolean onPackageFailureLocked(String packageName) {
+ public boolean notifyPackageFailureLocked(String packageName) {
if (getMonitoredPackage(packageName) == null && registeredObserver.isPersistent()
&& registeredObserver.mayObservePackage(packageName)) {
putMonitoredPackage(sPackageWatchdog.newMonitoredPackage(
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index 129e47f..88fe36c 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -477,7 +477,7 @@
*
* <p>This method could be called frequently if there is a severe problem on the device.
*/
- public void onPackageFailure(@NonNull List<VersionedPackage> packages,
+ public void notifyPackageFailure(@NonNull List<VersionedPackage> packages,
@FailureReasons int failureReason) {
if (packages == null) {
Slog.w(TAG, "Could not resolve a list of failing packages");
@@ -488,7 +488,7 @@
if (Flags.recoverabilityDetection()) {
if (now >= mLastMitigation
&& (now - mLastMitigation) < getMitigationWindowMs()) {
- Slog.i(TAG, "Skipping onPackageFailure mitigation");
+ Slog.i(TAG, "Skipping notifyPackageFailure mitigation");
return;
}
}
@@ -515,7 +515,7 @@
ObserverInternal observer = mAllObservers.valueAt(oIndex);
PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null
- && observer.onPackageFailureLocked(
+ && observer.notifyPackageFailureLocked(
versionedPackage.getPackageName())) {
MonitoredPackage p = observer.getMonitoredPackage(
versionedPackage.getPackageName());
@@ -714,7 +714,7 @@
// Check if native watchdog reported a crash
if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
// We rollback all available low impact rollbacks when crash is unattributable
- onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH);
+ notifyPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH);
// we stop polling after an attempt to execute rollback, regardless of whether the
// attempt succeeds or not
} else {
@@ -926,7 +926,7 @@
* effectively behave as if the explicit health check hasn't passed for {@code packageName}.
*
* <p> {@code packageName} can still be considered failed if reported by
- * {@link #onPackageFailureLocked} before the package expires.
+ * {@link #notifyPackageFailureLocked} before the package expires.
*
* <p> Triggered by components outside the system server when they are fully functional after an
* update.
@@ -1253,7 +1253,7 @@
return;
}
final List<VersionedPackage> pkgList = Collections.singletonList(pkg);
- onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
+ notifyPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
});
}
@@ -1467,7 +1467,7 @@
* @hide
*/
@GuardedBy("mLock")
- public boolean onPackageFailureLocked(String packageName) {
+ public boolean notifyPackageFailureLocked(String packageName) {
if (getMonitoredPackage(packageName) == null && registeredObserver.isPersistent()
&& registeredObserver.mayObservePackage(packageName)) {
putMonitoredPackage(sPackageWatchdog.newMonitoredPackage(
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 2321097..b01b7c9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -390,6 +390,16 @@
}
/**
+ * Get the {@link MediaRoute2Info.Type} of the device.
+ */
+ public int getRouteType() {
+ if (mRouteInfo == null) {
+ return TYPE_UNKNOWN;
+ }
+ return mRouteInfo.getType();
+ }
+
+ /**
* Checks if route's volume is fixed, if true, we should disable volume control for the device.
*
* @return route for this device is fixed.
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 927a1c59..1f291cd 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -107,6 +107,8 @@
Settings.Secure.DISPLAY_WHITE_BALANCE_ENABLED,
Settings.Secure.SYNC_PARENT_SOUNDS,
Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
// ACCESSIBILITY_QS_TARGETS needs to be restored after ENABLED_ACCESSIBILITY_SERVICES
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 6d73ee2..abd5b9a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -160,6 +160,9 @@
VALIDATORS.put(Secure.DISPLAY_WHITE_BALANCE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SYNC_PARENT_SOUNDS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
+ new InclusiveIntegerRangeValidator(0, 1));
VALIDATORS.put(Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.QS_TILES, TILE_LIST_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 600c36e..5ae11ba 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2124,6 +2124,15 @@
SecureSettingsProto.Display.SCREEN_RESOLUTION_MODE);
p.end(displayToken);
+ final long doubleTapPowerButtonToken = p.start(SecureSettingsProto.DOUBLE_TAP_POWER_BUTTON);
+ dumpSetting(s, p,
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
+ SecureSettingsProto.DoubleTapPowerButton.GESTURE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE,
+ SecureSettingsProto.DoubleTapPowerButton.GESTURE);
+ p.end(doubleTapPowerButtonToken);
+
final long dozeToken = p.start(SecureSettingsProto.DOZE);
dumpSetting(s, p,
Settings.Secure.DOZE_ENABLED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
index d835c5f..b0409c0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
@@ -33,6 +33,15 @@
final class WritableNamespaces {
public static final Set<String> ALLOWLIST =
new ArraySet<String>(Arrays.asList(
- "exo"
+ "captive_portal_login",
+ "connectivity",
+ "exo",
+ "nearby",
+ "netd_native",
+ "network_security",
+ "on_device_personalization",
+ "tethering",
+ "tethering_u_or_later_native",
+ "thread_network"
));
}
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index 3350efc..5f81085 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -27,6 +27,7 @@
],
flags_packages: [
"android.security.flags-aconfig",
+ "android.permission.flags-aconfig",
],
platform_apis: true,
certificate: "platform",
@@ -51,5 +52,6 @@
manifest: "AndroidManifest.xml",
flags_packages: [
"android.security.flags-aconfig",
+ "android.permission.flags-aconfig",
],
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ac02af81..27e6bab 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -981,6 +981,14 @@
<uses-permission android:name="android.permission.READ_SYSTEM_PREFERENCES" />
<uses-permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES" />
+ <!-- Permissions required for CTS test - ActivityManagerForegroundServiceTypeTest -->
+ <uses-permission android:name="android.permission.health.READ_HEART_RATE"
+ android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/>
+ <uses-permission android:name="android.permission.health.READ_OXYGEN_SATURATION"
+ android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/>
+ <uses-permission android:name="android.permission.health.READ_SKIN_TEMPERATURE"
+ android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/>
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java
index 1f29255..544d201 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_PROMOTED_ONGOING;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_MIN;
@@ -24,12 +25,18 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Person;
import android.content.Intent;
import android.graphics.Color;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,11 +45,14 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -136,6 +146,60 @@
assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
}
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ public void testIncludePromotedOngoingInSection_flagEnabled() {
+ // GIVEN the notification has FLAG_PROMOTED_ONGOING
+ mEntryBuilder.setFlag(mContext, FLAG_PROMOTED_ONGOING, true);
+
+ // THEN the entry is in the fgs section
+ assertTrue(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
+
+ @Test
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME)
+ public void testDiscludePromotedOngoingInSection_flagDisabled() {
+ // GIVEN the notification has FLAG_PROMOTED_ONGOING
+ mEntryBuilder.setFlag(mContext, FLAG_PROMOTED_ONGOING, true);
+
+ // THEN the entry is NOT in the fgs section
+ assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ public void promoterSelectsPromotedOngoing_flagEnabled() {
+ ArgumentCaptor<NotifPromoter> captor = ArgumentCaptor.forClass(NotifPromoter.class);
+ verify(mNotifPipeline).addPromoter(captor.capture());
+ NotifPromoter promoter = captor.getValue();
+
+ // GIVEN the notification has FLAG_PROMOTED_ONGOING
+ mEntryBuilder.setFlag(mContext, FLAG_PROMOTED_ONGOING, true);
+
+ // THEN the entry is promoted to top level
+ assertTrue(promoter.shouldPromoteToTopLevel(mEntryBuilder.build()));
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ public void promoterIgnoresNonPromotedOngoing_flagEnabled() {
+ ArgumentCaptor<NotifPromoter> captor = ArgumentCaptor.forClass(NotifPromoter.class);
+ verify(mNotifPipeline).addPromoter(captor.capture());
+ NotifPromoter promoter = captor.getValue();
+
+ // GIVEN the notification does not have FLAG_PROMOTED_ONGOING
+ mEntryBuilder.setFlag(mContext, FLAG_PROMOTED_ONGOING, false);
+
+ // THEN the entry is NOT promoted to top level
+ assertFalse(promoter.shouldPromoteToTopLevel(mEntryBuilder.build()));
+ }
+
+ @Test
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME)
+ public void noPromoterAdded_flagDisabled() {
+ verify(mNotifPipeline, never()).addPromoter(any());
+ }
+
private Notification.CallStyle makeCallStyle() {
final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent("action"), PendingIntent.FLAG_IMMUTABLE);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 7b91eae..38cfb9b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -25,6 +25,8 @@
import com.android.systemui.biometrics.BiometricNotificationService
import com.android.systemui.bouncer.domain.startable.BouncerStartable
import com.android.systemui.clipboardoverlay.ClipboardListener
+import com.android.systemui.complication.ComplicationTypesUpdater
+import com.android.systemui.complication.DreamClockTimeComplication
import com.android.systemui.controls.dagger.StartControlsStartableModule
import com.android.systemui.dagger.qualifiers.PerUser
import com.android.systemui.dreams.AssistantAttentionMonitor
@@ -337,4 +339,18 @@
abstract fun bindDreamOverlayRegistrant(
dreamOverlayRegistrant: DreamOverlayRegistrant
): CoreStartable
+
+ /** Inject into DreamClockTimeComplication.Registrant */
+ @Binds
+ @IntoMap
+ @ClassKey(DreamClockTimeComplication.Registrant::class)
+ abstract fun bindDreamClockTimeComplicationRegistrant(
+ registrant: DreamClockTimeComplication.Registrant
+ ): CoreStartable
+
+ /** Inject into ComplicationTypesUpdater. */
+ @Binds
+ @IntoMap
+ @ClassKey(ComplicationTypesUpdater::class)
+ abstract fun bindComplicationTypesUpdater(updater: ComplicationTypesUpdater): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
index 47a0429..733b986 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
@@ -20,13 +20,21 @@
import android.app.Notification;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
+import com.google.common.primitives.Booleans;
+
import javax.inject.Inject;
/**
@@ -44,12 +52,21 @@
@Override
public void attach(NotifPipeline pipeline) {
+ if (PromotedNotificationUi.isEnabled()) {
+ pipeline.addPromoter(mPromotedOngoingPromoter);
+ }
}
public NotifSectioner getSectioner() {
return mNotifSectioner;
}
+ private final NotifPromoter mPromotedOngoingPromoter = new NotifPromoter("PromotedOngoing") {
+ @Override
+ public boolean shouldPromoteToTopLevel(NotificationEntry child) {
+ return isPromotedOngoing(child);
+ }
+ };
/**
* Puts colorized foreground service and call notifications into its own section.
@@ -64,11 +81,30 @@
}
return false;
}
+
+ private NotifComparator mPreferPromoted = new NotifComparator("PreferPromoted") {
+ @Override
+ public int compare(@NonNull ListEntry o1, @NonNull ListEntry o2) {
+ return -1 * Booleans.compare(
+ isPromotedOngoing(o1.getRepresentativeEntry()),
+ isPromotedOngoing(o2.getRepresentativeEntry()));
+ }
+ };
+
+ @Nullable
+ @Override
+ public NotifComparator getComparator() {
+ if (PromotedNotificationUi.isEnabled()) {
+ return mPreferPromoted;
+ } else {
+ return null;
+ }
+ }
};
/** Determines if the given notification is a colorized or call notification */
public static boolean isRichOngoing(NotificationEntry entry) {
- return isColorizedForegroundService(entry) || isCall(entry);
+ return isPromotedOngoing(entry) || isColorizedForegroundService(entry) || isCall(entry);
}
private static boolean isColorizedForegroundService(NotificationEntry entry) {
@@ -78,6 +114,11 @@
&& entry.getImportance() > IMPORTANCE_MIN;
}
+ private static boolean isPromotedOngoing(NotificationEntry entry) {
+ // NOTE: isPromotedOngoing already checks the android.app.ui_rich_ongoing flag.
+ return entry != null && entry.getSbn().getNotification().isPromotedOngoing();
+ }
+
private static boolean isCall(NotificationEntry entry) {
Notification notification = entry.getSbn().getNotification();
return entry.getImportance() > IMPORTANCE_MIN
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 2808056..a0b989b 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -55,16 +55,6 @@
}
flag {
- name: "compute_window_changes_on_a11y_v2"
- namespace: "accessibility"
- description: "Computes accessibility window changes in accessibility instead of wm package."
- bug: "322444245"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "deprecate_package_list_observer"
namespace: "accessibility"
description: "Stops using the deprecated PackageListObserver."
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 9a81aa6..5cbe0c4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -433,22 +433,10 @@
return Collections.emptyList();
}
- /**
- * Callbacks from window manager when there's an accessibility change in windows.
- *
- * @param forceSend Send the windows for accessibility even if they haven't changed.
- * @param topFocusedDisplayId The display Id which has the top focused window.
- * @param topFocusedWindowToken The window token of top focused window.
- * @param windows The windows for accessibility.
- */
- @Override
- public void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
+ private void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows) {
+ // TODO(b/322444245): no longer need to get a lock.
synchronized (mLock) {
- if (!Flags.computeWindowChangesOnA11yV2()) {
- // If the flag is enabled, it's already done in #createWindowInfoListLocked.
- updateWindowsByWindowAttributesLocked(windows);
- }
if (DEBUG) {
Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, "
+ "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId,
@@ -490,9 +478,7 @@
}
/**
- * Called when the windows for accessibility changed. This is called if
- * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is
- * true.
+ * Called when the windows for accessibility changed.
*
* @param forceSend Send the windows for accessibility even if they haven't
* changed.
@@ -655,16 +641,6 @@
return true;
}
- private void updateWindowsByWindowAttributesLocked(List<WindowInfo> windows) {
- for (int i = windows.size() - 1; i >= 0; i--) {
- final WindowInfo windowInfo = windows.get(i);
- final IBinder token = windowInfo.token;
- final int windowId = findWindowIdLocked(
- mAccessibilityUserManager.getCurrentUserIdLocked(), token);
- updateWindowWithWindowAttributes(windowInfo, mWindowAttributes.get(windowId));
- }
- }
-
private void updateWindowWithWindowAttributes(@NonNull WindowInfo windowInfo,
@Nullable AccessibilityWindowAttributes attributes) {
if (attributes == null) {
@@ -990,19 +966,6 @@
private AccessibilityWindowInfo populateReportedWindowLocked(int userId,
WindowInfo window, SparseArray<AccessibilityWindowInfo> oldWindowsById) {
final int windowId = findWindowIdLocked(userId, window.token);
-
- // With the flag enabled, createWindowInfoListLocked() already removes invalid windows.
- if (!Flags.computeWindowChangesOnA11yV2()) {
- if (windowId < 0) {
- return null;
- }
-
- // Don't need to add the embedded hierarchy windows into the a11y windows list.
- if (isEmbeddedHierarchyWindowsLocked(windowId)) {
- return null;
- }
- }
-
final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
reportedWindow.setId(windowId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5c5361b..1a5d1f5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -131,6 +131,9 @@
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.security.Flags.preventIntentRedirect;
+import static android.security.Flags.preventIntentRedirectCollectNestedKeysOnServerIfNotCollected;
+import static android.security.Flags.preventIntentRedirectShowToast;
+import static android.security.Flags.preventIntentRedirectThrowExceptionIfNestedKeysNotCollected;
import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static android.view.Display.INVALID_DISPLAY;
@@ -387,6 +390,7 @@
import android.view.View;
import android.view.WindowManager;
import android.view.autofill.AutofillManagerInternal;
+import android.widget.Toast;
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
@@ -437,6 +441,7 @@
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.ThreadPriorityBooster;
+import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.LowMemDetector.MemFactor;
import com.android.server.appop.AppOpsService;
@@ -478,6 +483,7 @@
import dalvik.annotation.optimization.NeverCompile;
import dalvik.system.VMRuntime;
+
import libcore.util.EmptyArray;
import java.io.File;
@@ -19304,9 +19310,32 @@
*/
public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
if (!preventIntentRedirect()) return;
-
if (intent == null) return;
+ if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0) {
+ Slog.wtf(TAG,
+ "[IntentRedirect] The intent does not have its nested keys collected as a "
+ + "preparation for creating intent creator tokens. Intent: "
+ + intent + "; creatorPackage: " + creatorPackage);
+ if (preventIntentRedirectShowToast()) {
+ UiThread.getHandler().post(
+ () -> Toast.makeText(mContext,
+ "Nested keys not collected. go/report-bug-intentRedir to report a"
+ + " bug", Toast.LENGTH_LONG).show());
+ }
+ if (preventIntentRedirectThrowExceptionIfNestedKeysNotCollected()) {
+ // this flag will be internal only, not ramped to public.
+ throw new SecurityException(
+ "The intent does not have its nested keys collected as a preparation for "
+ + "creating intent creator tokens. Intent: "
+ + intent + "; creatorPackage: " + creatorPackage);
+ }
+ if (preventIntentRedirectCollectNestedKeysOnServerIfNotCollected()) {
+ // this flag will be ramped to public.
+ intent.collectExtraIntentKeys();
+ }
+ }
+
String targetPackage = intent.getComponent() != null
? intent.getComponent().getPackageName()
: intent.getPackage();
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 415f78a..b7a5f3e 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -598,7 +598,7 @@
}
if (r != null) {
- mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(),
+ mPackageWatchdog.notifyPackageFailure(r.getPackageListWithVersionCode(),
PackageWatchdog.FAILURE_REASON_APP_CRASH);
synchronized (mService) {
@@ -1142,7 +1142,7 @@
}
// Notify PackageWatchdog without the lock held
if (packageList != null) {
- mPackageWatchdog.onPackageFailure(packageList,
+ mPackageWatchdog.notifyPackageFailure(packageList,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2eb9f3c..d5bd057 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1262,6 +1262,17 @@
mFrameworkStatsLogger = frameworkStatsLogger;
}
+ private static float clampPowerMah(double powerMah, String consumer) {
+ float resultPowerMah = 0;
+ if (powerMah <= Float.MAX_VALUE && powerMah >= Float.MIN_VALUE) {
+ resultPowerMah = (float) powerMah;
+ } else {
+ // Handle overflow appropriately
+ Slog.wtfStack(TAG, consumer + " reported powerMah float overflow: " + powerMah);
+ }
+ return resultPowerMah;
+ }
+
/**
* Generates StatsEvents for the supplied battery usage stats and adds them to
* the supplied list.
@@ -1282,7 +1293,8 @@
bus.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
- final float totalDeviceConsumedPowerMah = (float) deviceConsumer.getConsumedPower();
+ final float totalDeviceConsumedPowerMah =
+ clampPowerMah(deviceConsumer.getConsumedPower(), "AggregateBatteryConsumer");
for (@BatteryConsumer.PowerComponentId int powerComponentId :
deviceConsumer.getPowerComponentIds()) {
@@ -1314,7 +1326,9 @@
// Log single atom for BatteryUsageStats per uid/process_state/component/etc.
for (UidBatteryConsumer uidConsumer : uidConsumers) {
final int uid = uidConsumer.getUid();
- final float totalConsumedPowerMah = (float) uidConsumer.getConsumedPower();
+
+ final float totalConsumedPowerMah =
+ clampPowerMah(uidConsumer.getConsumedPower(), "uidConsumer-" + uid);
for (@BatteryConsumer.PowerComponentId int powerComponentId :
uidConsumer.getPowerComponentIds()) {
@@ -1358,7 +1372,10 @@
}
final String powerComponentName = batteryConsumer.getPowerComponentName(componentId);
- final float powerMah = (float) batteryConsumer.getConsumedPower(key);
+ final double consumedPowerMah = batteryConsumer.getConsumedPower(key);
+ float powerMah =
+ clampPowerMah(
+ consumedPowerMah, "uidConsumer-" + uid + "-" + powerComponentName);
final long powerComponentDurationMillis = batteryConsumer.getUsageDurationMillis(key);
if (powerMah == 0 && powerComponentDurationMillis == 0) {
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index ed41f2e..fa2e674 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -31,7 +31,6 @@
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_TAKE_AUDIO_FOCUS;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
@@ -177,8 +176,6 @@
case OP_RECORD_AUDIO:
case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO:
return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
- case OP_TAKE_AUDIO_FOCUS:
- return PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
default:
return PROCESS_CAPABILITY_NONE;
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 40d5f86..5c2eb5c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4954,6 +4954,15 @@
}
final Set<Integer> deviceTypes = getDeviceSetForStreamDirect(streamType);
+
+ final Set<Integer> a2dpDevices = AudioSystem.intersectionAudioDeviceTypes(
+ AudioSystem.DEVICE_OUT_ALL_A2DP_SET, deviceTypes);
+ if (!a2dpDevices.isEmpty()) {
+ int index = getStreamVolume(streamType,
+ a2dpDevices.toArray(new Integer[0])[0].intValue());
+ mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index);
+ }
+
final Set<Integer> absVolumeMultiModeCaseDevices =
AudioSystem.intersectionAudioDeviceTypes(
mAbsVolumeMultiModeCaseDevices, deviceTypes);
@@ -11425,6 +11434,10 @@
return mSpatializerHelper.canBeSpatialized(attributes, format);
}
+ public @NonNull List<Integer> getSpatializedChannelMasks() {
+ return mSpatializerHelper.getSpatializedChannelMasks();
+ }
+
/** @see Spatializer.SpatializerInfoDispatcherStub */
public void registerSpatializerCallback(
@NonNull ISpatializerCallback cb) {
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 9265ff2..afa90d5 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -59,6 +59,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
@@ -1100,6 +1102,23 @@
return able;
}
+ synchronized @NonNull List<Integer> getSpatializedChannelMasks() {
+ if (!checkSpatializer("getSpatializedChannelMasks")) {
+ return Collections.emptyList();
+ }
+ try {
+ final int[] nativeMasks = new int[0]; // FIXME mSpat query goes here
+ for (int i = 0; i < nativeMasks.length; i++) {
+ nativeMasks[i] = AudioFormat.convertNativeChannelMaskToOutMask(nativeMasks[i]);
+ }
+ final List<Integer> masks = Arrays.stream(nativeMasks).boxed().toList();
+ return masks;
+ } catch (Exception e) { // just catch Exception in case nativeMasks is null
+ Log.e(TAG, "Error calling getSpatializedChannelMasks", e);
+ return Collections.emptyList();
+ }
+ }
+
//------------------------------------------------------
// head tracking
final RemoteCallbackList<ISpatializerHeadTrackingModeCallback> mHeadTrackingModeCallbacks =
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 97a8854..b365ef7 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -434,21 +434,12 @@
public boolean getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(int userId) {
if (!mMandatoryBiometricsEnabled.containsKey(userId)) {
- Slog.d(TAG, "update mb toggle for user " + userId);
updateMandatoryBiometricsForAllProfiles(userId);
}
if (!mMandatoryBiometricsRequirementsSatisfied.containsKey(userId)) {
- Slog.d(TAG, "update mb reqs for user " + userId);
updateMandatoryBiometricsRequirementsForAllProfiles(userId);
}
- Slog.d(TAG, mMandatoryBiometricsEnabled.getOrDefault(userId,
- DEFAULT_MANDATORY_BIOMETRICS_STATUS)
- + " " + mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId,
- DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS)
- + " " + getEnabledForApps(userId)
- + " " + (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
- || mFaceEnrolledForUser.getOrDefault(userId, false /* default */)));
return mMandatoryBiometricsEnabled.getOrDefault(userId,
DEFAULT_MANDATORY_BIOMETRICS_STATUS)
&& mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId,
diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryHelper.java b/services/core/java/com/android/server/crashrecovery/CrashRecoveryHelper.java
index 8e72546..eef2b15 100644
--- a/services/core/java/com/android/server/crashrecovery/CrashRecoveryHelper.java
+++ b/services/core/java/com/android/server/crashrecovery/CrashRecoveryHelper.java
@@ -93,7 +93,8 @@
return;
}
final List<VersionedPackage> pkgList = Collections.singletonList(pkg);
- PackageWatchdog.getInstance(mContext).onPackageFailure(pkgList, PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
+ PackageWatchdog.getInstance(mContext).notifyPackageFailure(pkgList,
+ PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
});
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 7cbacd6..4b7e74a 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -22,7 +22,6 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
@@ -1784,22 +1783,13 @@
mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked(
mDisplayId, visibleWindows);
- if (!com.android.server.accessibility.Flags.computeWindowChangesOnA11yV2()) {
- windows = buildWindowInfoListLocked(visibleWindows, screenSize);
- }
-
// Gets the top focused display Id and window token for supporting multi-display.
topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
}
- if (com.android.server.accessibility.Flags.computeWindowChangesOnA11yV2()) {
- mCallback.onAccessibilityWindowsChanged(forceSend, topFocusedDisplayId,
- topFocusedWindowToken, screenSize, visibleWindows);
- } else {
- mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
- topFocusedWindowToken, windows);
- }
+ mCallback.onAccessibilityWindowsChanged(forceSend, topFocusedDisplayId,
+ topFocusedWindowToken, screenSize, visibleWindows);
// Recycle the windows as we do not need them.
for (final AccessibilityWindowsPopulator.AccessibilityWindow window : visibleWindows) {
@@ -1808,166 +1798,6 @@
mInitialized = true;
}
- // Here are old code paths, called when computeWindowChangesOnA11yV2 flag is disabled.
- // LINT.IfChange
-
- /**
- * From a list of windows, decides windows to be exposed to accessibility based on touchable
- * region in the screen.
- */
- private List<WindowInfo> buildWindowInfoListLocked(List<AccessibilityWindow> visibleWindows,
- Point screenSize) {
- final List<WindowInfo> windows = new ArrayList<>();
- final Set<IBinder> addedWindows = mTempBinderSet;
- addedWindows.clear();
-
- boolean focusedWindowAdded = false;
-
- final int visibleWindowCount = visibleWindows.size();
-
- Region unaccountedSpace = mTempRegion;
- unaccountedSpace.set(0, 0, screenSize.x, screenSize.y);
-
- // Iterate until we figure out what is touchable for the entire screen.
- for (int i = 0; i < visibleWindowCount; i++) {
- final AccessibilityWindow a11yWindow = visibleWindows.get(i);
- final Region regionInWindow = new Region();
- a11yWindow.getTouchableRegionInWindow(regionInWindow);
- if (windowMattersToAccessibility(a11yWindow, regionInWindow, unaccountedSpace)) {
- addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows);
- if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
- updateUnaccountedSpace(a11yWindow, unaccountedSpace);
- }
- focusedWindowAdded |= a11yWindow.isFocused();
- } else if (a11yWindow.isUntouchableNavigationBar()) {
- // If this widow is navigation bar without touchable region, accounting the
- // region of navigation bar inset because all touch events from this region
- // would be received by launcher, i.e. this region is a un-touchable one
- // for the application.
- unaccountedSpace.op(
- getSystemBarInsetsFrame(
- mService.mWindowMap.get(a11yWindow.getWindowInfo().token)),
- unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
- }
-
- if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
- break;
- }
- }
-
- // Remove child/parent references to windows that were not added.
- final int windowCount = windows.size();
- for (int i = 0; i < windowCount; i++) {
- WindowInfo window = windows.get(i);
- if (!addedWindows.contains(window.parentToken)) {
- window.parentToken = null;
- }
- if (window.childTokens != null) {
- final int childTokenCount = window.childTokens.size();
- for (int j = childTokenCount - 1; j >= 0; j--) {
- if (!addedWindows.contains(window.childTokens.get(j))) {
- window.childTokens.remove(j);
- }
- }
- // Leave the child token list if empty.
- }
- }
-
- addedWindows.clear();
-
- return windows;
- }
-
- // Some windows should be excluded from unaccounted space computation, though they still
- // should be reported
- private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) {
- // Do not account space of trusted non-touchable windows, except the split-screen
- // divider.
- // If it's not trusted, touch events are not sent to the windows behind it.
- if (!a11yWindow.isTouchable()
- && (a11yWindow.getType() != TYPE_DOCK_DIVIDER)
- && a11yWindow.isTrustedOverlay()) {
- return false;
- }
-
- if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
- return false;
- }
- return true;
- }
-
- private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow,
- Region regionInScreen, Region unaccountedSpace) {
- if (a11yWindow.isFocused()) {
- return true;
- }
-
- // Ignore non-touchable windows, except the split-screen divider, which is
- // occasionally non-touchable but still useful for identifying split-screen
- // mode and the PIP menu.
- if (!a11yWindow.isTouchable()
- && (a11yWindow.getType() != TYPE_DOCK_DIVIDER
- && !a11yWindow.isPIPMenu())) {
- return false;
- }
-
- // If the window is completely covered by other windows - ignore.
- if (unaccountedSpace.quickReject(regionInScreen)) {
- return false;
- }
-
- // Add windows of certain types not covered by modal windows.
- if (isReportedWindowType(a11yWindow.getType())) {
- return true;
- }
-
- return false;
- }
-
- private void updateUnaccountedSpace(AccessibilityWindow a11yWindow,
- Region unaccountedSpace) {
- if (a11yWindow.getType()
- != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
- // Account for the space this window takes if the window
- // is not an accessibility overlay which does not change
- // the reported windows.
- final Region touchableRegion = mTempRegion2;
- a11yWindow.getTouchableRegionInScreen(touchableRegion);
- unaccountedSpace.op(touchableRegion, unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
- }
- }
-
- private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow,
- Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut) {
- final WindowInfo window = a11yWindow.getWindowInfo();
- if (window.token == null) {
- // The window was used in calculating visible windows but does not have an
- // associated IWindow token, so exclude it from the list returned to accessibility.
- return;
- }
- window.regionInScreen.set(regionInScreen);
- window.layer = tokenOut.size();
- out.add(window);
- tokenOut.add(window.token);
- }
-
- private static boolean isReportedWindowType(int windowType) {
- return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
- && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
- && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
- && windowType != WindowManager.LayoutParams.TYPE_DRAG
- && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
- && windowType != WindowManager.LayoutParams.TYPE_POINTER
- && windowType != TYPE_MAGNIFICATION_OVERLAY
- && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
- && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
- && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
- }
-
- // LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java)
-
private WindowState getTopFocusWindow() {
return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index fd2a909..7fc11e6 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -724,8 +724,7 @@
}
// Compute system bar insets frame if needed.
- if (com.android.server.accessibility.Flags.computeWindowChangesOnA11yV2()
- && windowState != null && instance.isUntouchableNavigationBar()) {
+ if (windowState != null && instance.isUntouchableNavigationBar()) {
final InsetsSourceProvider provider =
windowState.getControllableInsetProvider();
if (provider != null) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index ce032b4..c77b1d9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -46,7 +46,6 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
-import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
import android.view.inputmethod.ImeTracker;
import android.window.ScreenCapture;
@@ -158,26 +157,8 @@
* accessibility changed.
*/
public interface WindowsForAccessibilityCallback {
-
/**
- * Called when the windows for accessibility changed. This is called if
- * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is
- * false.
- *
- * @param forceSend Send the windows for accessibility even if they haven't changed.
- * @param topFocusedDisplayId The display Id which has the top focused window.
- * @param topFocusedWindowToken The window token of top focused window.
- * @param windows The windows for accessibility.
- */
- void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
- IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows);
-
- /**
- * Called when the windows for accessibility changed. This is called if
- * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is
- * true.
- * TODO(b/322444245): Remove screenSize parameter by getting it from
- * DisplayManager#getDisplay(int).getRealSize() on the a11y side.
+ * Called when the windows for accessibility changed.
*
* @param forceSend Send the windows for accessibility even if they haven't changed.
* @param topFocusedDisplayId The display Id which has the top focused window.
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 403930d..2ae31ad 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -18,20 +18,24 @@
import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT;
import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId;
+import static com.android.server.accessibility.AccessibilityWindowManagerTest.EventWindowIdMatcher.eventWindowId;
import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges;
-import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId;
+import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.windowId;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -42,14 +46,13 @@
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.Region;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.SparseArray;
import android.view.Display;
import android.view.IWindow;
@@ -63,6 +66,7 @@
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.test.MessageCapturingHandler;
+import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
@@ -70,7 +74,6 @@
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -81,17 +84,9 @@
import java.util.Arrays;
import java.util.List;
-// This test verifies deprecated codepath. Probably changing this file means
-// AccessibilityWindowManagerWithAccessibilityWindowTest also needs to be updated.
-// LINT.IfChange
-
/**
- * Tests for the AccessibilityWindowManager with Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2
- * enabled.
- * TODO(b/322444245): Merge with AccessibilityWindowManagerWithAccessibilityWindowTest
- * after completing the flag migration.
+ * Tests for the AccessibilityWindowManager.
*/
-@RequiresFlagsDisabled(Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2)
public class AccessibilityWindowManagerTest {
private static final String PACKAGE_NAME = "com.android.server.accessibility";
private static final boolean FORCE_SEND = true;
@@ -122,9 +117,8 @@
// List of window token, mapping from windowId -> window token.
private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>();
- // List of window info lists, mapping from displayId -> window info lists.
- private final SparseArray<ArrayList<WindowInfo>> mWindowInfos =
- new SparseArray<>();
+ // List of window info lists, mapping from displayId -> a11y window lists.
+ private final SparseArray<ArrayList<AccessibilityWindow>> mWindows = new SparseArray<>();
// List of callback, mapping from displayId -> callback.
private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows =
new SparseArray<>();
@@ -134,6 +128,13 @@
private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
+ // This maps displayId -> next region offset.
+ // Touchable region must have un-occluded area so that it's exposed to a11y services.
+ // This offset can be used as left and top of new region so that top-left of each region are
+ // kept visible.
+ // It's expected to be incremented by some amount everytime the value is used.
+ private final SparseArray<Integer> mNextRegionOffsets = new SparseArray<>();
+
@Mock private WindowManagerInternal mMockWindowManagerInternal;
@Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender;
@Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
@@ -144,9 +145,6 @@
@Mock private IBinder mMockEmbeddedToken;
@Mock private IBinder mMockInvalidToken;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
@@ -159,7 +157,7 @@
anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME);
doAnswer((invocation) -> {
- onWindowsForAccessibilityChanged(invocation.getArgument(0), false);
+ onAccessibilityWindowsChanged(invocation.getArgument(0), false);
return null;
}).when(mMockWindowManagerInternal).computeWindowsForAccessibility(anyInt());
@@ -173,7 +171,7 @@
// as top focused display before each testing starts.
startTrackingPerDisplay(Display.DEFAULT_DISPLAY);
- // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged.
+ // AccessibilityEventSender is invoked during onAccessibilityWindowsChanged.
// Resets it for mockito verify of further test case.
Mockito.reset(mMockA11yEventSender);
@@ -237,19 +235,18 @@
@Test
public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() {
final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
- WindowInfo focusedWindowInfo =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX);
+ final WindowInfo focusedWindowInfo =
+ mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo();
assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked(
USER_SYSTEM_ID, focusedWindowInfo.token));
focusedWindowInfo.focused = false;
- focusedWindowInfo =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1);
- focusedWindowInfo.focused = true;
+ mWindows.get(Display.DEFAULT_DISPLAY).get(
+ DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().focused = true;
mA11yWindowManager.onTouchInteractionStart();
setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
}
@@ -273,7 +270,7 @@
changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
- onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
// The active window should not be changed.
assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
// The top focused window should not be changed.
@@ -301,8 +298,8 @@
changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
- onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
// The active window should be changed.
assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
// The top focused window should be changed.
@@ -312,53 +309,181 @@
@Test
public void onWindowsChanged_shouldReportCorrectLayer() {
- // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
+ // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup.
List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
for (int i = 0; i < a11yWindows.size(); i++) {
final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i);
- assertThat(mWindowInfos.get(Display.DEFAULT_DISPLAY).size() - windowInfo.layer - 1,
+ assertThat(mWindows.get(Display.DEFAULT_DISPLAY).size() - i - 1,
is(a11yWindow.getLayer()));
}
}
@Test
public void onWindowsChanged_shouldReportCorrectOrder() {
- // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
+ // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup.
List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
for (int i = 0; i < a11yWindows.size(); i++) {
final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
final IBinder windowToken = mA11yWindowManager
.getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId());
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i);
+ final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY)
+ .get(i).getWindowInfo();
assertThat(windowToken, is(windowInfo.token));
}
}
@Test
- public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- final int correctLayer =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer();
- windowInfo.layer += 1;
+ public void onWindowsChanged_shouldNotReportNonTouchableWindow() {
+ final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ when(window.isTouchable()).thenReturn(false);
+ final int windowId = mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, window.getWindowInfo().token);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
- assertNotEquals(correctLayer,
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer());
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, not(hasItem(windowId(windowId))));
}
@Test
- public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() {
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- final int correctLayer =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer();
- windowInfo.layer += 1;
+ public void onWindowsChanged_shouldReportFocusedNonTouchableWindow() {
+ final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(
+ DEFAULT_FOCUSED_INDEX);
+ when(window.isTouchable()).thenReturn(false);
+ final int windowId = mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, window.getWindowInfo().token);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
- assertEquals(correctLayer,
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer());
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasItem(windowId(windowId)));
+ }
+
+ @Test
+ public void onWindowsChanged_trustedFocusedNonTouchableWindow_shouldNotHideWindowsBelow() {
+ // Make the focused trusted un-touchable window fullscreen.
+ final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(
+ DEFAULT_FOCUSED_INDEX);
+ setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+ when(window.isTouchable()).thenReturn(false);
+ when(window.isTrustedOverlay()).thenReturn(true);
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS));
+ }
+
+ @Test
+ public void onWindowsChanged_accessibilityOverlay_shouldNotHideWindowsBelow() {
+ // Make the a11y overlay window fullscreen.
+ final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+ when(window.getType()).thenReturn(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY);
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS));
+ }
+
+ @Test
+ public void onWindowsChanged_shouldReportFocusedWindowEvenIfOccluded() {
+ // Make the front window fullscreen.
+ final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(frontWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+ final int frontWindowId = mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, frontWindow.getWindowInfo().token);
+
+ final AccessibilityWindow focusedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(
+ DEFAULT_FOCUSED_INDEX);
+ final int focusedWindowId = mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, focusedWindow.getWindowInfo().token);
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasSize(2));
+ assertThat(a11yWindows.get(0), windowId(frontWindowId));
+ assertThat(a11yWindows.get(1), windowId(focusedWindowId));
+ }
+
+ @Test
+ public void onWindowsChanged_embeddedWindows_shouldOnlyReportHost() throws RemoteException {
+ final Rect embeddingBounds = new Rect(0, 0, 200, 100);
+
+ // The embedded window comes front of the host window.
+ final IBinder embeddedWindowLeashToken = Mockito.mock(IBinder.class);
+ final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, embeddedWindowLeashToken, USER_SYSTEM_ID);
+ final AccessibilityWindow embeddedWindow = createMockAccessibilityWindow(
+ mA11yWindowTokens.get(embeddedWindowId), Display.DEFAULT_DISPLAY);
+ setRegionForMockAccessibilityWindow(embeddedWindow, new Region(embeddingBounds));
+ mWindows.get(Display.DEFAULT_DISPLAY).set(0, embeddedWindow);
+
+ final IBinder hostWindowLeashToken = Mockito.mock(IBinder.class);
+ final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, hostWindowLeashToken, USER_SYSTEM_ID);
+ final AccessibilityWindow hostWindow = createMockAccessibilityWindow(
+ mA11yWindowTokens.get(hostWindowId), Display.DEFAULT_DISPLAY);
+ setRegionForMockAccessibilityWindow(hostWindow, new Region(embeddingBounds));
+ mWindows.get(Display.DEFAULT_DISPLAY).set(1, hostWindow);
+
+ mA11yWindowManager.associateEmbeddedHierarchyLocked(
+ hostWindowLeashToken, embeddedWindowLeashToken);
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, not(hasItem(windowId(embeddedWindowId))));
+ assertThat(a11yWindows.get(0), windowId(hostWindowId));
+ final Rect bounds = new Rect();
+ a11yWindows.get(0).getBoundsInScreen(bounds);
+ assertEquals(bounds, embeddingBounds);
+ }
+
+ @Test
+ public void onWindowsChanged_shouldNotReportfullyOccludedWindow() {
+ final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(frontWindow, new Region(100, 100, 300, 300));
+ final int frontWindowId = mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, frontWindow.getWindowInfo().token);
+
+ // index 1 is focused. Let's use the next one for this test.
+ final AccessibilityWindow occludedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(2);
+ setRegionForMockAccessibilityWindow(occludedWindow, new Region(150, 150, 250, 250));
+ final int occludedWindowId = mA11yWindowManager.findWindowIdLocked(
+ USER_SYSTEM_ID, occludedWindow.getWindowInfo().token);
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
+ final List<AccessibilityWindowInfo> a11yWindows =
+ mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasItem(windowId(frontWindowId)));
+ assertThat(a11yWindows, not(hasItem(windowId(occludedWindowId))));
+ }
+
+ @Test
+ public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
+ assertNotEquals("new title",
+ toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)
+ .get(0).getTitle()));
+
+ mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().title = "new title";
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+ assertEquals("new title",
+ toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)
+ .get(0).getTitle()));
}
@Test
@@ -368,14 +493,10 @@
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0);
final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
true, USER_SYSTEM_ID);
- final WindowInfo windowInfo = WindowInfo.obtain();
- windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
- windowInfo.token = token.asBinder();
- windowInfo.layer = 0;
- windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- mWindowInfos.get(Display.DEFAULT_DISPLAY).set(0, windowInfo);
+ mWindows.get(Display.DEFAULT_DISPLAY).set(0,
+ createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY));
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
assertNotEquals(oldWindow,
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0));
}
@@ -383,12 +504,12 @@
@Test
public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() {
final WindowInfo focusedWindowInfo =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX);
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+ mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo();
+ final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo();
focusedWindowInfo.focused = false;
windowInfo.focused = true;
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)
.isFocused());
}
@@ -497,15 +618,18 @@
@Test
public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() {
// Updates top 2 z-order WindowInfo are whole visible.
- WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
- windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1);
- windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2,
- SCREEN_WIDTH, SCREEN_HEIGHT);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(firstWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2));
+ final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1);
+ setRegionForMockAccessibilityWindow(secondWindow,
+ new Region(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT));
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
final List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasSize(2));
final Region outBounds = new Region();
int windowId = a11yWindows.get(0).getId();
@@ -523,12 +647,17 @@
@Test
public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() {
// Updates z-order #1 WindowInfo is half visible.
- WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
+ final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(firstWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2));
+ final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1);
+ setRegionForMockAccessibilityWindow(secondWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
final List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasSize(2));
final Region outBounds = new Region();
int windowId = a11yWindows.get(1).getId();
@@ -539,9 +668,17 @@
@Test
public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() {
- // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible.
+ // z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible.
+ final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(firstWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
final List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ // Note that the second window is also exposed even if region is empty because it's focused.
+ assertThat(a11yWindows, hasSize(2));
final Region outBounds = new Region();
int windowId = a11yWindows.get(1).getId();
@@ -552,16 +689,21 @@
@Test
public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() {
// Updates z-order #0 WindowInfo to have two interact-able areas.
- Region region = new Region(0, 0, SCREEN_WIDTH, 200);
+ final Region region = new Region(0, 0, SCREEN_WIDTH, 200);
region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION);
- WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- windowInfo.regionInScreen.set(region);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(firstWindow, region);
+ final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1);
+ setRegionForMockAccessibilityWindow(secondWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
final List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasSize(2));
final Region outBounds = new Region();
- int windowId = a11yWindows.get(1).getId();
+ final int windowId = a11yWindows.get(1).getId();
mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
assertFalse(outBounds.getBounds().isEmpty());
@@ -572,7 +714,8 @@
@Test
public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() {
final IBinder eventWindowToken =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1).token;
+ mWindows.get(Display.DEFAULT_DISPLAY)
+ .get(DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().token;
final int eventWindowId = mA11yWindowManager.findWindowIdLocked(
USER_SYSTEM_ID, eventWindowToken);
when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates())
@@ -611,11 +754,11 @@
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
allOf(displayId(Display.DEFAULT_DISPLAY),
- a11yWindowId(currentActiveWindowId),
+ eventWindowId(currentActiveWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
assertThat(captor.getAllValues().get(1),
allOf(displayId(Display.DEFAULT_DISPLAY),
- a11yWindowId(eventWindowId),
+ eventWindowId(eventWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
}
@@ -641,7 +784,7 @@
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
allOf(displayId(Display.DEFAULT_DISPLAY),
- a11yWindowId(eventWindowId),
+ eventWindowId(eventWindowId),
a11yWindowChanges(
AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
}
@@ -690,12 +833,12 @@
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
allOf(displayId(initialDisplayId),
- a11yWindowId(initialWindowId),
+ eventWindowId(initialWindowId),
a11yWindowChanges(
AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
assertThat(captor.getAllValues().get(1),
allOf(displayId(eventDisplayId),
- a11yWindowId(eventWindowId),
+ eventWindowId(eventWindowId),
a11yWindowChanges(
AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
}
@@ -722,7 +865,7 @@
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
noUse);
assertThat(mA11yWindowManager.getFocusedWindowId(
- AccessibilityNodeInfo.FOCUS_ACCESSIBILITY),
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY),
is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID));
}
@@ -751,11 +894,11 @@
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
allOf(displayId(Display.DEFAULT_DISPLAY),
- a11yWindowId(eventWindowId),
+ eventWindowId(eventWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
assertThat(captor.getAllValues().get(1),
allOf(displayId(Display.DEFAULT_DISPLAY),
- a11yWindowId(currentActiveWindowId),
+ eventWindowId(currentActiveWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
}
@@ -763,7 +906,8 @@
public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus()
throws RemoteException {
final IBinder defaultFocusWinToken =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).token;
+ mWindows.get(Display.DEFAULT_DISPLAY).get(
+ DEFAULT_FOCUSED_INDEX).getWindowInfo().token;
final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked(
USER_SYSTEM_ID, defaultFocusWinToken);
when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates())
@@ -808,8 +952,8 @@
@Test
public void getPictureInPictureWindow_shouldNotNull() {
assertNull(mA11yWindowManager.getPictureInPictureWindowLocked());
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1).inPictureInPicture = true;
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ mWindows.get(Display.DEFAULT_DISPLAY).get(1).getWindowInfo().inPictureInPicture = true;
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked());
}
@@ -823,8 +967,9 @@
final IAccessibilityInteractionConnection mockRemoteConnection =
mA11yWindowManager.getConnectionLocked(
USER_SYSTEM_ID, outsideWindowId).getRemote();
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0).hasFlagWatchOutsideTouch = true;
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().hasFlagWatchOutsideTouch =
+ true;
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId);
verify(mockRemoteConnection).notifyOutsideTouch();
@@ -942,18 +1087,14 @@
@Test
public void sendAccessibilityEventOnWindowRemoval() {
- final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+ final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY);
// Removing index 0 because it's not focused, and avoids unnecessary layer change.
final int windowId =
getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
- infos.remove(0);
- for (WindowInfo info : infos) {
- // Adjust layer number because it should start from 0.
- info.layer--;
- }
+ windows.remove(0);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
final ArgumentCaptor<AccessibilityEvent> captor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
@@ -961,27 +1102,21 @@
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
allOf(displayId(Display.DEFAULT_DISPLAY),
- a11yWindowId(windowId),
+ eventWindowId(windowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED)));
}
@Test
public void sendAccessibilityEventOnWindowAddition() throws RemoteException {
- final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
-
- for (WindowInfo info : infos) {
- // Adjust layer number because new window will have 0 so that layer number in
- // A11yWindowInfo in window won't be changed.
- info.layer++;
- }
+ final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY);
final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
false, USER_SYSTEM_ID);
- addWindowInfo(infos, token, 0);
- final int windowId =
- getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, infos.size() - 1);
+ // Adding window to the front so that other windows' layer won't change.
+ windows.add(0, createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY));
+ final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
final ArgumentCaptor<AccessibilityEvent> captor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
@@ -989,17 +1124,17 @@
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
allOf(displayId(Display.DEFAULT_DISPLAY),
- a11yWindowId(windowId),
+ eventWindowId(windowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED)));
}
@Test
public void sendAccessibilityEventOnWindowChange() {
- final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
- infos.get(0).title = "new title";
+ final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY);
+ windows.get(0).getWindowInfo().title = "new title";
final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
final ArgumentCaptor<AccessibilityEvent> captor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
@@ -1007,7 +1142,7 @@
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
allOf(displayId(Display.DEFAULT_DISPLAY),
- a11yWindowId(windowId),
+ eventWindowId(windowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE)));
}
@@ -1017,48 +1152,47 @@
}
private void startTrackingPerDisplay(int displayId) throws RemoteException {
- ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
+ ArrayList<AccessibilityWindow> windowsForDisplay = new ArrayList<>();
// Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
// mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos
// for the test.
- int layer = 0;
for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) {
final IWindow token = addAccessibilityInteractionConnection(displayId,
true, USER_SYSTEM_ID);
- addWindowInfo(windowInfosForDisplay, token, layer++);
+ windowsForDisplay.add(createMockAccessibilityWindow(token, displayId));
}
for (int i = 0; i < NUM_APP_WINDOWS; i++) {
final IWindow token = addAccessibilityInteractionConnection(displayId,
false, USER_SYSTEM_ID);
- addWindowInfo(windowInfosForDisplay, token, layer++);
+ windowsForDisplay.add(createMockAccessibilityWindow(token, displayId));
}
// Sets up current focused window of display.
// Each display has its own current focused window if config_perDisplayFocusEnabled is true.
// Otherwise only default display needs to current focused window.
if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) {
- windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true;
+ windowsForDisplay.get(DEFAULT_FOCUSED_INDEX).getWindowInfo().focused = true;
}
// Turns on windows tracking, and update window info.
mA11yWindowManager.startTrackingWindows(displayId, false);
// Puts window lists into array.
- mWindowInfos.put(displayId, windowInfosForDisplay);
+ mWindows.put(displayId, windowsForDisplay);
// Sets the default display is the top focused display and
// its current focused window is the top focused window.
if (displayId == Display.DEFAULT_DISPLAY) {
setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX);
}
// Invokes callback for sending window lists to A11y framework.
- onWindowsForAccessibilityChanged(displayId, FORCE_SEND);
+ onAccessibilityWindowsChanged(displayId, FORCE_SEND);
assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(),
- windowInfosForDisplay.size());
+ windowsForDisplay.size());
}
private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) {
ArgumentCaptor<WindowsForAccessibilityCallback> windowsForAccessibilityCallbacksCaptor =
ArgumentCaptor.forClass(
- WindowManagerInternal.WindowsForAccessibilityCallback.class);
+ WindowsForAccessibilityCallback.class);
verify(mMockWindowManagerInternal)
.setWindowsForAccessibilityCallback(eq(displayId),
windowsForAccessibilityCallbacksCaptor.capture());
@@ -1106,36 +1240,28 @@
return windowId;
}
- private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) {
- final WindowInfo windowInfo = WindowInfo.obtain();
- windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
- windowInfo.token = windowToken.asBinder();
- windowInfo.layer = layer;
- windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- windowInfos.add(windowInfo);
- }
-
private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) {
- final IBinder windowToken = mWindowInfos.get(displayId).get(index).token;
+ final IBinder windowToken = mWindows.get(displayId).get(index).getWindowInfo().token;
return mA11yWindowManager.findWindowIdLocked(
USER_SYSTEM_ID, windowToken);
}
private void setTopFocusedWindowAndDisplay(int displayId, int index) {
// Sets the top focus window.
- mTopFocusedWindowToken = mWindowInfos.get(displayId).get(index).token;
+ mTopFocusedWindowToken = mWindows.get(displayId).get(index).getWindowInfo().token;
// Sets the top focused display.
mTopFocusedDisplayId = displayId;
}
- private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) {
+ private void onAccessibilityWindowsChanged(int displayId, boolean forceSend) {
WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId);
if (callbacks == null) {
callbacks = getWindowsForAccessibilityCallbacks(displayId);
mCallbackOfWindows.put(displayId, callbacks);
}
- callbacks.onWindowsForAccessibilityChanged(forceSend, mTopFocusedDisplayId,
- mTopFocusedWindowToken, mWindowInfos.get(displayId));
+ callbacks.onAccessibilityWindowsChanged(forceSend, mTopFocusedDisplayId,
+ mTopFocusedWindowToken, new Point(SCREEN_WIDTH, SCREEN_HEIGHT),
+ mWindows.get(displayId));
}
private void changeFocusedWindowOnDisplayPerDisplayFocusConfig(
@@ -1144,23 +1270,23 @@
if (mSupportPerDisplayFocus) {
// Gets the old focused window of display which wants to change focused window.
WindowInfo focusedWindowInfo =
- mWindowInfos.get(changeFocusedDisplayId).get(oldFocusedWindowIndex);
+ mWindows.get(changeFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo();
// Resets the focus of old focused window.
focusedWindowInfo.focused = false;
// Gets the new window of display which wants to change focused window.
focusedWindowInfo =
- mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex);
+ mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo();
// Sets the focus of new focused window.
focusedWindowInfo.focused = true;
} else {
// Gets the window of display which wants to change focused window.
WindowInfo focusedWindowInfo =
- mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex);
+ mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo();
// Sets the focus of new focused window.
focusedWindowInfo.focused = true;
// Gets the old focused window of old top focused display.
focusedWindowInfo =
- mWindowInfos.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex);
+ mWindows.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo();
// Resets the focus of old focused window.
focusedWindowInfo.focused = false;
// Changes the top focused display and window.
@@ -1168,6 +1294,39 @@
}
}
+ private AccessibilityWindow createMockAccessibilityWindow(IWindow windowToken, int displayId) {
+ final WindowInfo windowInfo = WindowInfo.obtain();
+ windowInfo.type = WindowManager.LayoutParams.TYPE_APPLICATION;
+ windowInfo.token = windowToken.asBinder();
+
+ final AccessibilityWindow window = Mockito.mock(AccessibilityWindow.class);
+ when(window.getWindowInfo()).thenReturn(windowInfo);
+ when(window.isFocused()).thenAnswer(invocation -> windowInfo.focused);
+ when(window.isTouchable()).thenReturn(true);
+ when(window.getType()).thenReturn(windowInfo.type);
+
+ setRegionForMockAccessibilityWindow(window, nextToucableRegion(displayId));
+ return window;
+ }
+
+ private void setRegionForMockAccessibilityWindow(AccessibilityWindow window, Region region) {
+ doAnswer(invocation -> {
+ ((Region) invocation.getArgument(0)).set(region);
+ return null;
+ }).when(window).getTouchableRegionInScreen(any(Region.class));
+ doAnswer(invocation -> {
+ ((Region) invocation.getArgument(0)).set(region);
+ return null;
+ }).when(window).getTouchableRegionInWindow(any(Region.class));
+ }
+
+ private Region nextToucableRegion(int displayId) {
+ final int topLeft = mNextRegionOffsets.get(displayId, 0);
+ final int bottomRight = topLeft + 100;
+ mNextRegionOffsets.put(displayId, topLeft + 10);
+ return new Region(topLeft, topLeft, bottomRight, bottomRight);
+ }
+
@Nullable
private static String toString(@Nullable CharSequence cs) {
return cs == null ? null : cs.toString();
@@ -1196,16 +1355,16 @@
}
}
- static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+ static class EventWindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
private int mWindowId;
- WindowIdMatcher(int windowId) {
+ EventWindowIdMatcher(int windowId) {
super();
mWindowId = windowId;
}
- static WindowIdMatcher a11yWindowId(int windowId) {
- return new WindowIdMatcher(windowId);
+ static EventWindowIdMatcher eventWindowId(int windowId) {
+ return new EventWindowIdMatcher(windowId);
}
@Override
@@ -1241,5 +1400,27 @@
description.appendText("Matching to window changes " + mWindowChanges);
}
}
+
+ static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityWindowInfo> {
+ private final int mWindowId;
+
+ WindowIdMatcher(int windowId) {
+ super();
+ mWindowId = windowId;
+ }
+
+ static WindowIdMatcher windowId(int windowId) {
+ return new WindowIdMatcher(windowId);
+ }
+
+ @Override
+ protected boolean matchesSafely(AccessibilityWindowInfo window) {
+ return window.getId() == mWindowId;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matching to windowId " + mWindowId);
+ }
+ }
}
-// LINT.ThenChange(/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java)
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
deleted file mode 100644
index 1904145..0000000
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
+++ /dev/null
@@ -1,1444 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.accessibility;
-
-import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT;
-import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.DisplayIdMatcher.displayId;
-import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.WindowIdMatcher.windowId;
-import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.WindowChangesMatcher.a11yWindowChanges;
-import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.EventWindowIdMatcher.eventWindowId;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-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.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.annotation.Nullable;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.IBinder;
-import android.os.LocaleList;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.IWindow;
-import android.view.WindowInfo;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityWindowAttributes;
-import android.view.accessibility.AccessibilityWindowInfo;
-import android.view.accessibility.IAccessibilityInteractionConnection;
-
-import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
-import com.android.server.accessibility.test.MessageCapturingHandler;
-import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow;
-import com.android.server.wm.WindowManagerInternal;
-import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
-
-import org.hamcrest.Description;
-import org.hamcrest.TypeSafeMatcher;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Tests for the AccessibilityWindowManager with Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2
- * TODO(b/322444245): Merge with AccessibilityWindowManagerTest
- * after completing the flag migration.
- */
-@RequiresFlagsEnabled(Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2)
-public class AccessibilityWindowManagerWithAccessibilityWindowTest {
- private static final String PACKAGE_NAME = "com.android.server.accessibility";
- private static final boolean FORCE_SEND = true;
- private static final boolean SEND_ON_WINDOW_CHANGES = false;
- private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM;
- private static final int USER_PROFILE = 11;
- private static final int USER_PROFILE_PARENT = 1;
- private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
- private static final int NUM_GLOBAL_WINDOWS = 4;
- private static final int NUM_APP_WINDOWS = 4;
- private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS);
- private static final int DEFAULT_FOCUSED_INDEX = 1;
- private static final int SCREEN_WIDTH = 1080;
- private static final int SCREEN_HEIGHT = 1920;
- private static final int INVALID_ID = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
- private static final int HOST_WINDOW_ID = 10;
- private static final int EMBEDDED_WINDOW_ID = 11;
- private static final int OTHER_WINDOW_ID = 12;
-
- private AccessibilityWindowManager mA11yWindowManager;
- // Window manager will support multiple focused window if config_perDisplayFocusEnabled is true,
- // i.e., each display would have its current focused window, and one of all focused windows
- // would be top focused window. Otherwise, window manager only supports one focused window
- // at all displays, and that focused window would be top focused window.
- private boolean mSupportPerDisplayFocus = false;
- private int mTopFocusedDisplayId = Display.INVALID_DISPLAY;
- private IBinder mTopFocusedWindowToken = null;
-
- // List of window token, mapping from windowId -> window token.
- private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>();
- // List of window info lists, mapping from displayId -> a11y window lists.
- private final SparseArray<ArrayList<AccessibilityWindow>> mWindows = new SparseArray<>();
- // List of callback, mapping from displayId -> callback.
- private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows =
- new SparseArray<>();
- // List of display ID.
- private final ArrayList<Integer> mExpectedDisplayList = new ArrayList<>(Arrays.asList(
- Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID));
-
- private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
-
- // This maps displayId -> next region offset.
- // Touchable region must have un-occluded area so that it's exposed to a11y services.
- // This offset can be used as left and top of new region so that top-left of each region are
- // kept visible.
- // It's expected to be incremented by some amount everytime the value is used.
- private final SparseArray<Integer> mNextRegionOffsets = new SparseArray<>();
-
- @Mock
- private WindowManagerInternal mMockWindowManagerInternal;
- @Mock
- private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender;
- @Mock
- private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
- @Mock
- private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
- @Mock
- private AccessibilityTraceManager mMockA11yTraceManager;
-
- @Mock
- private IBinder mMockHostToken;
- @Mock
- private IBinder mMockEmbeddedToken;
- @Mock
- private IBinder mMockInvalidToken;
-
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Before
- public void setUp() throws RemoteException {
- MockitoAnnotations.initMocks(this);
- when(mMockA11yUserManager.getCurrentUserIdLocked()).thenReturn(USER_SYSTEM_ID);
- when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
- USER_PROFILE)).thenReturn(USER_PROFILE_PARENT);
- when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
- USER_SYSTEM_ID)).thenReturn(USER_SYSTEM_ID);
- when(mMockA11ySecurityPolicy.resolveValidReportedPackageLocked(
- anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME);
-
- doAnswer((invocation) -> {
- onAccessibilityWindowsChanged(invocation.getArgument(0), false);
- return null;
- }).when(mMockWindowManagerInternal).computeWindowsForAccessibility(anyInt());
-
- mA11yWindowManager = new AccessibilityWindowManager(new Object(), mHandler,
- mMockWindowManagerInternal,
- mMockA11yEventSender,
- mMockA11ySecurityPolicy,
- mMockA11yUserManager,
- mMockA11yTraceManager);
- // Starts tracking window of default display and sets the default display
- // as top focused display before each testing starts.
- startTrackingPerDisplay(Display.DEFAULT_DISPLAY);
-
- // AccessibilityEventSender is invoked during onAccessibilityWindowsChanged.
- // Resets it for mockito verify of further test case.
- Mockito.reset(mMockA11yEventSender);
-
- registerLeashedTokenAndWindowId();
- }
-
- @After
- public void tearDown() {
- mHandler.removeAllMessages();
- }
-
- @Test
- public void startTrackingWindows_shouldEnableWindowManagerCallback() {
- // AccessibilityWindowManager#startTrackingWindows already invoked in setup.
- assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
- final WindowsForAccessibilityCallback callbacks =
- mCallbackOfWindows.get(Display.DEFAULT_DISPLAY);
- verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(
- eq(Display.DEFAULT_DISPLAY), eq(callbacks));
- }
-
- @Test
- public void stopTrackingWindows_shouldDisableWindowManagerCallback() {
- assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
- Mockito.reset(mMockWindowManagerInternal);
-
- mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
- assertFalse(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
- verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(
- eq(Display.DEFAULT_DISPLAY), isNull());
-
- }
-
- @Test
- public void stopTrackingWindows_shouldClearWindows() {
- assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
- final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
-
- mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
- assertNull(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY));
- assertEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
- AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
- assertEquals(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID),
- activeWindowId);
- }
-
- @Test
- public void stopTrackingWindows_onNonTopFocusedDisplay_shouldNotResetTopFocusWindow()
- throws RemoteException {
- // At setup, the default display sets be the top focused display and
- // its current focused window sets be the top focused window.
- // Starts tracking window of second display.
- startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
- assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID));
- // Stops tracking windows of second display.
- mA11yWindowManager.stopTrackingWindows(SECONDARY_DISPLAY_ID);
- assertNotEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
- AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
- }
-
- @Test
- public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() {
- final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
- final WindowInfo focusedWindowInfo =
- mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo();
- assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, focusedWindowInfo.token));
-
- focusedWindowInfo.focused = false;
- mWindows.get(Display.DEFAULT_DISPLAY).get(
- DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().focused = true;
-
- mA11yWindowManager.onTouchInteractionStart();
- setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1);
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
- }
-
- @Test
- public void
- onWindowsChanged_focusChangeOnNonTopFocusedDisplay_perDisplayFocusOn_notChangeWindow()
- throws RemoteException {
- // At setup, the default display sets be the top focused display and
- // its current focused window sets be the top focused window.
- // Sets supporting multiple focused window, i.e., config_perDisplayFocusEnabled is true.
- mSupportPerDisplayFocus = true;
- // Starts tracking window of second display.
- startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
- // Gets the active window.
- final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
- // Gets the top focused window.
- final int topFocusedWindowId =
- mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT);
- // Changes the current focused window at second display.
- changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
- DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
-
- onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
- // The active window should not be changed.
- assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
- // The top focused window should not be changed.
- assertEquals(topFocusedWindowId,
- mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT));
- }
-
- @Test
- public void
- onWindowChange_focusChangeToNonTopFocusedDisplay_perDisplayFocusOff_shouldChangeWindow()
- throws RemoteException {
- // At setup, the default display sets be the top focused display and
- // its current focused window sets be the top focused window.
- // Sets not supporting multiple focused window, i.e., config_perDisplayFocusEnabled is
- // false.
- mSupportPerDisplayFocus = false;
- // Starts tracking window of second display.
- startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
- // Gets the active window.
- final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
- // Gets the top focused window.
- final int topFocusedWindowId =
- mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT);
- // Changes the current focused window from default display to second display.
- changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
- DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
- onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
- // The active window should be changed.
- assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
- // The top focused window should be changed.
- assertNotEquals(topFocusedWindowId,
- mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT));
- }
-
- @Test
- public void onWindowsChanged_shouldReportCorrectLayer() {
- // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup.
- List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- for (int i = 0; i < a11yWindows.size(); i++) {
- final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
- assertThat(mWindows.get(Display.DEFAULT_DISPLAY).size() - i - 1,
- is(a11yWindow.getLayer()));
- }
- }
-
- @Test
- public void onWindowsChanged_shouldReportCorrectOrder() {
- // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup.
- List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- for (int i = 0; i < a11yWindows.size(); i++) {
- final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
- final IBinder windowToken = mA11yWindowManager
- .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId());
- final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY)
- .get(i).getWindowInfo();
- assertThat(windowToken, is(windowInfo.token));
- }
- }
-
- @Test
- public void onWindowsChanged_shouldNotReportNonTouchableWindow() {
- final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
- when(window.isTouchable()).thenReturn(false);
- final int windowId = mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, window.getWindowInfo().token);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, not(hasItem(windowId(windowId))));
- }
-
- @Test
- public void onWindowsChanged_shouldReportFocusedNonTouchableWindow() {
- final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(
- DEFAULT_FOCUSED_INDEX);
- when(window.isTouchable()).thenReturn(false);
- final int windowId = mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, window.getWindowInfo().token);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, hasItem(windowId(windowId)));
- }
-
- @Test
- public void onWindowsChanged_trustedFocusedNonTouchableWindow_shouldNotHideWindowsBelow() {
- // Make the focused trusted un-touchable window fullscreen.
- final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(
- DEFAULT_FOCUSED_INDEX);
- setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
- when(window.isTouchable()).thenReturn(false);
- when(window.isTrustedOverlay()).thenReturn(true);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS));
- }
-
- @Test
- public void onWindowsChanged_accessibilityOverlay_shouldNotHideWindowsBelow() {
- // Make the a11y overlay window fullscreen.
- final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
- setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
- when(window.getType()).thenReturn(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS));
- }
-
- @Test
- public void onWindowsChanged_shouldReportFocusedWindowEvenIfOccluded() {
- // Make the front window fullscreen.
- final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
- setRegionForMockAccessibilityWindow(frontWindow,
- new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
- final int frontWindowId = mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, frontWindow.getWindowInfo().token);
-
- final AccessibilityWindow focusedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(
- DEFAULT_FOCUSED_INDEX);
- final int focusedWindowId = mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, focusedWindow.getWindowInfo().token);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, hasSize(2));
- assertThat(a11yWindows.get(0), windowId(frontWindowId));
- assertThat(a11yWindows.get(1), windowId(focusedWindowId));
- }
-
- @Test
- public void onWindowsChanged_embeddedWindows_shouldOnlyReportHost() throws RemoteException {
- final Rect embeddingBounds = new Rect(0, 0, 200, 100);
-
- // The embedded window comes front of the host window.
- final IBinder embeddedWindowLeashToken = Mockito.mock(IBinder.class);
- final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- false, embeddedWindowLeashToken, USER_SYSTEM_ID);
- final AccessibilityWindow embeddedWindow = createMockAccessibilityWindow(
- mA11yWindowTokens.get(embeddedWindowId), Display.DEFAULT_DISPLAY);
- setRegionForMockAccessibilityWindow(embeddedWindow, new Region(embeddingBounds));
- mWindows.get(Display.DEFAULT_DISPLAY).set(0, embeddedWindow);
-
- final IBinder hostWindowLeashToken = Mockito.mock(IBinder.class);
- final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- false, hostWindowLeashToken, USER_SYSTEM_ID);
- final AccessibilityWindow hostWindow = createMockAccessibilityWindow(
- mA11yWindowTokens.get(hostWindowId), Display.DEFAULT_DISPLAY);
- setRegionForMockAccessibilityWindow(hostWindow, new Region(embeddingBounds));
- mWindows.get(Display.DEFAULT_DISPLAY).set(1, hostWindow);
-
- mA11yWindowManager.associateEmbeddedHierarchyLocked(
- hostWindowLeashToken, embeddedWindowLeashToken);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, not(hasItem(windowId(embeddedWindowId))));
- assertThat(a11yWindows.get(0), windowId(hostWindowId));
- final Rect bounds = new Rect();
- a11yWindows.get(0).getBoundsInScreen(bounds);
- assertEquals(bounds, embeddingBounds);
- }
-
- @Test
- public void onWindowsChanged_shouldNotReportfullyOccludedWindow() {
- final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
- setRegionForMockAccessibilityWindow(frontWindow, new Region(100, 100, 300, 300));
- final int frontWindowId = mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, frontWindow.getWindowInfo().token);
-
- // index 1 is focused. Let's use the next one for this test.
- final AccessibilityWindow occludedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(2);
- setRegionForMockAccessibilityWindow(occludedWindow, new Region(150, 150, 250, 250));
- final int occludedWindowId = mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, occludedWindow.getWindowInfo().token);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, hasItem(windowId(frontWindowId)));
- assertThat(a11yWindows, not(hasItem(windowId(occludedWindowId))));
- }
-
- @Test
- public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
- assertNotEquals("new title",
- toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)
- .get(0).getTitle()));
-
- mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().title = "new title";
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
- assertEquals("new title",
- toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)
- .get(0).getTitle()));
- }
-
- @Test
- public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows()
- throws RemoteException {
- final AccessibilityWindowInfo oldWindow =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0);
- final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- true, USER_SYSTEM_ID);
- mWindows.get(Display.DEFAULT_DISPLAY).set(0,
- createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY));
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
- assertNotEquals(oldWindow,
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0));
- }
-
- @Test
- public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() {
- final WindowInfo focusedWindowInfo =
- mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo();
- final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo();
- focusedWindowInfo.focused = false;
- windowInfo.focused = true;
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
- assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)
- .isFocused());
- }
-
- @Test
- public void removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved() {
- for (int i = 0; i < NUM_OF_WINDOWS; i++) {
- final int windowId = mA11yWindowTokens.keyAt(i);
- final IWindow windowToken = mA11yWindowTokens.valueAt(i);
- assertNotNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId));
-
- mA11yWindowManager.removeAccessibilityInteractionConnection(windowToken);
- assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId));
- }
- }
-
- @Test
- public void remoteAccessibilityConnection_binderDied_shouldRemoveConnection() {
- for (int i = 0; i < NUM_OF_WINDOWS; i++) {
- final int windowId = mA11yWindowTokens.keyAt(i);
- final RemoteAccessibilityConnection remoteA11yConnection =
- mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId);
- assertNotNull(remoteA11yConnection);
-
- remoteA11yConnection.binderDied();
- assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId));
- }
- }
-
- @Test
- public void getWindowTokenForUserAndWindowId_shouldNotNull() {
- final List<AccessibilityWindowInfo> windows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- for (int i = 0; i < windows.size(); i++) {
- final int windowId = windows.get(i).getId();
-
- assertNotNull(mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
- USER_SYSTEM_ID, windowId));
- }
- }
-
- @Test
- public void findWindowId() {
- final List<AccessibilityWindowInfo> windows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- for (int i = 0; i < windows.size(); i++) {
- final int windowId = windows.get(i).getId();
- final IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
- USER_SYSTEM_ID, windowId);
-
- assertEquals(mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, windowToken), windowId);
- }
- }
-
- @Test
- public void resolveParentWindowId_windowIsNotEmbedded_shouldReturnGivenId()
- throws RemoteException {
- final int windowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false,
- Mockito.mock(IBinder.class), USER_SYSTEM_ID);
- assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
- }
-
- @Test
- public void resolveParentWindowId_windowIsNotRegistered_shouldReturnGivenId() {
- final int windowId = -1;
- assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
- }
-
- @Test
- public void resolveParentWindowId_windowIsAssociated_shouldReturnParentWindowId()
- throws RemoteException {
- final IBinder mockHostToken = Mockito.mock(IBinder.class);
- final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
- final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- false, mockHostToken, USER_SYSTEM_ID);
- final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- false, mockEmbeddedToken, USER_SYSTEM_ID);
-
- mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
-
- final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
- embeddedWindowId);
- assertEquals(hostWindowId, resolvedWindowId);
- }
-
- @Test
- public void resolveParentWindowId_windowIsDisassociated_shouldReturnGivenId()
- throws RemoteException {
- final IBinder mockHostToken = Mockito.mock(IBinder.class);
- final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
- final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- false, mockHostToken, USER_SYSTEM_ID);
- final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- false, mockEmbeddedToken, USER_SYSTEM_ID);
-
- mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
- mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken);
-
- final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
- embeddedWindowId);
- assertNotEquals(hostWindowId, resolvedWindowId);
- assertEquals(embeddedWindowId, resolvedWindowId);
- }
-
- @Test
- public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() {
- // Updates top 2 z-order WindowInfo are whole visible.
- final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
- setRegionForMockAccessibilityWindow(firstWindow,
- new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2));
- final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1);
- setRegionForMockAccessibilityWindow(secondWindow,
- new Region(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT));
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, hasSize(2));
- final Region outBounds = new Region();
- int windowId = a11yWindows.get(0).getId();
-
- mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
- assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH));
- assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2));
-
- windowId = a11yWindows.get(1).getId();
-
- mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
- assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH));
- assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2));
- }
-
- @Test
- public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() {
- // Updates z-order #1 WindowInfo is half visible.
- final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
- setRegionForMockAccessibilityWindow(firstWindow,
- new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2));
- final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1);
- setRegionForMockAccessibilityWindow(secondWindow,
- new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, hasSize(2));
- final Region outBounds = new Region();
- int windowId = a11yWindows.get(1).getId();
-
- mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
- assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH));
- assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2));
- }
-
- @Test
- public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() {
- // z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible.
- final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
- setRegionForMockAccessibilityWindow(firstWindow,
- new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- // Note that the second window is also exposed even if region is empty because it's focused.
- assertThat(a11yWindows, hasSize(2));
- final Region outBounds = new Region();
- int windowId = a11yWindows.get(1).getId();
-
- mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
- assertTrue(outBounds.getBounds().isEmpty());
- }
-
- @Test
- public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() {
- // Updates z-order #0 WindowInfo to have two interact-able areas.
- final Region region = new Region(0, 0, SCREEN_WIDTH, 200);
- region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION);
- final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
- setRegionForMockAccessibilityWindow(firstWindow, region);
- final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1);
- setRegionForMockAccessibilityWindow(secondWindow,
- new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- final List<AccessibilityWindowInfo> a11yWindows =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- assertThat(a11yWindows, hasSize(2));
- final Region outBounds = new Region();
- final int windowId = a11yWindows.get(1).getId();
-
- mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
- assertFalse(outBounds.getBounds().isEmpty());
- assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH));
- assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT - 400));
- }
-
- @Test
- public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() {
- final IBinder eventWindowToken =
- mWindows.get(Display.DEFAULT_DISPLAY)
- .get(DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().token;
- final int eventWindowId = mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, eventWindowToken);
- when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates())
- .thenReturn(eventWindowToken);
-
- final int noUse = 0;
- mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- eventWindowId,
- noUse,
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
- noUse);
- assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId));
- assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
- is(eventWindowId));
- }
-
- @Test
- public void updateActiveAndA11yFocusedWindow_hoverEvent_touchInteract_shouldSetActiveWindow() {
- final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
- DEFAULT_FOCUSED_INDEX + 1);
- final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
- assertThat(currentActiveWindowId, is(not(eventWindowId)));
-
- final int noUse = 0;
- mA11yWindowManager.onTouchInteractionStart();
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- eventWindowId,
- noUse,
- AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
- noUse);
- assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId));
- final ArgumentCaptor<AccessibilityEvent> captor =
- ArgumentCaptor.forClass(AccessibilityEvent.class);
- verify(mMockA11yEventSender, times(2))
- .sendAccessibilityEventForCurrentUserLocked(captor.capture());
- assertThat(captor.getAllValues().get(0),
- allOf(displayId(Display.DEFAULT_DISPLAY),
- eventWindowId(currentActiveWindowId),
- a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
- assertThat(captor.getAllValues().get(1),
- allOf(displayId(Display.DEFAULT_DISPLAY),
- eventWindowId(eventWindowId),
- a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
- }
-
- @Test
- public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_shouldUpdateA11yFocus() {
- final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
- DEFAULT_FOCUSED_INDEX);
- final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId(
- AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
- assertThat(currentA11yFocusedWindowId, is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID));
-
- final int noUse = 0;
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- eventWindowId,
- AccessibilityNodeInfo.ROOT_NODE_ID,
- AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
- noUse);
- assertThat(mA11yWindowManager.getFocusedWindowId(
- AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId));
- final ArgumentCaptor<AccessibilityEvent> captor =
- ArgumentCaptor.forClass(AccessibilityEvent.class);
- verify(mMockA11yEventSender, times(1))
- .sendAccessibilityEventForCurrentUserLocked(captor.capture());
- assertThat(captor.getAllValues().get(0),
- allOf(displayId(Display.DEFAULT_DISPLAY),
- eventWindowId(eventWindowId),
- a11yWindowChanges(
- AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
- }
-
- @Test
- public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_defaultToSecondary()
- throws RemoteException {
- runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
- Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID);
- }
-
- @Test
- public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_SecondaryToDefault()
- throws RemoteException {
- runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
- SECONDARY_DISPLAY_ID, Display.DEFAULT_DISPLAY);
- }
-
- private void runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
- int initialDisplayId, int eventDisplayId) throws RemoteException {
- startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
- final int initialWindowId = getWindowIdFromWindowInfosForDisplay(
- initialDisplayId, DEFAULT_FOCUSED_INDEX);
- final int noUse = 0;
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- initialWindowId,
- AccessibilityNodeInfo.ROOT_NODE_ID,
- AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
- noUse);
- assertThat(mA11yWindowManager.getFocusedWindowId(
- AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(initialWindowId));
- Mockito.reset(mMockA11yEventSender);
-
- final int eventWindowId = getWindowIdFromWindowInfosForDisplay(
- eventDisplayId, DEFAULT_FOCUSED_INDEX);
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- eventWindowId,
- AccessibilityNodeInfo.ROOT_NODE_ID,
- AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
- noUse);
- assertThat(mA11yWindowManager.getFocusedWindowId(
- AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId));
- final ArgumentCaptor<AccessibilityEvent> captor =
- ArgumentCaptor.forClass(AccessibilityEvent.class);
- verify(mMockA11yEventSender, times(2))
- .sendAccessibilityEventForCurrentUserLocked(captor.capture());
- assertThat(captor.getAllValues().get(0),
- allOf(displayId(initialDisplayId),
- eventWindowId(initialWindowId),
- a11yWindowChanges(
- AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
- assertThat(captor.getAllValues().get(1),
- allOf(displayId(eventDisplayId),
- eventWindowId(eventWindowId),
- a11yWindowChanges(
- AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
- }
-
- @Test
- public void updateActiveAndA11yFocusedWindow_clearA11yFocusEvent_shouldClearA11yFocus() {
- final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
- DEFAULT_FOCUSED_INDEX);
- final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId(
- AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
- assertThat(currentA11yFocusedWindowId, is(not(eventWindowId)));
-
- final int noUse = 0;
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- eventWindowId,
- AccessibilityNodeInfo.ROOT_NODE_ID,
- AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
- noUse);
- assertThat(mA11yWindowManager.getFocusedWindowId(
- AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId));
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- eventWindowId,
- AccessibilityNodeInfo.ROOT_NODE_ID,
- AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
- noUse);
- assertThat(mA11yWindowManager.getFocusedWindowId(
- AccessibilityNodeInfo.FOCUS_ACCESSIBILITY),
- is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID));
- }
-
- @Test
- public void onTouchInteractionEnd_shouldRollbackActiveWindow() {
- final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
- DEFAULT_FOCUSED_INDEX + 1);
- final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
- assertThat(currentActiveWindowId, is(not(eventWindowId)));
-
- final int noUse = 0;
- mA11yWindowManager.onTouchInteractionStart();
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- eventWindowId,
- noUse,
- AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
- noUse);
- assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId));
- // AccessibilityEventSender is invoked after active window changed. Reset it.
- Mockito.reset(mMockA11yEventSender);
-
- mA11yWindowManager.onTouchInteractionEnd();
- final ArgumentCaptor<AccessibilityEvent> captor =
- ArgumentCaptor.forClass(AccessibilityEvent.class);
- verify(mMockA11yEventSender, times(2))
- .sendAccessibilityEventForCurrentUserLocked(captor.capture());
- assertThat(captor.getAllValues().get(0),
- allOf(displayId(Display.DEFAULT_DISPLAY),
- eventWindowId(eventWindowId),
- a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
- assertThat(captor.getAllValues().get(1),
- allOf(displayId(Display.DEFAULT_DISPLAY),
- eventWindowId(currentActiveWindowId),
- a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
- }
-
- @Test
- public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus()
- throws RemoteException {
- final IBinder defaultFocusWinToken =
- mWindows.get(Display.DEFAULT_DISPLAY).get(
- DEFAULT_FOCUSED_INDEX).getWindowInfo().token;
- final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, defaultFocusWinToken);
- when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates())
- .thenReturn(defaultFocusWinToken);
- final int newFocusWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY,
- DEFAULT_FOCUSED_INDEX + 1);
- final IAccessibilityInteractionConnection mockNewFocusConnection =
- mA11yWindowManager.getConnectionLocked(
- USER_SYSTEM_ID, newFocusWindowId).getRemote();
-
- mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
- final int noUse = 0;
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- defaultFocusWindowId,
- noUse,
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
- noUse);
- assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(defaultFocusWindowId));
- assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
- is(defaultFocusWindowId));
-
- mA11yWindowManager.onTouchInteractionStart();
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- newFocusWindowId,
- noUse,
- AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
- noUse);
- mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
- newFocusWindowId,
- AccessibilityNodeInfo.ROOT_NODE_ID,
- AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
- noUse);
- assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(newFocusWindowId));
- assertThat(mA11yWindowManager.getFocusedWindowId(
- AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(newFocusWindowId));
-
- mA11yWindowManager.onTouchInteractionEnd();
- mHandler.sendLastMessage();
- verify(mockNewFocusConnection).clearAccessibilityFocus();
- }
-
- @Test
- public void getPictureInPictureWindow_shouldNotNull() {
- assertNull(mA11yWindowManager.getPictureInPictureWindowLocked());
- mWindows.get(Display.DEFAULT_DISPLAY).get(1).getWindowInfo().inPictureInPicture = true;
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked());
- }
-
- @Test
- public void notifyOutsideTouch() throws RemoteException {
- final int targetWindowId =
- getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 1);
- final int outsideWindowId =
- getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
- final IAccessibilityInteractionConnection mockRemoteConnection =
- mA11yWindowManager.getConnectionLocked(
- USER_SYSTEM_ID, outsideWindowId).getRemote();
- mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().hasFlagWatchOutsideTouch =
- true;
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-
- mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId);
- verify(mockRemoteConnection).notifyOutsideTouch();
- }
-
- @Test
- public void addAccessibilityInteractionConnection_profileUser_findInParentUser()
- throws RemoteException {
- final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- false, USER_PROFILE);
- final int windowId = mA11yWindowManager.findWindowIdLocked(
- USER_PROFILE_PARENT, token.asBinder());
- assertTrue(windowId >= 0);
- }
-
- @Test
- public void getDisplayList() throws RemoteException {
- // Starts tracking window of second display.
- startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
-
- final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked(
- DISPLAY_TYPE_DEFAULT);
- assertTrue(displayList.equals(mExpectedDisplayList));
- }
-
- @Test
- public void setAccessibilityWindowIdToSurfaceMetadata()
- throws RemoteException {
- final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- true, USER_SYSTEM_ID);
- int windowId = -1;
- for (int i = 0; i < mA11yWindowTokens.size(); i++) {
- if (mA11yWindowTokens.valueAt(i).equals(token)) {
- windowId = mA11yWindowTokens.keyAt(i);
- }
- }
- assertNotEquals("Returned token is not found in mA11yWindowTokens", -1, windowId);
- verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata(
- token.asBinder(), windowId);
-
- mA11yWindowManager.removeAccessibilityInteractionConnection(token);
- verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata(
- token.asBinder(), -1);
- }
-
- @Test
- public void getHostTokenLocked_hierarchiesAreAssociated_shouldReturnHostToken() {
- mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
- final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
- assertEquals(hostToken, mMockHostToken);
- }
-
- @Test
- public void getHostTokenLocked_hierarchiesAreNotAssociated_shouldReturnNull() {
- final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
- assertNull(hostToken);
- }
-
- @Test
- public void getHostTokenLocked_embeddedHierarchiesAreDisassociated_shouldReturnNull() {
- mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
- mA11yWindowManager.disassociateLocked(mMockEmbeddedToken);
- final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
- assertNull(hostToken);
- }
-
- @Test
- public void getHostTokenLocked_hostHierarchiesAreDisassociated_shouldReturnNull() {
- mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
- mA11yWindowManager.disassociateLocked(mMockHostToken);
- final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockHostToken);
- assertNull(hostToken);
- }
-
- @Test
- public void getWindowIdLocked_windowIsRegistered_shouldReturnWindowId() {
- final int windowId = mA11yWindowManager.getWindowIdLocked(mMockHostToken);
- assertEquals(windowId, HOST_WINDOW_ID);
- }
-
- @Test
- public void getWindowIdLocked_windowIsNotRegistered_shouldReturnInvalidWindowId() {
- final int windowId = mA11yWindowManager.getWindowIdLocked(mMockInvalidToken);
- assertEquals(windowId, INVALID_ID);
- }
-
- @Test
- public void getTokenLocked_windowIsRegistered_shouldReturnToken() {
- final IBinder token = mA11yWindowManager.getLeashTokenLocked(HOST_WINDOW_ID);
- assertEquals(token, mMockHostToken);
- }
-
- @Test
- public void getTokenLocked_windowIsNotRegistered_shouldReturnNull() {
- final IBinder token = mA11yWindowManager.getLeashTokenLocked(OTHER_WINDOW_ID);
- assertNull(token);
- }
-
- @Test
- public void setAccessibilityWindowAttributes_windowIsNotRegistered_titleIsChanged() {
- final int windowId =
- getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
- final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
- layoutParams.accessibilityTitle = "accessibility window title";
- final AccessibilityWindowAttributes attributes = new AccessibilityWindowAttributes(
- layoutParams, new LocaleList());
-
- mA11yWindowManager.setAccessibilityWindowAttributes(Display.DEFAULT_DISPLAY, windowId,
- USER_SYSTEM_ID, attributes);
-
- final AccessibilityWindowInfo a11yWindow = mA11yWindowManager.findA11yWindowInfoByIdLocked(
- windowId);
- assertEquals(toString(layoutParams.accessibilityTitle), toString(a11yWindow.getTitle()));
- }
-
- @Test
- public void sendAccessibilityEventOnWindowRemoval() {
- final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY);
-
- // Removing index 0 because it's not focused, and avoids unnecessary layer change.
- final int windowId =
- getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
- windows.remove(0);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
-
- final ArgumentCaptor<AccessibilityEvent> captor =
- ArgumentCaptor.forClass(AccessibilityEvent.class);
- verify(mMockA11yEventSender, times(1))
- .sendAccessibilityEventForCurrentUserLocked(captor.capture());
- assertThat(captor.getAllValues().get(0),
- allOf(displayId(Display.DEFAULT_DISPLAY),
- eventWindowId(windowId),
- a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED)));
- }
-
- @Test
- public void sendAccessibilityEventOnWindowAddition() throws RemoteException {
- final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY);
-
- final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
- false, USER_SYSTEM_ID);
- // Adding window to the front so that other windows' layer won't change.
- windows.add(0, createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY));
- final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
-
- final ArgumentCaptor<AccessibilityEvent> captor =
- ArgumentCaptor.forClass(AccessibilityEvent.class);
- verify(mMockA11yEventSender, times(1))
- .sendAccessibilityEventForCurrentUserLocked(captor.capture());
- assertThat(captor.getAllValues().get(0),
- allOf(displayId(Display.DEFAULT_DISPLAY),
- eventWindowId(windowId),
- a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED)));
- }
-
- @Test
- public void sendAccessibilityEventOnWindowChange() {
- final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY);
- windows.get(0).getWindowInfo().title = "new title";
- final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
-
- onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
-
- final ArgumentCaptor<AccessibilityEvent> captor =
- ArgumentCaptor.forClass(AccessibilityEvent.class);
- verify(mMockA11yEventSender, times(1))
- .sendAccessibilityEventForCurrentUserLocked(captor.capture());
- assertThat(captor.getAllValues().get(0),
- allOf(displayId(Display.DEFAULT_DISPLAY),
- eventWindowId(windowId),
- a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE)));
- }
-
- private void registerLeashedTokenAndWindowId() {
- mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
- mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);
- }
-
- private void startTrackingPerDisplay(int displayId) throws RemoteException {
- ArrayList<AccessibilityWindow> windowsForDisplay = new ArrayList<>();
- // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
- // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos
- // for the test.
- for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) {
- final IWindow token = addAccessibilityInteractionConnection(displayId,
- true, USER_SYSTEM_ID);
- windowsForDisplay.add(createMockAccessibilityWindow(token, displayId));
-
- }
- for (int i = 0; i < NUM_APP_WINDOWS; i++) {
- final IWindow token = addAccessibilityInteractionConnection(displayId,
- false, USER_SYSTEM_ID);
- windowsForDisplay.add(createMockAccessibilityWindow(token, displayId));
- }
- // Sets up current focused window of display.
- // Each display has its own current focused window if config_perDisplayFocusEnabled is true.
- // Otherwise only default display needs to current focused window.
- if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) {
- windowsForDisplay.get(DEFAULT_FOCUSED_INDEX).getWindowInfo().focused = true;
- }
- // Turns on windows tracking, and update window info.
- mA11yWindowManager.startTrackingWindows(displayId, false);
- // Puts window lists into array.
- mWindows.put(displayId, windowsForDisplay);
- // Sets the default display is the top focused display and
- // its current focused window is the top focused window.
- if (displayId == Display.DEFAULT_DISPLAY) {
- setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX);
- }
- // Invokes callback for sending window lists to A11y framework.
- onAccessibilityWindowsChanged(displayId, FORCE_SEND);
-
- assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(),
- windowsForDisplay.size());
- }
-
- private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) {
- ArgumentCaptor<WindowsForAccessibilityCallback> windowsForAccessibilityCallbacksCaptor =
- ArgumentCaptor.forClass(
- WindowsForAccessibilityCallback.class);
- verify(mMockWindowManagerInternal)
- .setWindowsForAccessibilityCallback(eq(displayId),
- windowsForAccessibilityCallbacksCaptor.capture());
- return windowsForAccessibilityCallbacksCaptor.getValue();
- }
-
- private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal,
- int userId) throws RemoteException {
- final IWindow mockWindowToken = Mockito.mock(IWindow.class);
- final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
- IAccessibilityInteractionConnection.class);
- final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
- final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
- final IBinder mockLeashToken = Mockito.mock(IBinder.class);
- when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
- when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
- when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
- .thenReturn(bGlobal);
- when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
- .thenReturn(displayId);
-
- int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
- mockWindowToken, mockLeashToken, mockA11yConnection, PACKAGE_NAME, userId);
- mA11yWindowTokens.put(windowId, mockWindowToken);
- return mockWindowToken;
- }
-
- private int addAccessibilityInteractionConnection(int displayId, boolean bGlobal,
- IBinder leashToken, int userId) throws RemoteException {
- final IWindow mockWindowToken = Mockito.mock(IWindow.class);
- final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
- IAccessibilityInteractionConnection.class);
- final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
- final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
- when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
- when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
- when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
- .thenReturn(bGlobal);
- when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
- .thenReturn(displayId);
-
- int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
- mockWindowToken, leashToken, mockA11yConnection, PACKAGE_NAME, userId);
- mA11yWindowTokens.put(windowId, mockWindowToken);
- return windowId;
- }
-
- private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) {
- final IBinder windowToken = mWindows.get(displayId).get(index).getWindowInfo().token;
- return mA11yWindowManager.findWindowIdLocked(
- USER_SYSTEM_ID, windowToken);
- }
-
- private void setTopFocusedWindowAndDisplay(int displayId, int index) {
- // Sets the top focus window.
- mTopFocusedWindowToken = mWindows.get(displayId).get(index).getWindowInfo().token;
- // Sets the top focused display.
- mTopFocusedDisplayId = displayId;
- }
-
- private void onAccessibilityWindowsChanged(int displayId, boolean forceSend) {
- WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId);
- if (callbacks == null) {
- callbacks = getWindowsForAccessibilityCallbacks(displayId);
- mCallbackOfWindows.put(displayId, callbacks);
- }
- callbacks.onAccessibilityWindowsChanged(forceSend, mTopFocusedDisplayId,
- mTopFocusedWindowToken, new Point(SCREEN_WIDTH, SCREEN_HEIGHT),
- mWindows.get(displayId));
- }
-
- private void changeFocusedWindowOnDisplayPerDisplayFocusConfig(
- int changeFocusedDisplayId, int newFocusedWindowIndex, int oldTopFocusedDisplayId,
- int oldFocusedWindowIndex) {
- if (mSupportPerDisplayFocus) {
- // Gets the old focused window of display which wants to change focused window.
- WindowInfo focusedWindowInfo =
- mWindows.get(changeFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo();
- // Resets the focus of old focused window.
- focusedWindowInfo.focused = false;
- // Gets the new window of display which wants to change focused window.
- focusedWindowInfo =
- mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo();
- // Sets the focus of new focused window.
- focusedWindowInfo.focused = true;
- } else {
- // Gets the window of display which wants to change focused window.
- WindowInfo focusedWindowInfo =
- mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo();
- // Sets the focus of new focused window.
- focusedWindowInfo.focused = true;
- // Gets the old focused window of old top focused display.
- focusedWindowInfo =
- mWindows.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo();
- // Resets the focus of old focused window.
- focusedWindowInfo.focused = false;
- // Changes the top focused display and window.
- setTopFocusedWindowAndDisplay(changeFocusedDisplayId, newFocusedWindowIndex);
- }
- }
-
- private AccessibilityWindow createMockAccessibilityWindow(IWindow windowToken, int displayId) {
- final WindowInfo windowInfo = WindowInfo.obtain();
- windowInfo.type = WindowManager.LayoutParams.TYPE_APPLICATION;
- windowInfo.token = windowToken.asBinder();
-
- final AccessibilityWindow window = Mockito.mock(AccessibilityWindow.class);
- when(window.getWindowInfo()).thenReturn(windowInfo);
- when(window.isFocused()).thenAnswer(invocation -> windowInfo.focused);
- when(window.isTouchable()).thenReturn(true);
- when(window.getType()).thenReturn(windowInfo.type);
-
- setRegionForMockAccessibilityWindow(window, nextToucableRegion(displayId));
- return window;
- }
-
- private void setRegionForMockAccessibilityWindow(AccessibilityWindow window, Region region) {
- doAnswer(invocation -> {
- ((Region) invocation.getArgument(0)).set(region);
- return null;
- }).when(window).getTouchableRegionInScreen(any(Region.class));
- doAnswer(invocation -> {
- ((Region) invocation.getArgument(0)).set(region);
- return null;
- }).when(window).getTouchableRegionInWindow(any(Region.class));
- }
-
- private Region nextToucableRegion(int displayId) {
- final int topLeft = mNextRegionOffsets.get(displayId, 0);
- final int bottomRight = topLeft + 100;
- mNextRegionOffsets.put(displayId, topLeft + 10);
- return new Region(topLeft, topLeft, bottomRight, bottomRight);
- }
-
- @Nullable
- private static String toString(@Nullable CharSequence cs) {
- return cs == null ? null : cs.toString();
- }
-
- static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
- private final int mDisplayId;
-
- DisplayIdMatcher(int displayId) {
- super();
- mDisplayId = displayId;
- }
-
- static DisplayIdMatcher displayId(int displayId) {
- return new DisplayIdMatcher(displayId);
- }
-
- @Override
- protected boolean matchesSafely(AccessibilityEvent event) {
- return event.getDisplayId() == mDisplayId;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("Matching to displayId " + mDisplayId);
- }
- }
-
- static class EventWindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
- private int mWindowId;
-
- EventWindowIdMatcher(int windowId) {
- super();
- mWindowId = windowId;
- }
-
- static EventWindowIdMatcher eventWindowId(int windowId) {
- return new EventWindowIdMatcher(windowId);
- }
-
- @Override
- protected boolean matchesSafely(AccessibilityEvent event) {
- return event.getWindowId() == mWindowId;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("Matching to windowId " + mWindowId);
- }
- }
-
- static class WindowChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> {
- private int mWindowChanges;
-
- WindowChangesMatcher(int windowChanges) {
- super();
- mWindowChanges = windowChanges;
- }
-
- static WindowChangesMatcher a11yWindowChanges(int windowChanges) {
- return new WindowChangesMatcher(windowChanges);
- }
-
- @Override
- protected boolean matchesSafely(AccessibilityEvent event) {
- return event.getWindowChanges() == mWindowChanges;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("Matching to window changes " + mWindowChanges);
- }
- }
-
- static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityWindowInfo> {
- private final int mWindowId;
-
- WindowIdMatcher(int windowId) {
- super();
- mWindowId = windowId;
- }
-
- static WindowIdMatcher windowId(int windowId) {
- return new WindowIdMatcher(windowId);
- }
-
- @Override
- protected boolean matchesSafely(AccessibilityWindowInfo window) {
- return window.getId() == mWindowId;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("Matching to windowId " + mWindowId);
- }
- }
-}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e22d75e..ad5d42a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -19599,4 +19599,37 @@
throw ex.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Returns carrier id maps to the passing CarrierIdentifier.
+ * To recognize a carrier (including MVNO) as a first-class identity,
+ * Android assigns each carrier with a canonical integer a.k.a. carrier id.
+ * The carrier ID is an Android platform-wide identifier for a carrier.
+ * AOSP maintains carrier ID assignments in
+ * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
+ *
+ * @param carrierIdentifier {@link CarrierIdentifier}
+ *
+ * @return Carrier id. Return {@link #UNKNOWN_CARRIER_ID} if the carrier cannot be identified.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ID_FROM_CARRIER_IDENTIFIER)
+ @SystemApi
+ @WorkerThread
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getCarrierIdFromCarrierIdentifier(@NonNull CarrierIdentifier carrierIdentifier) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getCarrierIdFromIdentifier(carrierIdentifier);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return UNKNOWN_CARRIER_ID;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d22e9fa..294c93c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3519,4 +3519,15 @@
* @hide
*/
void setNtnSmsSupported(boolean ntnSmsSupported);
+
+ /**
+ * Returns carrier id maps to the passing {@link CarrierIdentifier}.
+ *
+ * @param {@link CarrierIdentifier}.
+ *
+ * @return carrier id from passing {@link CarrierIdentifier} or {@link #UNKNOWN_CARRIER_ID}
+ * if the carrier cannot be identified
+ * @hide
+ */
+ int getCarrierIdFromIdentifier(in CarrierIdentifier carrierIdentifier);
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 6432827..e0900a6 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -23,6 +23,7 @@
import android.graphics.Region
import android.os.SystemClock
import android.platform.uiautomatorhelpers.DeviceHelpers
+import android.tools.PlatformConsts
import android.tools.device.apphelpers.IStandardAppHelper
import android.tools.helpers.SYSTEMUI_PACKAGE
import android.tools.traces.parsers.WindowManagerStateHelper
@@ -163,7 +164,10 @@
.StateSyncBuilder()
.withAppTransitionIdle()
.apply {
- if (isPip) withPipShown() else withWindowSurfaceDisappeared(innerHelper)
+ if (isPip) withPipShown()
+ else
+ withWindowSurfaceDisappeared(innerHelper)
+ .withActivityState(innerHelper, PlatformConsts.STATE_STOPPED)
}
.waitForAndVerify()
}
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index fe974e3..af87bf7 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -1014,7 +1014,7 @@
triggerFailureCount = 1;
}
for (int i = 0; i < triggerFailureCount; i++) {
- watchdog.onPackageFailure(packages, failureReason);
+ watchdog.notifyPackageFailure(packages, failureReason);
}
mTestLooper.dispatchAll();
if (Flags.recoverabilityDetection()) {
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index c25bed2..5a8a6be 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -392,7 +392,7 @@
// Then fail APP_A below the threshold
for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) {
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
}
@@ -1025,14 +1025,14 @@
watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Fail APP_A below the threshold which should not trigger package failures
for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
}
mTestLooper.dispatchAll();
assertThat(observer.mHealthCheckFailedPackages).isEmpty();
// One more to trigger the package failure
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
mTestLooper.dispatchAll();
assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
@@ -1051,10 +1051,10 @@
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE);
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1);
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
mTestLooper.dispatchAll();
@@ -1062,10 +1062,10 @@
// DEFAULT_TRIGGER_FAILURE_DURATION_MS.
assertThat(observer.mHealthCheckFailedPackages).isEmpty();
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS - 1);
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
mTestLooper.dispatchAll();
@@ -1129,17 +1129,17 @@
watchdog.startObservingHealth(observer, Arrays.asList(APP_A), Long.MAX_VALUE);
// Raise 2 failures at t=0 and t=900 respectively
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
moveTimeForwardAndDispatch(900);
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
// Raise 2 failures at t=1100
moveTimeForwardAndDispatch(200);
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
- watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
mTestLooper.dispatchAll();
@@ -1433,13 +1433,13 @@
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
watchdog.startObservingHealth(observer, List.of(APP_A), SHORT_DURATION);
for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
- watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
}
mTestLooper.dispatchAll();
assertThat(observer.mMitigatedPackages).isEmpty();
watchdog.startObservingHealth(observer, List.of(APP_A), LONG_DURATION);
- watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
+ watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_UNKNOWN);
mTestLooper.dispatchAll();
assertThat(observer.mMitigatedPackages).isEqualTo(List.of(APP_A));
@@ -1737,7 +1737,7 @@
triggerFailureCount = 1;
}
for (int i = 0; i < triggerFailureCount; i++) {
- watchdog.onPackageFailure(packages, failureReason);
+ watchdog.notifyPackageFailure(packages, failureReason);
}
mTestLooper.dispatchAll();
if (Flags.recoverabilityDetection()) {