summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt35
-rw-r--r--api/system-current.txt35
-rw-r--r--api/test-current.txt35
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java2
-rw-r--r--core/java/android/app/UiAutomation.java2
-rw-r--r--core/java/android/bluetooth/le/ScanFilter.java12
-rw-r--r--core/java/android/companion/AssociationRequest.java107
-rw-r--r--core/java/android/companion/BluetoothDeviceFilter.java28
-rw-r--r--core/java/android/companion/BluetoothDeviceFilterUtils.java41
-rw-r--r--core/java/android/companion/BluetoothLEDeviceFilter.java191
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java2
-rw-r--r--core/java/android/companion/DeviceFilter.java24
-rw-r--r--core/java/android/companion/WifiDeviceFilter.java125
-rw-r--r--core/java/android/os/Parcel.java15
-rw-r--r--core/java/android/text/TextUtils.java5
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java73
-rw-r--r--core/java/android/view/View.java5
-rw-r--r--core/java/android/view/ViewRootImpl.java22
-rw-r--r--core/java/android/view/WindowInfo.java5
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java2
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java26
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java124
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java29
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java67
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl5
-rw-r--r--core/java/com/android/internal/policy/DecorView.java6
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java29
-rw-r--r--core/java/com/android/internal/util/BitUtils.java58
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp4
-rw-r--r--core/jni/com_google_android_gles_jni_EGLImpl.cpp5
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/values/ids.xml3
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--packages/CompanionDeviceManager/AndroidManifest.xml2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java16
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java197
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java194
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java131
-rw-r--r--services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java229
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java36
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java16
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java92
-rw-r--r--services/core/java/com/android/server/am/TaskPersister.java6
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java32
-rw-r--r--services/core/java/com/android/server/wm/StackWindowController.java20
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java58
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/print/java/com/android/server/print/CompanionDeviceManagerService.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java279
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java2
57 files changed, 2007 insertions, 494 deletions
diff --git a/api/current.txt b/api/current.txt
index 0925dad6d95c..4c900b7cdc18 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1779,6 +1779,7 @@ package android {
public static final class R.id {
ctor public R.id();
field public static final int accessibilityActionContextClick = 16908348; // 0x102003c
+ field public static final int accessibilityActionMoveWindow = 16908354; // 0x1020042
field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a
field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039
field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b
@@ -8187,18 +8188,17 @@ package android.bluetooth.le {
package android.companion {
- public final class AssociationRequest<F extends android.companion.DeviceFilter> implements android.os.Parcelable {
+ public final class AssociationRequest implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.companion.AssociationRequest> CREATOR;
}
- public static final class AssociationRequest.Builder<F extends android.companion.DeviceFilter> {
- method public android.companion.AssociationRequest<F> build();
- method public static android.companion.AssociationRequest.Builder<android.companion.BluetoothDeviceFilter> createForBluetoothDevice();
- method public static android.companion.AssociationRequest.Builder<android.companion.BluetoothLEDeviceFilter> createForBluetoothLEDevice();
- method public android.companion.AssociationRequest.Builder<F> setDeviceFilter(F);
- method public android.companion.AssociationRequest.Builder<F> setSingleDevice(boolean);
+ public static final class AssociationRequest.Builder {
+ ctor public AssociationRequest.Builder();
+ method public android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>);
+ method public android.companion.AssociationRequest build();
+ method public android.companion.AssociationRequest.Builder setSingleDevice(boolean);
}
public final class BluetoothDeviceFilter implements android.companion.DeviceFilter {
@@ -8217,6 +8217,7 @@ package android.companion {
public final class BluetoothLEDeviceFilter implements android.companion.DeviceFilter {
method public int describeContents();
+ method public static int getRenamePrefixLengthLimit();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.companion.BluetoothLEDeviceFilter> CREATOR;
}
@@ -8225,11 +8226,13 @@ package android.companion {
ctor public BluetoothLEDeviceFilter.Builder();
method public android.companion.BluetoothLEDeviceFilter build();
method public android.companion.BluetoothLEDeviceFilter.Builder setNamePattern(java.util.regex.Pattern);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRawDataFilter(byte[], byte[]);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRename(java.lang.String, java.lang.String, int, int, boolean);
method public android.companion.BluetoothLEDeviceFilter.Builder setScanFilter(android.bluetooth.le.ScanFilter);
}
public final class CompanionDeviceManager {
- method public void associate(android.companion.AssociationRequest<?>, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
+ method public void associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
method public void disassociate(java.lang.String);
method public java.util.List<java.lang.String> getAssociations();
field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE";
@@ -8244,6 +8247,18 @@ package android.companion {
public abstract interface DeviceFilter<D extends android.os.Parcelable> implements android.os.Parcelable {
}
+ public final class WifiDeviceFilter implements android.companion.DeviceFilter {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.companion.WifiDeviceFilter> CREATOR;
+ }
+
+ public static final class WifiDeviceFilter.Builder {
+ ctor public WifiDeviceFilter.Builder();
+ method public android.companion.WifiDeviceFilter build();
+ method public android.companion.WifiDeviceFilter.Builder setNamePattern(java.util.regex.Pattern);
+ }
+
}
package android.content {
@@ -46949,6 +46964,8 @@ package android.view.accessibility {
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+ field public static final java.lang.String ACTION_ARGUMENT_MOVE_WINDOW_X = "android.view.accessibility.action.ARGUMENT_MOVE_WINDOW_X";
+ field public static final java.lang.String ACTION_ARGUMENT_MOVE_WINDOW_Y = "android.view.accessibility.action.ARGUMENT_MOVE_WINDOW_Y";
field public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
@@ -47005,6 +47022,7 @@ package android.view.accessibility {
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_LONG_CLICK;
+ field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_MOVE_WINDOW;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_HTML_ELEMENT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PASTE;
@@ -47129,6 +47147,7 @@ package android.view.accessibility {
method public android.view.accessibility.AccessibilityNodeInfo getRoot();
method public java.lang.CharSequence getTitle();
method public int getType();
+ method public boolean inPictureInPicture();
method public boolean isAccessibilityFocused();
method public boolean isActive();
method public boolean isFocused();
diff --git a/api/system-current.txt b/api/system-current.txt
index b1c55efe15c9..959adf580c63 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1896,6 +1896,7 @@ package android {
public static final class R.id {
ctor public R.id();
field public static final int accessibilityActionContextClick = 16908348; // 0x102003c
+ field public static final int accessibilityActionMoveWindow = 16908354; // 0x1020042
field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a
field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039
field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b
@@ -8688,18 +8689,17 @@ package android.bluetooth.le {
package android.companion {
- public final class AssociationRequest<F extends android.companion.DeviceFilter> implements android.os.Parcelable {
+ public final class AssociationRequest implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.companion.AssociationRequest> CREATOR;
}
- public static final class AssociationRequest.Builder<F extends android.companion.DeviceFilter> {
- method public android.companion.AssociationRequest<F> build();
- method public static android.companion.AssociationRequest.Builder<android.companion.BluetoothDeviceFilter> createForBluetoothDevice();
- method public static android.companion.AssociationRequest.Builder<android.companion.BluetoothLEDeviceFilter> createForBluetoothLEDevice();
- method public android.companion.AssociationRequest.Builder<F> setDeviceFilter(F);
- method public android.companion.AssociationRequest.Builder<F> setSingleDevice(boolean);
+ public static final class AssociationRequest.Builder {
+ ctor public AssociationRequest.Builder();
+ method public android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>);
+ method public android.companion.AssociationRequest build();
+ method public android.companion.AssociationRequest.Builder setSingleDevice(boolean);
}
public final class BluetoothDeviceFilter implements android.companion.DeviceFilter {
@@ -8718,6 +8718,7 @@ package android.companion {
public final class BluetoothLEDeviceFilter implements android.companion.DeviceFilter {
method public int describeContents();
+ method public static int getRenamePrefixLengthLimit();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.companion.BluetoothLEDeviceFilter> CREATOR;
}
@@ -8726,11 +8727,13 @@ package android.companion {
ctor public BluetoothLEDeviceFilter.Builder();
method public android.companion.BluetoothLEDeviceFilter build();
method public android.companion.BluetoothLEDeviceFilter.Builder setNamePattern(java.util.regex.Pattern);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRawDataFilter(byte[], byte[]);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRename(java.lang.String, java.lang.String, int, int, boolean);
method public android.companion.BluetoothLEDeviceFilter.Builder setScanFilter(android.bluetooth.le.ScanFilter);
}
public final class CompanionDeviceManager {
- method public void associate(android.companion.AssociationRequest<?>, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
+ method public void associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
method public void disassociate(java.lang.String);
method public java.util.List<java.lang.String> getAssociations();
field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE";
@@ -8745,6 +8748,18 @@ package android.companion {
public abstract interface DeviceFilter<D extends android.os.Parcelable> implements android.os.Parcelable {
}
+ public final class WifiDeviceFilter implements android.companion.DeviceFilter {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.companion.WifiDeviceFilter> CREATOR;
+ }
+
+ public static final class WifiDeviceFilter.Builder {
+ ctor public WifiDeviceFilter.Builder();
+ method public android.companion.WifiDeviceFilter build();
+ method public android.companion.WifiDeviceFilter.Builder setNamePattern(java.util.regex.Pattern);
+ }
+
}
package android.content {
@@ -50412,6 +50427,8 @@ package android.view.accessibility {
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+ field public static final java.lang.String ACTION_ARGUMENT_MOVE_WINDOW_X = "android.view.accessibility.action.ARGUMENT_MOVE_WINDOW_X";
+ field public static final java.lang.String ACTION_ARGUMENT_MOVE_WINDOW_Y = "android.view.accessibility.action.ARGUMENT_MOVE_WINDOW_Y";
field public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
@@ -50468,6 +50485,7 @@ package android.view.accessibility {
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_LONG_CLICK;
+ field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_MOVE_WINDOW;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_HTML_ELEMENT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PASTE;
@@ -50592,6 +50610,7 @@ package android.view.accessibility {
method public android.view.accessibility.AccessibilityNodeInfo getRoot();
method public java.lang.CharSequence getTitle();
method public int getType();
+ method public boolean inPictureInPicture();
method public boolean isAccessibilityFocused();
method public boolean isActive();
method public boolean isFocused();
diff --git a/api/test-current.txt b/api/test-current.txt
index ffe8346f644a..80b63794136c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1779,6 +1779,7 @@ package android {
public static final class R.id {
ctor public R.id();
field public static final int accessibilityActionContextClick = 16908348; // 0x102003c
+ field public static final int accessibilityActionMoveWindow = 16908354; // 0x1020042
field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a
field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039
field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b
@@ -8214,18 +8215,17 @@ package android.bluetooth.le {
package android.companion {
- public final class AssociationRequest<F extends android.companion.DeviceFilter> implements android.os.Parcelable {
+ public final class AssociationRequest implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.companion.AssociationRequest> CREATOR;
}
- public static final class AssociationRequest.Builder<F extends android.companion.DeviceFilter> {
- method public android.companion.AssociationRequest<F> build();
- method public static android.companion.AssociationRequest.Builder<android.companion.BluetoothDeviceFilter> createForBluetoothDevice();
- method public static android.companion.AssociationRequest.Builder<android.companion.BluetoothLEDeviceFilter> createForBluetoothLEDevice();
- method public android.companion.AssociationRequest.Builder<F> setDeviceFilter(F);
- method public android.companion.AssociationRequest.Builder<F> setSingleDevice(boolean);
+ public static final class AssociationRequest.Builder {
+ ctor public AssociationRequest.Builder();
+ method public android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>);
+ method public android.companion.AssociationRequest build();
+ method public android.companion.AssociationRequest.Builder setSingleDevice(boolean);
}
public final class BluetoothDeviceFilter implements android.companion.DeviceFilter {
@@ -8244,6 +8244,7 @@ package android.companion {
public final class BluetoothLEDeviceFilter implements android.companion.DeviceFilter {
method public int describeContents();
+ method public static int getRenamePrefixLengthLimit();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.companion.BluetoothLEDeviceFilter> CREATOR;
}
@@ -8252,11 +8253,13 @@ package android.companion {
ctor public BluetoothLEDeviceFilter.Builder();
method public android.companion.BluetoothLEDeviceFilter build();
method public android.companion.BluetoothLEDeviceFilter.Builder setNamePattern(java.util.regex.Pattern);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRawDataFilter(byte[], byte[]);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRename(java.lang.String, java.lang.String, int, int, boolean);
method public android.companion.BluetoothLEDeviceFilter.Builder setScanFilter(android.bluetooth.le.ScanFilter);
}
public final class CompanionDeviceManager {
- method public void associate(android.companion.AssociationRequest<?>, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
+ method public void associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
method public void disassociate(java.lang.String);
method public java.util.List<java.lang.String> getAssociations();
field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE";
@@ -8271,6 +8274,18 @@ package android.companion {
public abstract interface DeviceFilter<D extends android.os.Parcelable> implements android.os.Parcelable {
}
+ public final class WifiDeviceFilter implements android.companion.DeviceFilter {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.companion.WifiDeviceFilter> CREATOR;
+ }
+
+ public static final class WifiDeviceFilter.Builder {
+ ctor public WifiDeviceFilter.Builder();
+ method public android.companion.WifiDeviceFilter build();
+ method public android.companion.WifiDeviceFilter.Builder setNamePattern(java.util.regex.Pattern);
+ }
+
}
package android.content {
@@ -47317,6 +47332,8 @@ package android.view.accessibility {
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+ field public static final java.lang.String ACTION_ARGUMENT_MOVE_WINDOW_X = "android.view.accessibility.action.ARGUMENT_MOVE_WINDOW_X";
+ field public static final java.lang.String ACTION_ARGUMENT_MOVE_WINDOW_Y = "android.view.accessibility.action.ARGUMENT_MOVE_WINDOW_Y";
field public static final java.lang.String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
@@ -47373,6 +47390,7 @@ package android.view.accessibility {
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_LONG_CLICK;
+ field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_MOVE_WINDOW;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_HTML_ELEMENT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PASTE;
@@ -47497,6 +47515,7 @@ package android.view.accessibility {
method public android.view.accessibility.AccessibilityNodeInfo getRoot();
method public java.lang.CharSequence getTitle();
method public int getType();
+ method public boolean inPictureInPicture();
method public boolean isAccessibilityFocused();
method public boolean isActive();
method public boolean isFocused();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index b4e119e52fb7..64d7d4c1be97 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -1438,7 +1438,7 @@ public abstract class AccessibilityService extends Service {
*/
public AccessibilityNodeInfo findFocus(int focus) {
return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
- AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
+ AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
}
/**
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 1d6f42ecdb0b..18e7599e1846 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -381,7 +381,7 @@ public final class UiAutomation {
*/
public AccessibilityNodeInfo findFocus(int focus) {
return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
- AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
+ AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
}
/**
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index b89c64a8cac6..457096bf843e 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -23,6 +23,8 @@ import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
+import com.android.internal.util.BitUtils;
+
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -345,15 +347,7 @@ public final class ScanFilter implements Parcelable {
// Check if the uuid pattern matches the particular service uuid.
private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) {
- if (mask == null) {
- return uuid.equals(data);
- }
- if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) !=
- (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) {
- return false;
- }
- return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) ==
- (data.getMostSignificantBits() & mask.getMostSignificantBits()));
+ return BitUtils.maskedEquals(data, uuid, mask);
}
// Check whether the data pattern matches the parsed data.
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index d477f43ac8c2..56f5d4483270 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -16,20 +16,21 @@
package android.companion;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.OneTimeUseBuilder;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* A request for the user to select a companion device to associate with.
*
- * You can optionally set a {@link Builder#setDeviceFilter filter} for which devices to show to the
+ * You can optionally set {@link Builder#addDeviceFilter filters} for which devices to show to the
* user to select from.
* The exact type and fields of the filter you can set depend on the
* medium type. See {@link Builder}'s static factory methods for specific protocols that are
@@ -37,38 +38,22 @@ import java.lang.annotation.RetentionPolicy;
*
* You can also set {@link Builder#setSingleDevice single device} to request a popup with single
* device to be shown instead of a list to choose from
- *
- * @param <F> Device filter type
*/
-public final class AssociationRequest<F extends DeviceFilter> implements Parcelable {
-
- /** @hide */
- public static final int MEDIUM_TYPE_BLUETOOTH = 0;
- /** @hide */
- public static final int MEDIUM_TYPE_BLUETOOTH_LE = 1;
- /** @hide */
- public static final int MEDIUM_TYPE_WIFI = 2;
-
- /** @hide */
- @IntDef({MEDIUM_TYPE_BLUETOOTH, MEDIUM_TYPE_BLUETOOTH_LE, MEDIUM_TYPE_WIFI})
- @Retention(RetentionPolicy.SOURCE)
- public @interface MediumType {}
+public final class AssociationRequest implements Parcelable {
private final boolean mSingleDevice;
- private final int mMediumType;
- private final F mDeviceFilter;
+ private final List<DeviceFilter<?>> mDeviceFilters;
- private AssociationRequest(boolean singleDevice, int mMediumType, F deviceFilter) {
+ private AssociationRequest(
+ boolean singleDevice, @Nullable List<DeviceFilter<?>> deviceFilters) {
this.mSingleDevice = singleDevice;
- this.mMediumType = mMediumType;
- this.mDeviceFilter = deviceFilter;
+ this.mDeviceFilters = ArrayUtils.emptyIfNull(deviceFilters);
}
private AssociationRequest(Parcel in) {
this(
in.readByte() != 0,
- in.readInt(),
- in.readParcelable(AssociationRequest.class.getClassLoader()));
+ in.readParcelableList(new ArrayList<>(), AssociationRequest.class.getClassLoader()));
}
/** @hide */
@@ -77,22 +62,15 @@ public final class AssociationRequest<F extends DeviceFilter> implements Parcela
}
/** @hide */
- @MediumType
- public int getMediumType() {
- return mMediumType;
- }
-
- /** @hide */
- @Nullable
- public F getDeviceFilter() {
- return mDeviceFilter;
+ @NonNull
+ public List<DeviceFilter<?>> getDeviceFilters() {
+ return mDeviceFilters;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (mSingleDevice ? 1 : 0));
- dest.writeInt(mMediumType);
- dest.writeParcelable(mDeviceFilter, flags);
+ dest.writeParcelableList(mDeviceFilters, flags);
}
@Override
@@ -114,45 +92,19 @@ public final class AssociationRequest<F extends DeviceFilter> implements Parcela
/**
* A builder for {@link AssociationRequest}
- *
- * @param <F> the type of filter for the request.
*/
- public static final class Builder<F extends DeviceFilter>
- extends OneTimeUseBuilder<AssociationRequest<F>> {
+ public static final class Builder extends OneTimeUseBuilder<AssociationRequest> {
private boolean mSingleDevice = false;
- @MediumType private int mMediumType;
- @Nullable private F mDeviceFilter = null;
-
- private Builder() {}
-
- /**
- * Create a new builder for an association request with a Bluetooth LE device
- */
- @NonNull
- public static Builder<BluetoothLEDeviceFilter> createForBluetoothLEDevice() {
- return new Builder<BluetoothLEDeviceFilter>()
- .setMediumType(MEDIUM_TYPE_BLUETOOTH_LE);
- }
-
- /**
- * Create a new builder for an association request with a Bluetooth(non-LE) device
- */
- @NonNull
- public static Builder<BluetoothDeviceFilter> createForBluetoothDevice() {
- return new Builder<BluetoothDeviceFilter>()
- .setMediumType(MEDIUM_TYPE_BLUETOOTH);
- }
+ @Nullable private ArrayList<DeviceFilter<?>> mDeviceFilters = null;
- //TODO implement, once specific filter classes are available
-// public static Builder<> createForWiFiDevice()
-// public static Builder<> createForNanDevice()
+ public Builder() {}
/**
* @param singleDevice if true, scanning for a device will stop as soon as at least one
* fitting device is found
*/
@NonNull
- public Builder<F> setSingleDevice(boolean singleDevice) {
+ public Builder setSingleDevice(boolean singleDevice) {
checkNotUsed();
this.mSingleDevice = singleDevice;
return this;
@@ -163,29 +115,20 @@ public final class AssociationRequest<F extends DeviceFilter> implements Parcela
* user
*/
@NonNull
- public Builder<F> setDeviceFilter(@Nullable F deviceFilter) {
+ public Builder addDeviceFilter(@Nullable DeviceFilter<?> deviceFilter) {
checkNotUsed();
- this.mDeviceFilter = deviceFilter;
- return this;
- }
-
- /**
- * @param deviceType A type of medium over which to discover devices
- *
- * @see MediumType
- */
- @NonNull
- private Builder<F> setMediumType(@MediumType int deviceType) {
- mMediumType = deviceType;
+ if (deviceFilter != null) {
+ mDeviceFilters = ArrayUtils.add(mDeviceFilters, deviceFilter);
+ }
return this;
}
/** @inheritDoc */
@NonNull
@Override
- public AssociationRequest<F> build() {
+ public AssociationRequest build() {
markUsed();
- return new AssociationRequest<>(mSingleDevice, mMediumType, mDeviceFilter);
+ return new AssociationRequest(mSingleDevice, mDeviceFilters);
}
}
}
diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java
index 5a69955429aa..0f16b7b90165 100644
--- a/core/java/android/companion/BluetoothDeviceFilter.java
+++ b/core/java/android/companion/BluetoothDeviceFilter.java
@@ -16,6 +16,7 @@
package android.companion;
+import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal;
import static android.companion.BluetoothDeviceFilterUtils.matchesAddress;
import static android.companion.BluetoothDeviceFilterUtils.matchesName;
import static android.companion.BluetoothDeviceFilterUtils.matchesServiceUuids;
@@ -40,8 +41,6 @@ import java.util.regex.Pattern;
*/
public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice> {
- private static BluetoothDeviceFilter NO_OP;
-
private final Pattern mNamePattern;
private final String mAddress;
private final List<ParcelUuid> mServiceUuids;
@@ -67,30 +66,27 @@ public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice
}
private static List<ParcelUuid> readUuids(Parcel in) {
- final ArrayList<ParcelUuid> list = new ArrayList<>();
- in.readParcelableList(list, ParcelUuid.class.getClassLoader());
- return list;
+ return in.readParcelableList(new ArrayList<>(), ParcelUuid.class.getClassLoader());
}
/** @hide */
- @NonNull
- public static BluetoothDeviceFilter nullsafe(@Nullable BluetoothDeviceFilter nullable) {
- return nullable != null ? nullable : noOp();
+ @Override
+ public boolean matches(BluetoothDevice device) {
+ return matchesAddress(mAddress, device)
+ && matchesServiceUuids(mServiceUuids, mServiceUuidMasks, device)
+ && matchesName(getNamePattern(), device);
}
/** @hide */
- @NonNull
- public static BluetoothDeviceFilter noOp() {
- if (NO_OP == null) NO_OP = new Builder().build();
- return NO_OP;
+ @Override
+ public String getDeviceDisplayName(BluetoothDevice device) {
+ return getDeviceDisplayNameInternal(device);
}
/** @hide */
@Override
- public boolean matches(BluetoothDevice device) {
- return matchesAddress(mAddress, device)
- && matchesServiceUuids(mServiceUuids, mServiceUuidMasks, device)
- && matchesName(getNamePattern(), device);
+ public int getMediumType() {
+ return DeviceFilter.MEDIUM_TYPE_BLUETOOTH;
}
/** @hide */
diff --git a/core/java/android/companion/BluetoothDeviceFilterUtils.java b/core/java/android/companion/BluetoothDeviceFilterUtils.java
index 289f9953a068..8a316f19af8e 100644
--- a/core/java/android/companion/BluetoothDeviceFilterUtils.java
+++ b/core/java/android/companion/BluetoothDeviceFilterUtils.java
@@ -23,7 +23,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.ScanFilter;
+import android.net.wifi.ScanResult;
import android.os.ParcelUuid;
+import android.os.Parcelable;
import android.util.Log;
import java.util.Arrays;
@@ -96,12 +98,47 @@ public class BluetoothDeviceFilterUtils {
return result;
}
+ static boolean matchesName(@Nullable Pattern namePattern, ScanResult device) {
+ boolean result;
+ if (namePattern == null) {
+ result = true;
+ } else if (device == null) {
+ result = false;
+ } else {
+ final String name = device.SSID;
+ result = name != null && namePattern.matcher(name).find();
+ }
+ if (DEBUG) debugLogMatchResult(result, device, namePattern);
+ return result;
+ }
+
private static void debugLogMatchResult(
boolean result, BluetoothDevice device, Object criteria) {
- Log.i(LOG_TAG, getDeviceDisplayName(device) + (result ? " ~ " : " !~ ") + criteria);
+ Log.i(LOG_TAG, getDeviceDisplayNameInternal(device) + (result ? " ~ " : " !~ ") + criteria);
}
- public static String getDeviceDisplayName(@NonNull BluetoothDevice device) {
+ private static void debugLogMatchResult(
+ boolean result, ScanResult device, Object criteria) {
+ Log.i(LOG_TAG, getDeviceDisplayNameInternal(device) + (result ? " ~ " : " !~ ") + criteria);
+ }
+
+ public static String getDeviceDisplayNameInternal(@NonNull BluetoothDevice device) {
return firstNotEmpty(device.getAliasName(), device.getAddress());
}
+
+ public static String getDeviceDisplayNameInternal(@NonNull ScanResult device) {
+ return firstNotEmpty(device.SSID, device.BSSID);
+ }
+
+ public static String getDeviceMacAddress(@NonNull Parcelable device) {
+ if (device instanceof BluetoothDevice) {
+ return ((BluetoothDevice) device).getAddress();
+ } else if (device instanceof ScanResult) {
+ return ((ScanResult) device).BSSID;
+ } else if (device instanceof android.bluetooth.le.ScanResult) {
+ return getDeviceMacAddress(((android.bluetooth.le.ScanResult) device).getDevice());
+ } else {
+ throw new IllegalArgumentException("Unknown device type: " + device);
+ }
+ }
}
diff --git a/core/java/android/companion/BluetoothLEDeviceFilter.java b/core/java/android/companion/BluetoothLEDeviceFilter.java
index 4a481ca80045..e057fbcc901a 100644
--- a/core/java/android/companion/BluetoothLEDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLEDeviceFilter.java
@@ -16,18 +16,25 @@
package android.companion;
+import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal;
import static android.companion.BluetoothDeviceFilterUtils.patternFromString;
import static android.companion.BluetoothDeviceFilterUtils.patternToString;
+import static com.android.internal.util.Preconditions.checkArgument;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
import android.os.Parcel;
import android.provider.OneTimeUseBuilder;
+import android.text.TextUtils;
+import com.android.internal.util.BitUtils;
import com.android.internal.util.ObjectUtils;
+import com.android.internal.util.Preconditions;
import java.util.regex.Pattern;
@@ -36,57 +43,122 @@ import java.util.regex.Pattern;
*
* @see ScanFilter
*/
-public final class BluetoothLEDeviceFilter implements DeviceFilter<BluetoothDevice> {
+public final class BluetoothLEDeviceFilter implements DeviceFilter<ScanResult> {
- private static BluetoothLEDeviceFilter NO_OP;
+ private static final int RENAME_PREFIX_LENGTH_LIMIT = 10;
private final Pattern mNamePattern;
private final ScanFilter mScanFilter;
+ private final byte[] mRawDataFilter;
+ private final byte[] mRawDataFilterMask;
+ private final String mRenamePrefix;
+ private final String mRenameSuffix;
+ private final int mRenameBytesFrom;
+ private final int mRenameBytesTo;
+ private final boolean mRenameBytesReverseOrder;
- private BluetoothLEDeviceFilter(Pattern namePattern, ScanFilter scanFilter) {
+ private BluetoothLEDeviceFilter(Pattern namePattern, ScanFilter scanFilter,
+ byte[] rawDataFilter, byte[] rawDataFilterMask, String renamePrefix,
+ String renameSuffix, int renameBytesFrom, int renameBytesTo,
+ boolean renameBytesReverseOrder) {
mNamePattern = namePattern;
mScanFilter = ObjectUtils.firstNotNull(scanFilter, ScanFilter.EMPTY);
+ mRawDataFilter = rawDataFilter;
+ mRawDataFilterMask = rawDataFilterMask;
+ mRenamePrefix = renamePrefix;
+ mRenameSuffix = renameSuffix;
+ mRenameBytesFrom = renameBytesFrom;
+ mRenameBytesTo = renameBytesTo;
+ mRenameBytesReverseOrder = renameBytesReverseOrder;
}
- @SuppressLint("ParcelClassLoader")
- private BluetoothLEDeviceFilter(Parcel in) {
- this(
- patternFromString(in.readString()),
- in.readParcelable(null));
+ /** @hide */
+ @Nullable
+ public Pattern getNamePattern() {
+ return mNamePattern;
}
/** @hide */
@NonNull
- public static BluetoothLEDeviceFilter nullsafe(@Nullable BluetoothLEDeviceFilter nullable) {
- return nullable != null ? nullable : noOp();
+ public ScanFilter getScanFilter() {
+ return mScanFilter;
}
/** @hide */
- @NonNull
- public static BluetoothLEDeviceFilter noOp() {
- if (NO_OP == null) NO_OP = new Builder().build();
- return NO_OP;
+ @Nullable
+ public byte[] getRawDataFilter() {
+ return mRawDataFilter;
}
/** @hide */
@Nullable
- public Pattern getNamePattern() {
- return mNamePattern;
+ public byte[] getRawDataFilterMask() {
+ return mRawDataFilterMask;
}
/** @hide */
- @NonNull
- public ScanFilter getScanFilter() {
- return mScanFilter;
+ @Nullable
+ public String getRenamePrefix() {
+ return mRenamePrefix;
+ }
+
+ /** @hide */
+ @Nullable
+ public String getRenameSuffix() {
+ return mRenameSuffix;
+ }
+
+ /** @hide */
+ public int getRenameBytesFrom() {
+ return mRenameBytesFrom;
+ }
+
+ /** @hide */
+ public int getRenameBytesTo() {
+ return mRenameBytesTo;
+ }
+
+ /** @hide */
+ public boolean isRenameBytesReverseOrder() {
+ return mRenameBytesReverseOrder;
+ }
+
+ /** @hide */
+ @Override
+ @Nullable
+ public String getDeviceDisplayName(ScanResult sr) {
+ if (mRenameBytesFrom < 0) return getDeviceDisplayNameInternal(sr.getDevice());
+ final byte[] bytes = sr.getScanRecord().getBytes();
+ final StringBuilder sb = new StringBuilder(TextUtils.emptyIfNull(mRenamePrefix));
+ int startInclusive = mRenameBytesFrom;
+ int endInclusive = mRenameBytesTo - 1;
+ int initial = mRenameBytesReverseOrder ? endInclusive : startInclusive;
+ int step = mRenameBytesReverseOrder ? -1 : 1;
+ for (int i = initial; startInclusive <= i && i <= endInclusive; i+=step) {
+ sb.append(Byte.toHexString(bytes[i], true));
+ }
+ return sb.append(TextUtils.emptyIfNull(mRenameSuffix)).toString();
}
/** @hide */
@Override
- public boolean matches(BluetoothDevice device) {
+ public boolean matches(ScanResult device) {
+ return matches(device.getDevice())
+ && BitUtils.maskedEquals(device.getScanRecord().getBytes(),
+ mRawDataFilter, mRawDataFilterMask);
+ }
+
+ private boolean matches(BluetoothDevice device) {
return BluetoothDeviceFilterUtils.matches(getScanFilter(), device)
&& BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device);
}
+ /** @hide */
+ @Override
+ public int getMediumType() {
+ return DeviceFilter.MEDIUM_TYPE_BLUETOOTH_LE;
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(patternToString(getNamePattern()));
@@ -102,7 +174,13 @@ public final class BluetoothLEDeviceFilter implements DeviceFilter<BluetoothDevi
= new Creator<BluetoothLEDeviceFilter>() {
@Override
public BluetoothLEDeviceFilter createFromParcel(Parcel in) {
- return new BluetoothLEDeviceFilter(in);
+ return new BluetoothLEDeviceFilter.Builder()
+ .setNamePattern(patternFromString(in.readString()))
+ .setScanFilter(in.readParcelable(null))
+ .setRawDataFilter(in.readBlob(), in.readBlob())
+ .setRename(in.readString(), in.readString(),
+ in.readInt(), in.readInt(), in.readBoolean())
+ .build();
}
@Override
@@ -111,16 +189,28 @@ public final class BluetoothLEDeviceFilter implements DeviceFilter<BluetoothDevi
}
};
+ public static int getRenamePrefixLengthLimit() {
+ return RENAME_PREFIX_LENGTH_LIMIT;
+ }
+
/**
* Builder for {@link BluetoothLEDeviceFilter}
*/
public static final class Builder extends OneTimeUseBuilder<BluetoothLEDeviceFilter> {
private ScanFilter mScanFilter;
private Pattern mNamePattern;
+ private byte[] mRawDataFilter;
+ private byte[] mRawDataFilterMask;
+ private String mRenamePrefix;
+ private String mRenameSuffix;
+ private int mRenameBytesFrom = -1;
+ private int mRenameBytesTo;
+ private boolean mRenameBytesReverseOrder = false;
/**
* @param regex if set, only devices with {@link BluetoothDevice#getName name} matching the
* given regular expression will be shown
+ * @return self for chaining
*/
public Builder setNamePattern(@Nullable Pattern regex) {
checkNotUsed();
@@ -131,6 +221,7 @@ public final class BluetoothLEDeviceFilter implements DeviceFilter<BluetoothDevi
/**
* @param scanFilter a {@link ScanFilter} to filter devices by
*
+ * @return self for chaining
* @see ScanFilter for specific details on its various fields
*/
@NonNull
@@ -140,12 +231,66 @@ public final class BluetoothLEDeviceFilter implements DeviceFilter<BluetoothDevi
return this;
}
+ /**
+ * Filter devices by raw advertisement data, as obtained by {@link ScanRecord#getBytes}
+ *
+ * @param rawDataFilter bit values that have to match against advertized data
+ * @param rawDataFilterMask bits that have to be matched
+ * @return self for chaining
+ */
+ @NonNull
+ public Builder setRawDataFilter(@NonNull byte[] rawDataFilter,
+ @NonNull byte[] rawDataFilterMask) {
+ checkNotUsed();
+ checkArgument(rawDataFilter.length == rawDataFilterMask.length,
+ "Mask and filter should be the same length");
+ mRawDataFilter = Preconditions.checkNotNull(rawDataFilter);
+ mRawDataFilterMask = Preconditions.checkNotNull(rawDataFilterMask);
+ return this;
+ }
+
+ /**
+ * Rename the devices shown in the list, using specific bytes from the raw advertisement
+ * data ({@link ScanRecord#getBytes}) in hexadecimal format, as well as a custom
+ * prefix/suffix around them
+ *
+ * Note that the prefix length is limited to {@link #getRenamePrefixLengthLimit} characters
+ * to ensure that there's enough space to display the byte data
+ *
+ * The range of bytes to be displayed cannot be empty
+ *
+ * @param prefix to be displayed before the byte data
+ * @param suffix to be displayed after the byte data
+ * @param bytesFrom the start byte index to be displayed (inclusive)
+ * @param bytesTo the end byte index to be displayed (exclusive)
+ * @param bytesReverseOrder if true, the byte order of the provided range will be flipped
+ * when displaying
+ * @return self for chaining
+ */
+ @NonNull
+ public Builder setRename(@NonNull String prefix, @NonNull String suffix,
+ int bytesFrom, int bytesTo, boolean bytesReverseOrder) {
+ checkNotUsed();
+ checkArgument(TextUtils.length(prefix) >= getRenamePrefixLengthLimit(),
+ "Prefix is too short");
+ mRenamePrefix = prefix;
+ mRenameSuffix = suffix;
+ checkArgument(bytesFrom < bytesTo, "Byte range must be non-empty");
+ mRenameBytesFrom = bytesFrom;
+ mRenameBytesTo = bytesTo;
+ mRenameBytesReverseOrder = bytesReverseOrder;
+ return this;
+ }
+
/** @inheritDoc */
@Override
@NonNull
public BluetoothLEDeviceFilter build() {
markUsed();
- return new BluetoothLEDeviceFilter(mNamePattern, mScanFilter);
+ return new BluetoothLEDeviceFilter(mNamePattern, mScanFilter,
+ mRawDataFilter, mRawDataFilterMask,
+ mRenamePrefix, mRenameSuffix,
+ mRenameBytesFrom, mRenameBytesTo, mRenameBytesReverseOrder);
}
}
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 6fa32b4b6944..5710ad1318d7 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -117,7 +117,7 @@ public final class CompanionDeviceManager {
* @see AssociationRequest
*/
public void associate(
- @NonNull AssociationRequest<?> request,
+ @NonNull AssociationRequest request,
@NonNull Callback callback,
@Nullable Handler handler) {
final Handler finalHandler = handler != null
diff --git a/core/java/android/companion/DeviceFilter.java b/core/java/android/companion/DeviceFilter.java
index 8362b2dab8fd..9b4fdfdf5108 100644
--- a/core/java/android/companion/DeviceFilter.java
+++ b/core/java/android/companion/DeviceFilter.java
@@ -17,17 +17,28 @@
package android.companion;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A filter for companion devices of type {@code D}
*
* @param <D> Type of devices, filtered by this filter,
- * e.g. {@link android.bluetooth.BluetoothDevice}, {@link android.net.wifi.WifiInfo}
+ * e.g. {@link android.bluetooth.BluetoothDevice}, {@link android.net.wifi.ScanResult}
*/
public interface DeviceFilter<D extends Parcelable> extends Parcelable {
+ /** @hide */
+ int MEDIUM_TYPE_BLUETOOTH = 0;
+ /** @hide */
+ int MEDIUM_TYPE_BLUETOOTH_LE = 1;
+ /** @hide */
+ int MEDIUM_TYPE_WIFI = 2;
+
/**
* @return whether the given device matches this filter
*
@@ -35,6 +46,12 @@ public interface DeviceFilter<D extends Parcelable> extends Parcelable {
*/
boolean matches(D device);
+ /** @hide */
+ String getDeviceDisplayName(D device);
+
+ /** @hide */
+ @MediumType int getMediumType();
+
/**
* A nullsafe {@link #matches(Parcelable)}, returning true if the filter is null
*
@@ -43,4 +60,9 @@ public interface DeviceFilter<D extends Parcelable> extends Parcelable {
static <D extends Parcelable> boolean matches(@Nullable DeviceFilter<D> filter, D device) {
return filter == null || filter.matches(device);
}
+
+ /** @hide */
+ @IntDef({MEDIUM_TYPE_BLUETOOTH, MEDIUM_TYPE_BLUETOOTH_LE, MEDIUM_TYPE_WIFI})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface MediumType {}
}
diff --git a/core/java/android/companion/WifiDeviceFilter.java b/core/java/android/companion/WifiDeviceFilter.java
new file mode 100644
index 000000000000..1ab9ce11cb0f
--- /dev/null
+++ b/core/java/android/companion/WifiDeviceFilter.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 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.companion;
+
+import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal;
+import static android.companion.BluetoothDeviceFilterUtils.patternFromString;
+import static android.companion.BluetoothDeviceFilterUtils.patternToString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.ScanFilter;
+import android.net.wifi.ScanResult;
+import android.os.Parcel;
+import android.provider.OneTimeUseBuilder;
+
+import java.util.regex.Pattern;
+
+/**
+ * A filter for Wifi devices
+ *
+ * @see ScanFilter
+ */
+public final class WifiDeviceFilter implements DeviceFilter<ScanResult> {
+
+ private final Pattern mNamePattern;
+
+ private WifiDeviceFilter(Pattern namePattern) {
+ mNamePattern = namePattern;
+ }
+
+ @SuppressLint("ParcelClassLoader")
+ private WifiDeviceFilter(Parcel in) {
+ this(patternFromString(in.readString()));
+ }
+
+ /** @hide */
+ @Nullable
+ public Pattern getNamePattern() {
+ return mNamePattern;
+ }
+
+
+ /** @hide */
+ @Override
+ public boolean matches(ScanResult device) {
+ return BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device);
+ }
+
+ /** @hide */
+ @Override
+ public String getDeviceDisplayName(ScanResult device) {
+ return getDeviceDisplayNameInternal(device);
+ }
+
+ /** @hide */
+ @Override
+ public int getMediumType() {
+ return MEDIUM_TYPE_WIFI;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(patternToString(getNamePattern()));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<WifiDeviceFilter> CREATOR
+ = new Creator<WifiDeviceFilter>() {
+ @Override
+ public WifiDeviceFilter createFromParcel(Parcel in) {
+ return new WifiDeviceFilter(in);
+ }
+
+ @Override
+ public WifiDeviceFilter[] newArray(int size) {
+ return new WifiDeviceFilter[size];
+ }
+ };
+
+ /**
+ * Builder for {@link WifiDeviceFilter}
+ */
+ public static final class Builder extends OneTimeUseBuilder<WifiDeviceFilter> {
+ private Pattern mNamePattern;
+
+ /**
+ * @param regex if set, only devices with {@link BluetoothDevice#getName name} matching the
+ * given regular expression will be shown
+ * @return self for chaining
+ */
+ public Builder setNamePattern(@Nullable Pattern regex) {
+ checkNotUsed();
+ mNamePattern = regex;
+ return this;
+ }
+
+ /** @inheritDoc */
+ @Override
+ @NonNull
+ public WifiDeviceFilter build() {
+ markUsed();
+ return new WifiDeviceFilter(mNamePattern);
+ }
+ }
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index f94e89a2785d..7a39d239f84b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -601,6 +601,11 @@ public final class Parcel {
nativeWriteString(mNativePtr, val);
}
+ /** @hide */
+ public final void writeBoolean(boolean val) {
+ writeInt(val ? 1 : 0);
+ }
+
/**
* Write a CharSequence value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
@@ -1964,6 +1969,11 @@ public final class Parcel {
return nativeReadString(mNativePtr);
}
+ /** @hide */
+ public final boolean readBoolean() {
+ return readInt() != 0;
+ }
+
/**
* Read a CharSequence value from the parcel at the current dataPosition().
* @hide
@@ -2490,11 +2500,11 @@ public final class Parcel {
* @see #writeParcelableList(List, int)
* @hide
*/
- public final <T extends Parcelable> void readParcelableList(List<T> list, ClassLoader cl) {
+ public final <T extends Parcelable> List<T> readParcelableList(List<T> list, ClassLoader cl) {
final int N = readInt();
if (N == -1) {
list.clear();
- return;
+ return list;
}
final int M = list.size();
@@ -2508,6 +2518,7 @@ public final class Parcel {
for (; i<M; i++) {
list.remove(N);
}
+ return list;
}
/**
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index ac9c0d782c53..ee2b38e4f390 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -474,6 +474,11 @@ public class TextUtils {
return !isEmpty(a) ? a : Preconditions.checkStringNotEmpty(b);
}
+ /** {@hide} */
+ public static int length(@Nullable String s) {
+ return isEmpty(s) ? 0 : s.length();
+ }
+
/**
* Returns the length that the specified CharSequence would have if
* spaces and ASCII control characters were trimmed from the start and end,
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 77e9f0f2aa74..1b74c13be518 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -161,7 +161,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ if (accessibilityViewId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
root = mViewRootImpl.mView;
} else {
root = findViewByAccessibilityId(accessibilityViewId);
@@ -217,7 +217,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -283,7 +283,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -291,14 +291,9 @@ final class AccessibilityInteractionController {
if (root != null && isShown(root)) {
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
if (provider != null) {
- if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- infos = provider.findAccessibilityNodeInfosByText(text,
- virtualDescendantId);
- } else {
- infos = provider.findAccessibilityNodeInfosByText(text,
- AccessibilityNodeProvider.HOST_VIEW_ID);
- }
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ infos = provider.findAccessibilityNodeInfosByText(text,
+ virtualDescendantId);
+ } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
ArrayList<View> foundViews = mTempArrayList;
foundViews.clear();
root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
@@ -376,7 +371,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -402,7 +397,7 @@ final class AccessibilityInteractionController {
focused = AccessibilityNodeInfo.obtain(
mViewRootImpl.mAccessibilityFocusedVirtualView);
}
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
focused = host.createAccessibilityNodeInfo();
}
} break;
@@ -471,7 +466,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -531,7 +526,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View target = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
target = findViewByAccessibilityId(accessibilityViewId);
} else {
target = mViewRootImpl.mView;
@@ -544,14 +539,9 @@ final class AccessibilityInteractionController {
} else {
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
if (provider != null) {
- if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- succeeded = provider.performAction(virtualDescendantId, action,
- arguments);
- } else {
- succeeded = provider.performAction(
- AccessibilityNodeProvider.HOST_VIEW_ID, action, arguments);
- }
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ succeeded = provider.performAction(virtualDescendantId, action,
+ arguments);
+ } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
succeeded = target.performAccessibilityAction(action, arguments);
}
}
@@ -711,7 +701,9 @@ final class AccessibilityInteractionController {
applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
- infos.clear();
+ if (infos != null) {
+ infos.clear();
+ }
} catch (RemoteException re) {
/* ignore - the other side will time out */
} finally {
@@ -761,10 +753,8 @@ final class AccessibilityInteractionController {
AccessibilityNodeInfo infoWithSpan = null;
AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
if (provider != null) {
- int idForNode = (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
- ? AccessibilityNodeProvider.HOST_VIEW_ID : virtualDescendantId;
- infoWithSpan = provider.createAccessibilityNodeInfo(idForNode);
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ infoWithSpan = provider.createAccessibilityNodeInfo(virtualDescendantId);
+ } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
infoWithSpan = view.createAccessibilityNodeInfo();
}
if (infoWithSpan == null) {
@@ -817,13 +807,12 @@ final class AccessibilityInteractionController {
}
}
} else {
- final int idForRoot = (virtualViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
- ? AccessibilityNodeProvider.HOST_VIEW_ID : virtualViewId;
- final AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(idForRoot);
+ final AccessibilityNodeInfo root =
+ provider.createAccessibilityNodeInfo(virtualViewId);
if (root != null) {
if (extraDataRequested != null) {
provider.addExtraDataToAccessibilityNodeInfo(
- idForRoot, root, extraDataRequested, arguments);
+ virtualViewId, root, extraDataRequested, arguments);
}
outInfos.add(root);
if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
@@ -1034,15 +1023,10 @@ final class AccessibilityInteractionController {
}
final int virtualDescendantId =
AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
- if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
+ if (virtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
|| accessibilityViewId == providerHost.getAccessibilityViewId()) {
final AccessibilityNodeInfo parent;
- if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
- } else {
- parent = provider.createAccessibilityNodeInfo(
- AccessibilityNodeProvider.HOST_VIEW_ID);
- }
+ parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
if (parent == null) {
// Going up the parent relation we found a null predecessor,
// so remove these disconnected nodes form the result.
@@ -1072,15 +1056,10 @@ final class AccessibilityInteractionController {
AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
final int parentVirtualDescendantId =
AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
- if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
+ if (parentVirtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
|| parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
- final AccessibilityNodeInfo parent;
- if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- parent = provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
- } else {
- parent = provider.createAccessibilityNodeInfo(
- AccessibilityNodeProvider.HOST_VIEW_ID);
- }
+ final AccessibilityNodeInfo parent =
+ provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
if (parent != null) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index de816762c348..aa1cbf2d642e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -94,6 +94,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
@@ -8102,7 +8103,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public int getAccessibilityWindowId() {
return mAttachInfo != null ? mAttachInfo.mAccessibilityWindowId
- : AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+ : AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
}
/**
@@ -24657,7 +24658,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* The id of the window for accessibility purposes.
*/
- int mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+ int mAccessibilityWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
/**
* Flags related to accessibility processing.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f9863b0a6761..580888c4c3bc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -82,6 +82,7 @@ import android.view.accessibility.AccessibilityManager.HighTextContrastChangeLis
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -6880,12 +6881,7 @@ public final class ViewRootImpl implements ViewParent,
final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
sourceNodeId);
final AccessibilityNodeInfo node;
- if (virtualNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- node = provider.createAccessibilityNodeInfo(
- AccessibilityNodeProvider.HOST_VIEW_ID);
- } else {
- node = provider.createAccessibilityNodeInfo(virtualNodeId);
- }
+ node = provider.createAccessibilityNodeInfo(virtualNodeId);
setAccessibilityFocus(source, node);
}
}
@@ -6971,10 +6967,6 @@ public final class ViewRootImpl implements ViewParent,
final long focusedSourceNodeId = mAccessibilityFocusedVirtualView.getSourceNodeId();
int focusedChildId = AccessibilityNodeInfo.getVirtualDescendantId(focusedSourceNodeId);
- if (focusedChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- // TODO: Should we clear the focused virtual view?
- focusedChildId = AccessibilityNodeProvider.HOST_VIEW_ID;
- }
// Refresh the node for the focused virtual view.
final Rect oldBounds = mTempRect;
@@ -7495,8 +7487,8 @@ public final class ViewRootImpl implements ViewParent,
}
public void ensureConnection() {
- final boolean registered =
- mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+ final boolean registered = mAttachInfo.mAccessibilityWindowId
+ != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
if (!registered) {
mAttachInfo.mAccessibilityWindowId =
mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
@@ -7505,10 +7497,10 @@ public final class ViewRootImpl implements ViewParent,
}
public void ensureNoConnection() {
- final boolean registered =
- mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+ final boolean registered = mAttachInfo.mAccessibilityWindowId
+ != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
if (registered) {
- mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+ mAttachInfo.mAccessibilityWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
}
}
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index 737e46071313..95a63944b3bc 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -46,6 +46,7 @@ public class WindowInfo implements Parcelable {
public List<IBinder> childTokens;
public CharSequence title;
public int accessibilityIdOfAnchor = View.NO_ID;
+ public boolean inPictureInPicture;
private WindowInfo() {
/* do nothing - hide constructor */
@@ -69,6 +70,7 @@ public class WindowInfo implements Parcelable {
window.boundsInScreen.set(other.boundsInScreen);
window.title = other.title;
window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor;
+ window.inPictureInPicture = other.inPictureInPicture;
if (other.childTokens != null && !other.childTokens.isEmpty()) {
if (window.childTokens == null) {
@@ -101,6 +103,7 @@ public class WindowInfo implements Parcelable {
boundsInScreen.writeToParcel(parcel, flags);
parcel.writeCharSequence(title);
parcel.writeInt(accessibilityIdOfAnchor);
+ parcel.writeInt(inPictureInPicture ? 1 : 0);
if (childTokens != null && !childTokens.isEmpty()) {
parcel.writeInt(1);
@@ -136,6 +139,7 @@ public class WindowInfo implements Parcelable {
boundsInScreen.readFromParcel(parcel);
title = parcel.readCharSequence();
accessibilityIdOfAnchor = parcel.readInt();
+ inPictureInPicture = (parcel.readInt() == 1);
final boolean hasChildren = (parcel.readInt() == 1);
if (hasChildren) {
@@ -156,6 +160,7 @@ public class WindowInfo implements Parcelable {
if (childTokens != null) {
childTokens.clear();
}
+ inPictureInPicture = false;
}
public static final Parcelable.Creator<WindowInfo> CREATOR =
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 828583cdfe81..19213ca06c5e 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -158,7 +158,7 @@ public final class AccessibilityInteractionClient
*/
public AccessibilityNodeInfo getRootInActiveWindow(int connectionId) {
return findAccessibilityNodeInfoByAccessibilityId(connectionId,
- AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityWindowInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS, null);
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 45302b6cbdce..fe888ec56c09 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -21,6 +21,7 @@ import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -822,6 +823,31 @@ public final class AccessibilityManager {
}
}
+ /**
+ * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture
+ * window. Intended for use by the System UI only.
+ *
+ * @param connection The connection to handle the actions. Set to {@code null} to avoid
+ * affecting the actions.
+ *
+ * @hide
+ */
+ public void setPictureInPictureActionReplacingConnection(
+ @Nullable IAccessibilityInteractionConnection connection) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.setPictureInPictureActionReplacingConnection(connection);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting picture in picture action replacement", re);
+ }
+ }
+
private IAccessibilityManager getServiceLocked() {
if (mService == null) {
tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 8094fa6a1c71..50f17e089585 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -85,17 +85,19 @@ public class AccessibilityNodeInfo implements Parcelable {
/** @hide */
public static final int UNDEFINED_SELECTION_INDEX = -1;
+ /* Special IDs for node source IDs */
/** @hide */
public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE;
/** @hide */
- public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID);
+ public static final int ROOT_ITEM_ID = Integer.MAX_VALUE - 1;
/** @hide */
- public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID;
+ public static final long UNDEFINED_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID);
/** @hide */
- public static final int ANY_WINDOW_ID = -2;
+ public static final long ROOT_NODE_ID = makeNodeId(ROOT_ITEM_ID,
+ AccessibilityNodeProvider.HOST_VIEW_ID);
/** @hide */
public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
@@ -474,6 +476,34 @@ public class AccessibilityNodeInfo implements Parcelable {
"android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
/**
+ * Argument for specifying the x coordinate to which to move a window.
+ * <p>
+ * <strong>Type:</strong> int<br>
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_MOVE_WINDOW}</li>
+ * </ul>
+ *
+ * @see AccessibilityAction#ACTION_MOVE_WINDOW
+ */
+ public static final String ACTION_ARGUMENT_MOVE_WINDOW_X =
+ "android.view.accessibility.action.ARGUMENT_MOVE_WINDOW_X";
+
+ /**
+ * Argument for specifying the y coordinate to which to move a window.
+ * <p>
+ * <strong>Type:</strong> int<br>
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_MOVE_WINDOW}</li>
+ * </ul>
+ *
+ * @see AccessibilityAction#ACTION_MOVE_WINDOW
+ */
+ public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y =
+ "android.view.accessibility.action.ARGUMENT_MOVE_WINDOW_Y";
+
+ /**
* Argument to pass the {@link AccessibilityClickableSpan}.
* For use with R.id.accessibilityActionClickOnClickableSpan
* @hide
@@ -654,13 +684,6 @@ public class AccessibilityNodeInfo implements Parcelable {
* @hide
*/
public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
- // We changed the value for undefined node to positive due to wrong
- // global id composition (two 32-bin ints into one 64-bit long) but
- // the value used for the host node provider view has id -1 so we
- // remap it here.
- if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
- virtualDescendantId = UNDEFINED_ITEM_ID;
- }
return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
}
@@ -673,12 +696,12 @@ public class AccessibilityNodeInfo implements Parcelable {
// Data.
private int mWindowId = UNDEFINED_ITEM_ID;
- private long mSourceNodeId = ROOT_NODE_ID;
- private long mParentNodeId = ROOT_NODE_ID;
- private long mLabelForId = ROOT_NODE_ID;
- private long mLabeledById = ROOT_NODE_ID;
- private long mTraversalBefore = ROOT_NODE_ID;
- private long mTraversalAfter = ROOT_NODE_ID;
+ private long mSourceNodeId = UNDEFINED_NODE_ID;
+ private long mParentNodeId = UNDEFINED_NODE_ID;
+ private long mLabelForId = UNDEFINED_NODE_ID;
+ private long mLabeledById = UNDEFINED_NODE_ID;
+ private long mTraversalBefore = UNDEFINED_NODE_ID;
+ private long mTraversalAfter = UNDEFINED_NODE_ID;
private int mBooleanProperties;
private final Rect mBoundsInParent = new Rect();
@@ -733,7 +756,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param source The info source.
*/
public void setSource(View source) {
- setSource(source, UNDEFINED_ITEM_ID);
+ setSource(source, AccessibilityNodeProvider.HOST_VIEW_ID);
}
/**
@@ -950,7 +973,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void addChild(View child) {
- addChildInternal(child, UNDEFINED_ITEM_ID, true);
+ addChildInternal(child, AccessibilityNodeProvider.HOST_VIEW_ID, true);
}
/**
@@ -960,7 +983,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @hide
*/
public void addChildUnchecked(View child) {
- addChildInternal(child, UNDEFINED_ITEM_ID, false);
+ addChildInternal(child, AccessibilityNodeProvider.HOST_VIEW_ID, false);
}
/**
@@ -978,7 +1001,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public boolean removeChild(View child) {
- return removeChild(child, UNDEFINED_ITEM_ID);
+ return removeChild(child, AccessibilityNodeProvider.HOST_VIEW_ID);
}
/**
@@ -1205,6 +1228,17 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Removes all actions.
+ *
+ * @hide
+ */
+ public void removeAllActions() {
+ if (mActions != null) {
+ mActions.clear();
+ }
+ }
+
+ /**
* Gets the node before which this one is visited during traversal. A screen-reader
* must visit the content of this node before the content of the one it precedes.
*
@@ -1233,7 +1267,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @see #getTraversalBefore()
*/
public void setTraversalBefore(View view) {
- setTraversalBefore(view, UNDEFINED_ITEM_ID);
+ setTraversalBefore(view, AccessibilityNodeProvider.HOST_VIEW_ID);
}
/**
@@ -1294,7 +1328,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @see #getTraversalAfter()
*/
public void setTraversalAfter(View view) {
- setTraversalAfter(view, UNDEFINED_ITEM_ID);
+ setTraversalAfter(view, AccessibilityNodeProvider.HOST_VIEW_ID);
}
/**
@@ -1572,7 +1606,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void setParent(View parent) {
- setParent(parent, UNDEFINED_ITEM_ID);
+ setParent(parent, AccessibilityNodeProvider.HOST_VIEW_ID);
}
/**
@@ -2533,7 +2567,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param labeled The view for which this info serves as a label.
*/
public void setLabelFor(View labeled) {
- setLabelFor(labeled, UNDEFINED_ITEM_ID);
+ setLabelFor(labeled, AccessibilityNodeProvider.HOST_VIEW_ID);
}
/**
@@ -2585,7 +2619,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param label The view that labels this node's source.
*/
public void setLabeledBy(View label) {
- setLabeledBy(label, UNDEFINED_ITEM_ID);
+ setLabeledBy(label, AccessibilityNodeProvider.HOST_VIEW_ID);
}
/**
@@ -2815,6 +2849,20 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Sets the id of the source node.
+ *
+ * @param sourceId The id.
+ * @param windowId The window id.
+ *
+ * @hide
+ */
+ public void setSourceNodeId(long sourceId, int windowId) {
+ enforceNotSealed();
+ mSourceNodeId = sourceId;
+ mWindowId = windowId;
+ }
+
+ /**
* Gets the id of the source node.
*
* @return The id.
@@ -3299,12 +3347,12 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
private void clear() {
mSealed = false;
- mSourceNodeId = ROOT_NODE_ID;
- mParentNodeId = ROOT_NODE_ID;
- mLabelForId = ROOT_NODE_ID;
- mLabeledById = ROOT_NODE_ID;
- mTraversalBefore = ROOT_NODE_ID;
- mTraversalAfter = ROOT_NODE_ID;
+ mSourceNodeId = UNDEFINED_NODE_ID;
+ mParentNodeId = UNDEFINED_NODE_ID;
+ mLabelForId = UNDEFINED_NODE_ID;
+ mLabeledById = UNDEFINED_NODE_ID;
+ mTraversalBefore = UNDEFINED_NODE_ID;
+ mTraversalAfter = UNDEFINED_NODE_ID;
mWindowId = UNDEFINED_ITEM_ID;
mConnectionId = UNDEFINED_CONNECTION_ID;
mMaxTextLength = -1;
@@ -3324,9 +3372,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mError = null;
mContentDescription = null;
mViewIdResourceName = null;
- if (mActions != null) {
- mActions.clear();
- }
+ removeAllActions();
mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
mInputType = InputType.TYPE_NULL;
@@ -3975,6 +4021,16 @@ public class AccessibilityNodeInfo implements Parcelable {
public static final AccessibilityAction ACTION_SET_PROGRESS =
new AccessibilityAction(R.id.accessibilityActionSetProgress, null);
+ /**
+ * Action to move a window to a new location.
+ * <p>
+ * <strong>Arguments:</strong>
+ * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVE_WINDOW_X}
+ * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVE_WINDOW_Y}
+ */
+ public static final AccessibilityAction ACTION_MOVE_WINDOW =
+ new AccessibilityAction(R.id.accessibilityActionMoveWindow, null);
+
private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<>();
static {
sStandardActions.add(ACTION_FOCUS);
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index f2979bb82dcd..3f1fece3eff4 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -91,7 +91,7 @@ public class AccessibilityRecord {
int mAddedCount= UNDEFINED;
int mRemovedCount = UNDEFINED;
AccessibilityNodeInfo mSourceNode;
- int mSourceWindowId = UNDEFINED;
+ int mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
CharSequence mClassName;
CharSequence mContentDescription;
@@ -136,11 +136,12 @@ public class AccessibilityRecord {
public void setSource(View root, int virtualDescendantId) {
enforceNotSealed();
boolean important = true;
- mSourceWindowId = UNDEFINED;
+ mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
clearSourceNode();
if (root != null) {
- if (virtualDescendantId == UNDEFINED ||
- virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ if (virtualDescendantId == View.NO_ID
+ || virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID
+ || virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
important = root.isImportantForAccessibility();
mSourceNode = root.createAccessibilityNodeInfo();
} else {
@@ -156,6 +157,25 @@ public class AccessibilityRecord {
}
/**
+ * Set the source directly to an AccessibilityNodeInfo rather than indirectly via a View
+ *
+ * @param info The source
+ *
+ * @hide
+ */
+ public void setSource(AccessibilityNodeInfo info) {
+ enforceNotSealed();
+ clearSourceNode();
+ mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ if (info != null) {
+ mSourceNode = AccessibilityNodeInfo.obtain(info);
+ setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY,
+ mSourceNode.isImportantForAccessibility());
+ mSourceWindowId = info.getWindowId();
+ }
+ }
+
+ /**
* Gets the {@link AccessibilityNodeInfo} of the event source.
* <p>
* <strong>Note:</strong> It is a client responsibility to recycle the received info
@@ -833,6 +853,7 @@ public class AccessibilityRecord {
mSourceNode.recycle();
mSourceNode = null;
}
+
}
@Override
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index c3904060247a..2a7537b08921 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -74,7 +74,15 @@ public final class AccessibilityWindowInfo implements Parcelable {
*/
public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5;
- private static final int UNDEFINED = -1;
+ /* Special values for window IDs */
+ /** @hide */
+ public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE;
+ /** @hide */
+ public static final int UNDEFINED_WINDOW_ID = -1;
+ /** @hide */
+ public static final int ANY_WINDOW_ID = -2;
+ /** @hide */
+ public static final int PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID = -3;
private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1;
@@ -87,17 +95,18 @@ public final class AccessibilityWindowInfo implements Parcelable {
private static AtomicInteger sNumInstancesInUse;
// Data.
- private int mType = UNDEFINED;
- private int mLayer = UNDEFINED;
+ private int mType = UNDEFINED_WINDOW_ID;
+ private int mLayer = UNDEFINED_WINDOW_ID;
private int mBooleanProperties;
- private int mId = UNDEFINED;
- private int mParentId = UNDEFINED;
+ private int mId = UNDEFINED_WINDOW_ID;
+ private int mParentId = UNDEFINED_WINDOW_ID;
private final Rect mBoundsInScreen = new Rect();
private LongArray mChildIds;
private CharSequence mTitle;
- private int mAnchorId = UNDEFINED;
+ private int mAnchorId = UNDEFINED_WINDOW_ID;
+ private boolean mInPictureInPicture;
- private int mConnectionId = UNDEFINED;
+ private int mConnectionId = UNDEFINED_WINDOW_ID;
private AccessibilityWindowInfo() {
/* do nothing - hide constructor */
@@ -177,7 +186,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
* @return The root node.
*/
public AccessibilityNodeInfo getRoot() {
- if (mConnectionId == UNDEFINED) {
+ if (mConnectionId == UNDEFINED_WINDOW_ID) {
return null;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
@@ -203,7 +212,8 @@ public final class AccessibilityWindowInfo implements Parcelable {
* @return The anchor node, or {@code null} if none exists.
*/
public AccessibilityNodeInfo getAnchor() {
- if ((mConnectionId == UNDEFINED) || (mAnchorId == UNDEFINED) || (mParentId == UNDEFINED)) {
+ if ((mConnectionId == UNDEFINED_WINDOW_ID) || (mAnchorId == UNDEFINED_WINDOW_ID)
+ || (mParentId == UNDEFINED_WINDOW_ID)) {
return null;
}
@@ -212,13 +222,27 @@ public final class AccessibilityWindowInfo implements Parcelable {
mParentId, mAnchorId, true, 0, null);
}
+ /** @hide */
+ public void setPictureInPicture(boolean pictureInPicture) {
+ mInPictureInPicture = pictureInPicture;
+ }
+
+ /**
+ * Check if the window is in picture-in-picture mode.
+ *
+ * @return {@code true} if the window is in picture-in-picture mode, {@code false} otherwise.
+ */
+ public boolean inPictureInPicture() {
+ return mInPictureInPicture;
+ }
+
/**
* Gets the parent window.
*
* @return The parent window, or {@code null} if none exists.
*/
public AccessibilityWindowInfo getParent() {
- if (mConnectionId == UNDEFINED || mParentId == UNDEFINED) {
+ if (mConnectionId == UNDEFINED_WINDOW_ID || mParentId == UNDEFINED_WINDOW_ID) {
return null;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
@@ -371,7 +395,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
if (mChildIds == null) {
throw new IndexOutOfBoundsException();
}
- if (mConnectionId == UNDEFINED) {
+ if (mConnectionId == UNDEFINED_WINDOW_ID) {
return null;
}
final int childId = (int) mChildIds.get(index);
@@ -429,6 +453,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
infoClone.mBoundsInScreen.set(info.mBoundsInScreen);
infoClone.mTitle = info.mTitle;
infoClone.mAnchorId = info.mAnchorId;
+ infoClone.mInPictureInPicture = info.mInPictureInPicture;
if (info.mChildIds != null && info.mChildIds.size() > 0) {
if (infoClone.mChildIds == null) {
@@ -486,6 +511,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
mBoundsInScreen.writeToParcel(parcel, flags);
parcel.writeCharSequence(mTitle);
parcel.writeInt(mAnchorId);
+ parcel.writeInt(mInPictureInPicture ? 1 : 0);
final LongArray childIds = mChildIds;
if (childIds == null) {
@@ -510,6 +536,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
mBoundsInScreen.readFromParcel(parcel);
mTitle = parcel.readCharSequence();
mAnchorId = parcel.readInt();
+ mInPictureInPicture = parcel.readInt() == 1;
final int childCount = parcel.readInt();
if (childCount > 0) {
@@ -556,6 +583,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
builder.append(", bounds=").append(mBoundsInScreen);
builder.append(", focused=").append(isFocused());
builder.append(", active=").append(isActive());
+ builder.append(", pictureInPicture=").append(inPictureInPicture());
if (DEBUG) {
builder.append(", parent=").append(mParentId);
builder.append(", children=[");
@@ -572,8 +600,8 @@ public final class AccessibilityWindowInfo implements Parcelable {
}
builder.append(']');
} else {
- builder.append(", hasParent=").append(mParentId != UNDEFINED);
- builder.append(", isAnchored=").append(mAnchorId != UNDEFINED);
+ builder.append(", hasParent=").append(mParentId != UNDEFINED_WINDOW_ID);
+ builder.append(", isAnchored=").append(mAnchorId != UNDEFINED_WINDOW_ID);
builder.append(", hasChildren=").append(mChildIds != null
&& mChildIds.size() > 0);
}
@@ -585,17 +613,18 @@ public final class AccessibilityWindowInfo implements Parcelable {
* Clears the internal state.
*/
private void clear() {
- mType = UNDEFINED;
- mLayer = UNDEFINED;
+ mType = UNDEFINED_WINDOW_ID;
+ mLayer = UNDEFINED_WINDOW_ID;
mBooleanProperties = 0;
- mId = UNDEFINED;
- mParentId = UNDEFINED;
+ mId = UNDEFINED_WINDOW_ID;
+ mParentId = UNDEFINED_WINDOW_ID;
mBoundsInScreen.setEmpty();
if (mChildIds != null) {
mChildIds.clear();
}
- mConnectionId = UNDEFINED;
- mAnchorId = UNDEFINED;
+ mConnectionId = UNDEFINED_WINDOW_ID;
+ mAnchorId = UNDEFINED_WINDOW_ID;
+ mInPictureInPicture = false;
mTitle = null;
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 8fde47a30b2b..157980f6cb83 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -45,10 +45,13 @@ interface IAccessibilityManager {
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
int addAccessibilityInteractionConnection(IWindow windowToken,
- in IAccessibilityInteractionConnection connection, int userId);
+ in IAccessibilityInteractionConnection connection, int userId);
void removeAccessibilityInteractionConnection(IWindow windowToken);
+ void setPictureInPictureActionReplacingConnection(
+ in IAccessibilityInteractionConnection connection);
+
void registerUiTestAutomationService(IBinder owner, IAccessibilityServiceClient client,
in AccessibilityServiceInfo info, int flags);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 7ff115bee1d2..a8e16c96acfa 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -16,6 +16,7 @@
package com.android.internal.policy;
+import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;
import com.android.internal.policy.PhoneWindow.PanelFeatureState;
import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
@@ -2251,6 +2252,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
@Override
+ public int getAccessibilityViewId() {
+ return AccessibilityNodeInfo.ROOT_ITEM_ID;
+ }
+
+ @Override
public String toString() {
return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
+ getTitleSuffix(mWindow.getAttributes()) + "]";
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index be69d9f808e2..d0fbe7c8a666 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -237,6 +237,35 @@ public class ArrayUtils {
return false;
}
+ @NonNull
+ public static <T> List<T> filter(@Nullable List<?> list, Class<T> c) {
+ if (isEmpty(list)) return Collections.emptyList();
+ ArrayList<T> result = null;
+ for (int i = 0; i < list.size(); i++) {
+ final Object item = list.get(i);
+ if (c.isInstance(item)) {
+ result = add(result, (T) item);
+ }
+ }
+ return emptyIfNull(result);
+ }
+
+ public static <T> boolean any(@Nullable List<T> items,
+ java.util.function.Predicate<T> predicate) {
+ return find(items, predicate) != null;
+ }
+
+ @Nullable
+ public static <T> T find(@Nullable List<T> items,
+ java.util.function.Predicate<T> predicate) {
+ if (isEmpty(items)) return null;
+ for (int i = 0; i < items.size(); i++) {
+ final T item = items.get(i);
+ if (predicate.test(item)) return item;
+ }
+ return null;
+ }
+
public static long total(@Nullable long[] array) {
long total = 0;
if (array != null) {
diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
new file mode 100644
index 000000000000..a208ccb8f35f
--- /dev/null
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.internal.util;
+
+import android.annotation.Nullable;
+
+import libcore.util.Objects;
+
+import java.util.Arrays;
+import java.util.UUID;
+
+public class BitUtils {
+ private BitUtils() {}
+
+ public static boolean maskedEquals(long a, long b, long mask) {
+ return (a & mask) == (b & mask);
+ }
+
+ public static boolean maskedEquals(byte a, byte b, byte mask) {
+ return (a & mask) == (b & mask);
+ }
+
+ public static boolean maskedEquals(byte[] a, byte[] b, @Nullable byte[] mask) {
+ if (a == null || b == null) return a == b;
+ Preconditions.checkArgument(a.length == b.length, "Inputs must be of same size");
+ if (mask == null) return Arrays.equals(a, b);
+ Preconditions.checkArgument(a.length == mask.length, "Mask must be of same size as inputs");
+ for (int i = 0; i < mask.length; i++) {
+ if (!maskedEquals(a[i], b[i], mask[i])) return false;
+ }
+ return true;
+ }
+
+ public static boolean maskedEquals(UUID a, UUID b, @Nullable UUID mask) {
+ if (mask == null) {
+ return Objects.equal(a, b);
+ }
+ return maskedEquals(a.getLeastSignificantBits(), b.getLeastSignificantBits(),
+ mask.getLeastSignificantBits())
+ && maskedEquals(a.getMostSignificantBits(), b.getMostSignificantBits(),
+ mask.getMostSignificantBits());
+ }
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index c9f9d6f6dfb0..95b2593201e6 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -197,7 +197,6 @@ LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/android/graphics \
$(LOCAL_PATH)/../../libs/hwui \
- $(LOCAL_PATH)/../../../native/opengl/libs \
$(LOCAL_PATH)/../../../native/vulkan/include \
$(call include-path-for, bluedroid) \
$(call include-path-for, libhardware)/hardware \
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 99edf6ef944e..e1c0a211f7f9 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -27,7 +27,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <EGL/egl_cache.h>
+#include <private/EGL/cache.h>
#include <utils/Looper.h>
#include <utils/RefBase.h>
@@ -887,7 +887,7 @@ static void android_view_ThreadedRenderer_removeFrameMetricsObserver(JNIEnv* env
static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
jstring diskCachePath) {
const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
- egl_cache_t::get()->setCacheFilename(cacheArray);
+ android::egl_set_cache_filename(cacheArray);
env->ReleaseStringUTFChars(diskCachePath, cacheArray);
}
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 56bec186b8a3..1ac05f91b3c0 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -22,9 +22,9 @@
#include <utils/misc.h>
-#include <EGL/egl_display.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
+#include <private/EGL/display.h>
#include <gui/Surface.h>
#include <gui/GLConsumer.h>
@@ -180,8 +180,7 @@ static jboolean jni_eglQuerySurface(JNIEnv *_env, jobject _this, jobject display
static jint jni_getInitCount(JNIEnv *_env, jobject _clazz, jobject display) {
EGLDisplay dpy = getDisplay(_env, display);
- egl_display_t* eglDisplay = get_display_nowake(dpy);
- return eglDisplay ? eglDisplay->getRefsCount() : 0;
+ return android::egl_get_init_count(dpy);
}
static jboolean jni_eglReleaseThread(JNIEnv *_env, jobject _this) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ac36d835a2f5..c991f22b6294 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2306,6 +2306,10 @@
<permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN"
android:protectionLevel="signature" />
+ <!-- @hide Allows an application to modify accessibility information from another app. -->
+ <permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows an application to collect frame statistics -->
<permission android:name="android.permission.FRAME_STATS"
android:protectionLevel="signature" />
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 613616fa0ea6..f8a071d04372 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -135,4 +135,7 @@
<!-- ItemTouchHelper uses this id to save a View's original elevation. -->
<item type="id" name="item_touch_helper_previous_elevation"/>
+
+ <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_MOVE_WINDOW}. -->
+ <item type="id" name="accessibilityActionMoveWindow" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index eca3afdb7fd6..359fbcb08266 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2812,6 +2812,7 @@
<public-group type="id" first-id="0x01020041">
<public name="textAssist" />
+ <public name="accessibilityActionMoveWindow" />
</public-group>
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 65cac09a09b3..34bc4ebcd0aa 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -26,6 +26,8 @@
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<application
android:allowClearUserData="true"
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 12bab18c88c9..14b9de59bf5a 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -16,10 +16,9 @@
package com.android.companiondevicemanager;
-import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayName;
+import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
import android.app.Activity;
-import android.bluetooth.BluetoothDevice;
import android.companion.CompanionDeviceManager;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -34,6 +33,8 @@ import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.android.companiondevicemanager.DeviceDiscoveryService.DeviceFilterPair;
+
public class DeviceChooserActivity extends Activity {
private static final boolean DEBUG = false;
@@ -55,11 +56,11 @@ public class DeviceChooserActivity extends Activity {
if (getService().mRequest.isSingleDevice()) {
setContentView(R.layout.device_confirmation);
- final BluetoothDevice selectedDevice = getService().mDevicesFound.get(0);
+ final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
setTitle(Html.fromHtml(getString(
R.string.confirmation_title,
getCallingAppName(),
- getDeviceDisplayName(selectedDevice)), 0));
+ selectedDevice.getDisplayName()), 0));
getService().mSelectedDevice = selectedDevice;
} else {
setContentView(R.layout.device_chooser);
@@ -127,10 +128,11 @@ public class DeviceChooserActivity extends Activity {
return DeviceDiscoveryService.sInstance;
}
- protected void onPairTapped(BluetoothDevice selectedDevice) {
- getService().onDeviceSelected(getCallingPackage(), selectedDevice.getAddress());
+ protected void onPairTapped(DeviceFilterPair selectedDevice) {
+ getService().onDeviceSelected(
+ getCallingPackage(), getDeviceMacAddress(selectedDevice.device));
setResult(RESULT_OK,
- new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice));
+ new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
finish();
}
} \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index f0f910848943..e1e60bb99374 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -16,8 +16,10 @@
package com.android.companiondevicemanager;
-import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayName;
-import static android.companion.BluetoothLEDeviceFilter.nullsafe;
+import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal;
+import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
+
+import static com.android.internal.util.ArrayUtils.isEmpty;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,28 +34,38 @@ import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.companion.AssociationRequest;
+import android.companion.BluetoothDeviceFilter;
+import android.companion.BluetoothDeviceFilterUtils;
import android.companion.BluetoothLEDeviceFilter;
import android.companion.CompanionDeviceManager;
+import android.companion.DeviceFilter;
import android.companion.ICompanionDeviceDiscoveryService;
import android.companion.ICompanionDeviceDiscoveryServiceCallback;
import android.companion.IFindDeviceCallback;
+import android.companion.WifiDeviceFilter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
+import android.net.wifi.WifiManager;
import android.os.IBinder;
+import android.os.Parcelable;
import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import java.util.Objects;
public class DeviceDiscoveryService extends Service {
@@ -63,12 +75,16 @@ public class DeviceDiscoveryService extends Service {
static DeviceDiscoveryService sInstance;
private BluetoothAdapter mBluetoothAdapter;
- private BluetoothLEDeviceFilter mFilter;
- private ScanFilter mScanFilter;
+ private WifiManager mWifiManager;
private ScanSettings mDefaultScanSettings = new ScanSettings.Builder().build();
- AssociationRequest<?> mRequest;
- List<BluetoothDevice> mDevicesFound;
- BluetoothDevice mSelectedDevice;
+ private List<DeviceFilter<?>> mFilters;
+ private List<BluetoothLEDeviceFilter> mBLEFilters;
+ private List<BluetoothDeviceFilter> mBluetoothFilters;
+ private List<WifiDeviceFilter> mWifiFilters;
+ private List<ScanFilter> mBLEScanFilters;
+ AssociationRequest mRequest;
+ List<DeviceFilterPair> mDevicesFound;
+ DeviceFilterPair mSelectedDevice;
DevicesAdapter mDevicesAdapter;
IFindDeviceCallback mFindCallback;
ICompanionDeviceDiscoveryServiceCallback mServiceCallback;
@@ -95,11 +111,13 @@ public class DeviceDiscoveryService extends Service {
private final ScanCallback mBLEScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
- final BluetoothDevice device = result.getDevice();
+ final DeviceFilterPair<ScanResult> deviceFilterPair
+ = DeviceFilterPair.findMatch(result, mBLEFilters);
+ if (deviceFilterPair == null) return;
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) {
- onDeviceLost(device);
+ onDeviceLost(deviceFilterPair);
} else {
- onDeviceFound(device);
+ onDeviceFound(deviceFilterPair);
}
}
};
@@ -109,15 +127,35 @@ public class DeviceDiscoveryService extends Service {
private BroadcastReceiver mBluetoothDeviceFoundBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final BluetoothDevice device = intent.getParcelableExtra(
- BluetoothDevice.EXTRA_DEVICE);
- if (!mFilter.matches(device)) return; // ignore device
-
+ final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ final DeviceFilterPair<BluetoothDevice> deviceFilterPair
+ = DeviceFilterPair.findMatch(device, mBluetoothFilters);
+ if (deviceFilterPair == null) return;
if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
- onDeviceFound(device);
+ onDeviceFound(deviceFilterPair);
} else {
- onDeviceLost(device);
+ onDeviceLost(deviceFilterPair);
+ }
+ }
+ };
+
+ private BroadcastReceiver mWifiDeviceFoundBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ List<android.net.wifi.ScanResult> scanResults = mWifiManager.getScanResults();
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Wifi scan results: " + TextUtils.join("\n", scanResults));
+ }
+
+ for (int i = 0; i < scanResults.size(); i++) {
+ DeviceFilterPair<android.net.wifi.ScanResult> deviceFilterPair =
+ DeviceFilterPair.findMatch(scanResults.get(i), mWifiFilters);
+ if (deviceFilterPair != null) onDeviceFound(deviceFilterPair);
+ }
}
+
}
};
@@ -135,6 +173,7 @@ public class DeviceDiscoveryService extends Service {
mBluetoothAdapter = getSystemService(BluetoothManager.class).getAdapter();
mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
+ mWifiManager = getSystemService(WifiManager.class);
mDevicesFound = new ArrayList<>();
mDevicesAdapter = new DevicesAdapter();
@@ -142,23 +181,39 @@ public class DeviceDiscoveryService extends Service {
sInstance = this;
}
- private void startDiscovery(AssociationRequest<?> request) {
- //TODO support other protocols as well
+ private void startDiscovery(AssociationRequest request) {
mRequest = request;
- mFilter = nullsafe((BluetoothLEDeviceFilter) request.getDeviceFilter());
- mScanFilter = mFilter.getScanFilter();
+
+ mFilters = request.getDeviceFilters();
+ mWifiFilters = ArrayUtils.filter(mFilters, WifiDeviceFilter.class);
+ mBluetoothFilters = ArrayUtils.filter(mFilters, BluetoothDeviceFilter.class);
+ mBLEFilters = ArrayUtils.filter(mFilters, BluetoothLEDeviceFilter.class);
+ mBLEScanFilters = ArrayUtils.map(mBLEFilters, BluetoothLEDeviceFilter::getScanFilter);
reset();
- final IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
- intentFilter.addAction(BluetoothDevice.ACTION_DISAPPEARED);
+ if (shouldScan(mBluetoothFilters)) {
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
+ intentFilter.addAction(BluetoothDevice.ACTION_DISAPPEARED);
- registerReceiver(mBluetoothDeviceFoundBroadcastReceiver, intentFilter);
- mBluetoothAdapter.startDiscovery();
+ registerReceiver(mBluetoothDeviceFoundBroadcastReceiver, intentFilter);
+ mBluetoothAdapter.startDiscovery();
+ }
- mBLEScanner.startScan(
- Collections.singletonList(mScanFilter), mDefaultScanSettings, mBLEScanCallback);
+ if (shouldScan(mBLEFilters)) {
+ mBLEScanner.startScan(mBLEScanFilters, mDefaultScanSettings, mBLEScanCallback);
+ }
+
+ if (shouldScan(mWifiFilters)) {
+ registerReceiver(mWifiDeviceFoundBroadcastReceiver,
+ new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ mWifiManager.startScan();
+ }
+ }
+
+ private boolean shouldScan(List<? extends DeviceFilter> mediumSpecificFilters) {
+ return !isEmpty(mediumSpecificFilters) || isEmpty(mFilters);
}
private void reset() {
@@ -178,25 +233,18 @@ public class DeviceDiscoveryService extends Service {
mBluetoothAdapter.cancelDiscovery();
mBLEScanner.stopScan(mBLEScanCallback);
unregisterReceiver(mBluetoothDeviceFoundBroadcastReceiver);
+ unregisterReceiver(mWifiDeviceFoundBroadcastReceiver);
stopSelf();
}
- private void onDeviceFound(BluetoothDevice device) {
+ private void onDeviceFound(@Nullable DeviceFilterPair device) {
if (mDevicesFound.contains(device)) {
return;
}
- if (DEBUG) {
- Log.i(LOG_TAG, "Considering device " + getDeviceDisplayName(device));
- }
+ if (DEBUG) Log.i(LOG_TAG, "Found device " + device.getDisplayName() + " "
+ + getDeviceMacAddress(device.device));
- if (!mFilter.matches(device)) {
- return;
- }
-
- if (DEBUG) {
- Log.i(LOG_TAG, "Found device " + getDeviceDisplayName(device));
- }
if (mDevicesFound.isEmpty()) {
onReadyToShowUI();
}
@@ -217,12 +265,10 @@ public class DeviceDiscoveryService extends Service {
}
}
- private void onDeviceLost(BluetoothDevice device) {
+ private void onDeviceLost(@Nullable DeviceFilterPair device) {
mDevicesFound.remove(device);
mDevicesAdapter.notifyDataSetChanged();
- if (DEBUG) {
- Log.i(LOG_TAG, "Lost device " + getDeviceDisplayName(device));
- }
+ if (DEBUG) Log.i(LOG_TAG, "Lost device " + device.getDisplayName());
}
void onDeviceSelected(String callingPackage, String deviceAddress) {
@@ -236,7 +282,8 @@ public class DeviceDiscoveryService extends Service {
}
}
- class DevicesAdapter extends ArrayAdapter<BluetoothDevice> {
+ class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> {
+ //TODO wifi icon
private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth);
private Drawable icon(int drawableRes) {
@@ -261,8 +308,8 @@ public class DeviceDiscoveryService extends Service {
return view;
}
- private void bind(TextView textView, BluetoothDevice device) {
- textView.setText(getDeviceDisplayName(device));
+ private void bind(TextView textView, DeviceFilterPair device) {
+ textView.setText(device.getDisplayName());
textView.setBackgroundColor(
device.equals(mSelectedDevice)
? Color.GRAY
@@ -285,4 +332,62 @@ public class DeviceDiscoveryService extends Service {
return textView;
}
}
+
+ /**
+ * A pair of device and a filter that matched this device if any.
+ *
+ * @param <T> device type
+ */
+ static class DeviceFilterPair<T extends Parcelable> {
+ public final T device;
+ @Nullable
+ public final DeviceFilter<T> filter;
+
+ private DeviceFilterPair(T device, @Nullable DeviceFilter<T> filter) {
+ this.device = device;
+ this.filter = filter;
+ }
+
+ /**
+ * {@code (device, null)} if the filters list is empty or null
+ * {@code null} if none of the provided filters match the device
+ * {@code (device, filter)} where filter is among the list of filters and matches the device
+ */
+ @Nullable
+ public static <T extends Parcelable> DeviceFilterPair<T> findMatch(
+ T dev, @Nullable List<? extends DeviceFilter<T>> filters) {
+ if (isEmpty(filters)) return new DeviceFilterPair<>(dev, null);
+ final DeviceFilter<T> matchingFilter = ArrayUtils.find(filters, (f) -> f.matches(dev));
+ return matchingFilter != null ? new DeviceFilterPair<>(dev, matchingFilter) : null;
+ }
+
+ public String getDisplayName() {
+ if (filter == null) {
+ Preconditions.checkNotNull(device);
+ if (device instanceof BluetoothDevice) {
+ return getDeviceDisplayNameInternal((BluetoothDevice) device);
+ } else if (device instanceof android.net.wifi.ScanResult) {
+ return getDeviceDisplayNameInternal((android.net.wifi.ScanResult) device);
+ } else if (device instanceof ScanResult) {
+ return getDeviceDisplayNameInternal(((ScanResult) device).getDevice());
+ } else {
+ throw new IllegalArgumentException("Unknown device type: " + device.getClass());
+ }
+ }
+ return filter.getDeviceDisplayName(device);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DeviceFilterPair<?> that = (DeviceFilterPair<?>) o;
+ return Objects.equals(getDeviceMacAddress(device), getDeviceMacAddress(that.device));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getDeviceMacAddress(device));
+ }
+ }
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b2e2a2c5f2ff..dcc5501365b6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -177,6 +177,9 @@
<uses-permission android:name="android.permission.MODIFY_THEME_OVERLAY" />
+ <!-- accessibility -->
+ <uses-permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c5bcb6e91702..1541649ca24c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1818,6 +1818,9 @@
<!-- App label of the instant apps notification [CHAR LIMIT=60] -->
<string name="instant_apps">Instant Apps</string>
+ <!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
+ <string name="pip_menu_title">Picture in picture menu</string>
+
<!-- Message of the instant apps notification indicating they don't need install [CHAR LIMIT=NONE] -->
<string name="instant_apps_message">Instant apps don\'t require installation.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
index 7a1849ed741e..762d6bcb5188 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
@@ -44,6 +44,14 @@ public class InputConsumerController {
}
/**
+ * Listener interface for callers to learn when this class is registered or unregistered with
+ * window manager
+ */
+ public interface RegistrationListener {
+ void onRegistrationChanged(boolean isRegistered);
+ }
+
+ /**
* Input handler used for the PiP input consumer.
*/
private final class PipInputEventReceiver extends InputEventReceiver {
@@ -71,6 +79,7 @@ public class InputConsumerController {
private PipInputEventReceiver mInputEventReceiver;
private TouchListener mListener;
+ private RegistrationListener mRegistrationListener;
public InputConsumerController(IWindowManager windowManager) {
mWindowManager = windowManager;
@@ -85,6 +94,22 @@ public class InputConsumerController {
}
/**
+ * Sets the registration listener.
+ */
+ public void setRegistrationListener(RegistrationListener listener) {
+ mRegistrationListener = listener;
+ }
+
+ /**
+ * Check if the InputConsumer is currently registered with WindowManager
+ *
+ * @return {@code true} if registered, {@code false} if not.
+ */
+ public boolean isRegistered() {
+ return mInputEventReceiver != null;
+ }
+
+ /**
* Registers the input consumer.
*/
public void registerInputConsumer() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 138f6e71ac96..86e2c4956070 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -49,6 +49,7 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -143,6 +144,7 @@ public class PipMenuActivity extends Activity {
mExpandButton = (ImageView) findViewById(R.id.expand_button);
updateFromIntent(getIntent());
+ setTitle(R.string.pip_menu_title);
notifyActivityCallback(mMessenger);
}
@@ -262,6 +264,9 @@ public class PipMenuActivity extends Activity {
if (animationFinishedRunnable != null) {
animationFinishedRunnable.run();
}
+ if (getSystemService(AccessibilityManager.class).isEnabled()) {
+ finish();
+ }
}
});
mMenuContainerAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 7eaecdf7991a..17c344853638 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -21,14 +21,22 @@ import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.util.Size;
import android.view.IPinnedStackController;
-import android.view.IWindowManager;
+import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -37,6 +45,8 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.FlingAnimationUtils;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
@@ -64,6 +74,7 @@ public class PipTouchHandler {
private final PipMenuActivityController mMenuController;
private final PipDismissViewController mDismissViewController;
private final PipSnapAlgorithm mSnapAlgorithm;
+ private final AccessibilityManager mAccessibilityManager;
// The current movement bounds
private Rect mMovementBounds = new Rect();
@@ -85,12 +96,20 @@ public class PipTouchHandler {
}
};
+ private Runnable mShowMenuRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds);
+ }
+ };
+
// Behaviour states
private boolean mIsMenuVisible;
private boolean mIsMinimized;
private boolean mIsImeShowing;
private int mImeHeight;
private float mSavedSnapFraction = -1f;
+ private boolean mSendingHoverAccessibilityEvents;
// Touch state
private final PipTouchState mTouchState;
@@ -138,6 +157,7 @@ public class PipTouchHandler {
// Initialize the Pip input consumer
mContext = context;
mActivityManager = activityManager;
+ mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mViewConfig = ViewConfiguration.get(context);
mMenuController = menuController;
mMenuController.addListener(mMenuListener);
@@ -155,6 +175,8 @@ public class PipTouchHandler {
// Register the listener for input consumer touch events
inputConsumerController.setTouchListener(this::handleTouchEvent);
+ inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
+ onRegistrationChanged(inputConsumerController.isRegistered());
}
public void setTouchEnabled(boolean enabled) {
@@ -239,6 +261,11 @@ public class PipTouchHandler {
updateMovementBounds(mIsMenuVisible);
}
+ private void onRegistrationChanged(boolean isRegistered) {
+ mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
+ ? new AccessibilityInteractionConnection() : null);
+ }
+
private boolean handleTouchEvent(MotionEvent ev) {
// Skip touch handling until we are bound to the controller
if (mPinnedStackController == null) {
@@ -282,6 +309,31 @@ public class PipTouchHandler {
mTouchState.reset();
break;
}
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE: {
+ if (!mSendingHoverAccessibilityEvents) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+ AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
+ event.setSource(info);
+ info.recycle();
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ mSendingHoverAccessibilityEvents = true;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_HOVER_EXIT: {
+ if (mSendingHoverAccessibilityEvents) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+ AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
+ event.setSource(info);
+ info.recycle();
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ mSendingHoverAccessibilityEvents = false;
+ }
+ break;
+ }
}
return !mIsMenuVisible;
}
@@ -572,4 +624,144 @@ public class PipTouchHandler {
mTouchState.dump(pw, innerPrefix);
mMotionHelper.dump(pw, innerPrefix);
}
+
+ private static AccessibilityNodeInfo obtainRootAccessibilityNodeInfo() {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ info.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
+ info.setImportantForAccessibility(true);
+ info.setClickable(true);
+ info.setVisibleToUser(true);
+ return info;
+ }
+
+ /**
+ * Expose the touch actions to accessibility as if this object were a window with a single view.
+ * That pseudo-view exposes all of the actions this object can perform.
+ */
+ class AccessibilityInteractionConnection extends IAccessibilityInteractionConnection.Stub {
+ static final long ACCESSIBILITY_NODE_ID = 1;
+ List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;
+
+ @Override
+ public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
+ Region interactiveRegion, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
+ try {
+ callback.setFindAccessibilityNodeInfosResult(
+ (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID)
+ ? getNodeList() : null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void performAccessibilityAction(long accessibilityNodeId, int action,
+ Bundle arguments, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid) {
+ // We only support one view. A request for anything else is invalid
+ boolean result = false;
+ if (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_CLICK:
+ mHandler.post(mShowMenuRunnable);
+ result = true;
+ break;
+ case AccessibilityNodeInfo.ACTION_DISMISS:
+ mMotionHelper.dismissPip();
+ result = true;
+ break;
+ case com.android.internal.R.id.accessibilityActionMoveWindow:
+ int newX = arguments.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_X);
+ int newY = arguments.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_Y);
+ Rect pipBounds = new Rect();
+ pipBounds.set(mMotionHelper.getBounds());
+ mTmpBounds.offsetTo(newX, newY);
+ mMotionHelper.movePip(mTmpBounds);
+ result = true;
+ break;
+ case AccessibilityNodeInfo.ACTION_EXPAND:
+ mMotionHelper.expandPip();
+ result = true;
+ break;
+ default:
+ // Leave result as false
+ }
+ }
+ try {
+ callback.setPerformAccessibilityActionResult(result, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
+ String viewId, Region interactiveRegion, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+ // We have no view with a proper ID
+ try {
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
+ Region interactiveRegion, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+ // We have no view with text
+ try {
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+ // We have no view that can take focus
+ try {
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ @Override
+ public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+ // We have no view that can take focus
+ try {
+ callback.setFindAccessibilityNodeInfoResult(null, interactionId);
+ } catch (RemoteException re) {
+ /* best effort - ignore */
+ }
+ }
+
+ private List<AccessibilityNodeInfo> getNodeList() {
+ if (mAccessibilityNodeInfoList == null) {
+ mAccessibilityNodeInfoList = new ArrayList<>(1);
+ }
+ AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
+ mAccessibilityNodeInfoList.clear();
+ mAccessibilityNodeInfoList.add(info);
+ return mAccessibilityNodeInfoList;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 12332fb79059..47468aeaeb97 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -22,6 +22,7 @@ import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import android.annotation.NonNull;
@@ -1057,15 +1058,18 @@ public class SystemServicesProxy {
}
/**
- * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
+ * Returns the window rect for the RecentsActivity, based on the dimensions of the recents stack
*/
public Rect getWindowRect() {
Rect windowRect = new Rect();
if (mIam == null) return windowRect;
try {
- // Use the home stack bounds
- ActivityManager.StackInfo stackInfo = mIam.getStackInfo(HOME_STACK_ID);
+ // Use the recents stack bounds, fallback to fullscreen stack if it is null
+ ActivityManager.StackInfo stackInfo = mIam.getStackInfo(RECENTS_STACK_ID);
+ if (stackInfo == null) {
+ stackInfo = mIam.getStackInfo(FULLSCREEN_WORKSPACE_STACK_ID);
+ }
if (stackInfo != null) {
windowRect.set(stackInfo.bounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 6f59fe2001a1..97506e6df1c2 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -746,15 +746,18 @@ public class DividerView extends FrameLayout implements OnTouchListener,
if (mStableInsets.isEmpty()) {
SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
}
+ mMinimizedSnapAlgorithm = null;
+ mDockedStackMinimized = minimized;
+ initializeSnapAlgorithm();
if (!mIsInMinimizeInteraction && minimized) {
mIsInMinimizeInteraction = true;
mDividerPositionBeforeMinimized = DockedDividerUtils.calculateMiddlePosition(
isHorizontalDivision(), mStableInsets, mDisplayWidth, mDisplayHeight,
mDividerSize);
+
+ int position = mMinimizedSnapAlgorithm.getMiddleTarget().position;
+ resizeStack(position, position, mMinimizedSnapAlgorithm.getMiddleTarget());
}
- mMinimizedSnapAlgorithm = null;
- mDockedStackMinimized = minimized;
- initializeSnapAlgorithm();
}
}
@@ -1140,7 +1143,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
&& dockSideBottomRight(mDockSide))) {
return StackId.DOCKED_STACK_ID;
} else {
- return StackId.HOME_STACK_ID;
+ return StackId.RECENTS_STACK_ID;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 44afe1dbf4df..e0d78063ffb8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -82,6 +82,7 @@ import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
+import android.view.View;
import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManagerInternal;
@@ -97,6 +98,7 @@ import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.SomeArgs;
import com.android.server.LocalServices;
@@ -149,6 +151,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private static final String GET_WINDOW_TOKEN = "getWindowToken";
+ private static final String SET_PIP_ACTION_REPLACEMENT =
+ "setPictureInPictureActionReplacingConnection";
+
private static final ComponentName sFakeAccessibilityServiceComponentName =
new ComponentName("foo.bar", "FakeService");
@@ -158,8 +163,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
- private static final int WINDOW_ID_UNKNOWN = -1;
-
// Each service has an ID. Also provide one for magnification gesture handling
public static final int MAGNIFICATION_GESTURE_HANDLER_ID = 0;
@@ -220,6 +223,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final SparseArray<AccessibilityConnectionWrapper> mGlobalInteractionConnections =
new SparseArray<>();
+ private AccessibilityConnectionWrapper mPictureInPictureActionReplacingConnection;
+
private final SparseArray<IBinder> mGlobalWindowTokens = new SparseArray<>();
private final SparseArray<UserState> mUserStates = new SparseArray<>();
@@ -465,6 +470,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
boolean dispatchEvent = false;
synchronized (mLock) {
+ if (event.getWindowId() ==
+ AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID) {
+ // The replacer window isn't shown to services. Move its events into the pip.
+ AccessibilityWindowInfo pip = mSecurityPolicy.getPictureInPictureWindow();
+ if (pip != null) {
+ int pipId = pip.getId();
+ event.setWindowId(pipId);
+ event.setSealed(true);
+ AccessibilityNodeInfo info = event.getSource();
+ info.setSealed(false);
+ event.setSealed(false);
+ if (info != null) {
+ info.setSourceNodeId(info.getSourceNodeId(), pipId);
+ event.setSource(info);
+ info.recycle();
+ }
+ }
+ }
+
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution..
@@ -694,6 +718,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
@Override
+ public void setPictureInPictureActionReplacingConnection(
+ IAccessibilityInteractionConnection connection) throws RemoteException {
+ mSecurityPolicy.enforceCallingPermission(Manifest.permission.MODIFY_ACCESSIBILITY_DATA,
+ SET_PIP_ACTION_REPLACEMENT);
+ synchronized (mLock) {
+ if (mPictureInPictureActionReplacingConnection != null) {
+ mPictureInPictureActionReplacingConnection.unlinkToDeath();
+ mPictureInPictureActionReplacingConnection = null;
+ }
+ if (connection != null) {
+ AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(
+ AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID,
+ connection, UserHandle.USER_ALL);
+ mPictureInPictureActionReplacingConnection = wrapper;
+ wrapper.linkToDeath();
+ }
+ mSecurityPolicy.notifyWindowsChanged();
+ }
+ }
+
+ @Override
public void registerUiTestAutomationService(IBinder owner,
IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo,
@@ -1293,9 +1338,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return false;
}
- if (event.getWindowId() != WINDOW_ID_UNKNOWN && !event.isImportantForAccessibility()
- && (service.mFetchFlags
- & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) {
+ if ((event.getWindowId() != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
+ && !event.isImportantForAccessibility()
+ && (service.mFetchFlags & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS)
+ == 0) {
return false;
}
@@ -2035,6 +2081,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ @GuardedBy("mLock")
private MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
IBinder windowToken = mGlobalWindowTokens.get(windowId);
if (windowToken == null) {
@@ -2846,6 +2893,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
Region partialInteractiveRegion = Region.obtain();
+ MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!isCalledForCurrentUserLocked()) {
@@ -2867,10 +2915,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
+ spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
}
final int interrogatingPid = Binder.getCallingPid();
+ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
+ interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
- MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId, viewIdResName,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -2898,6 +2948,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
Region partialInteractiveRegion = Region.obtain();
+ MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!isCalledForCurrentUserLocked()) {
@@ -2919,10 +2970,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
+ spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
}
final int interrogatingPid = Binder.getCallingPid();
+ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
+ interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
- MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -2950,6 +3003,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
Region partialInteractiveRegion = Region.obtain();
+ MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!isCalledForCurrentUserLocked()) {
@@ -2971,10 +3025,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
+ spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
}
final int interrogatingPid = Binder.getCallingPid();
+ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
+ interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
- MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
partialInteractiveRegion, interactionId, callback, mFetchFlags | flags,
@@ -3002,6 +3058,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
Region partialInteractiveRegion = Region.obtain();
+ MagnificationSpec spec;
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
return false;
@@ -3023,10 +3080,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
+ spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
}
final int interrogatingPid = Binder.getCallingPid();
+ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
+ interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
- MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
connection.findFocus(accessibilityNodeId, focusType, partialInteractiveRegion,
interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
@@ -3054,6 +3113,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
Region partialInteractiveRegion = Region.obtain();
+ MagnificationSpec spec;
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
return false;
@@ -3074,10 +3134,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
+ spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
}
final int interrogatingPid = Binder.getCallingPid();
+ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
+ interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
- MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
connection.focusSearch(accessibilityNodeId, direction, partialInteractiveRegion,
interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
@@ -3149,6 +3211,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (connection == null) {
return false;
}
+ AccessibilityWindowInfo windowInfo =
+ mSecurityPolicy.findWindowById(resolvedWindowId);
+ if ((windowInfo != null) && windowInfo.inPictureInPicture()) {
+ boolean isA11yFocusAction =
+ (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)
+ || (action ==
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ if ((mPictureInPictureActionReplacingConnection != null)
+ && !isA11yFocusAction) {
+ connection = mPictureInPictureActionReplacingConnection.mConnection;
+ }
+ }
}
}
final int interrogatingPid = Binder.getCallingPid();
@@ -3562,7 +3636,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
event.setConnectionId(mId);
} else {
- event.setSource(null);
+ event.setSource((View) null);
}
event.setSealed(true);
}
@@ -3790,17 +3864,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private int resolveAccessibilityWindowIdLocked(int accessibilityWindowId) {
- if (accessibilityWindowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) {
+ if (accessibilityWindowId == AccessibilityWindowInfo.ACTIVE_WINDOW_ID) {
return mSecurityPolicy.getActiveWindowId();
}
return accessibilityWindowId;
}
private int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) {
- if (windowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) {
+ if (windowId == AccessibilityWindowInfo.ACTIVE_WINDOW_ID) {
return mSecurityPolicy.mActiveWindowId;
}
- if (windowId == AccessibilityNodeInfo.ANY_WINDOW_ID) {
+ if (windowId == AccessibilityWindowInfo.ANY_WINDOW_ID) {
if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) {
return mSecurityPolicy.mFocusedWindowId;
} else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) {
@@ -3810,6 +3884,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return windowId;
}
+ private IAccessibilityInteractionConnectionCallback replaceCallbackIfNeeded(
+ IAccessibilityInteractionConnectionCallback originalCallback,
+ int resolvedWindowId, int interactionId, int interrogatingPid,
+ long interrogatingTid) {
+ AccessibilityWindowInfo windowInfo = mSecurityPolicy.findWindowById(resolvedWindowId);
+ if ((windowInfo == null) || !windowInfo.inPictureInPicture()
+ || (mPictureInPictureActionReplacingConnection == null)) {
+ return originalCallback;
+ }
+ return new ActionReplacingCallback(originalCallback,
+ mPictureInPictureActionReplacingConnection.mConnection, interactionId,
+ interrogatingPid, interrogatingTid);
+ }
+
private final class InvocationHandler extends Handler {
public static final int MSG_ON_GESTURE = 1;
public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2;
@@ -3960,6 +4048,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
reportedWindow.setBoundsInScreen(window.boundsInScreen);
reportedWindow.setTitle(window.title);
reportedWindow.setAnchorId(window.accessibilityIdOfAnchor);
+ reportedWindow.setPictureInPicture(window.inPictureInPicture);
final int parentId = findWindowIdLocked(window.parentToken);
if (parentId >= 0) {
@@ -4157,7 +4246,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
| AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
+ // In Z order
public List<AccessibilityWindowInfo> mWindows;
+ public SparseArray<AccessibilityWindowInfo> mWindowsById = new SparseArray<>();
public int mActiveWindowId = INVALID_WINDOW_ID;
public int mFocusedWindowId = INVALID_WINDOW_ID;
@@ -4217,6 +4308,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
for (int i = oldWindowCount - 1; i >= 0; i--) {
mWindows.remove(i).recycle();
}
+ mWindowsById.clear();
mFocusedWindowId = INVALID_WINDOW_ID;
if (!mTouchInteractionInProgress) {
@@ -4245,6 +4337,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
mWindows.add(window);
+ mWindowsById.put(windowId, window);
}
if (mTouchInteractionInProgress && activeWindowGone) {
@@ -4306,7 +4399,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public void updateEventSourceLocked(AccessibilityEvent event) {
if ((event.getEventType() & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) {
- event.setSource(null);
+ event.setSource((View) null);
}
}
@@ -4446,7 +4539,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
- private void notifyWindowsChanged() {
+ public void notifyWindowsChanged() {
if (mWindowsForAccessibilityCallback == null) {
return;
}
@@ -4560,11 +4653,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private AccessibilityWindowInfo findWindowById(int windowId) {
+ return mWindowsById.get(windowId);
+ }
+
+ private AccessibilityWindowInfo getPictureInPictureWindow() {
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = mWindows.get(i);
- if (window.getId() == windowId) {
+ if (window.inPictureInPicture()) {
return window;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
new file mode 100644
index 000000000000..0e30fb28f3d7
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2017 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 android.os.Binder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.MagnificationSpec;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * If we are stripping and/or replacing the actions from a window, we need to intercept the
+ * nodes heading back to the service and swap out the actions.
+ */
+public class ActionReplacingCallback extends IAccessibilityInteractionConnectionCallback.Stub {
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "ActionReplacingCallback";
+
+ private final IAccessibilityInteractionConnectionCallback mServiceCallback;
+ private final IAccessibilityInteractionConnection mConnectionWithReplacementActions;
+ private final int mInteractionId;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ List<AccessibilityNodeInfo> mNodesWithReplacementActions;
+
+ @GuardedBy("mLock")
+ List<AccessibilityNodeInfo> mNodesFromOriginalWindow;
+
+ @GuardedBy("mLock")
+ AccessibilityNodeInfo mNodeFromOriginalWindow;
+
+ // Keep track of whether or not we've been called back for a single node
+ @GuardedBy("mLock")
+ boolean mSingleNodeCallbackHappened;
+
+ // Keep track of whether or not we've been called back for multiple node
+ @GuardedBy("mLock")
+ boolean mMultiNodeCallbackHappened;
+
+ // We shouldn't get any more callbacks after we've called back the original service, but
+ // keep track to make sure we catch such strange things
+ @GuardedBy("mLock")
+ boolean mDone;
+
+ public ActionReplacingCallback(IAccessibilityInteractionConnectionCallback serviceCallback,
+ IAccessibilityInteractionConnection connectionWithReplacementActions,
+ int interactionId, int interrogatingPid, long interrogatingTid) {
+ mServiceCallback = serviceCallback;
+ mConnectionWithReplacementActions = connectionWithReplacementActions;
+ mInteractionId = interactionId;
+
+ // Request the root node of the replacing window
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mConnectionWithReplacementActions.findAccessibilityNodeInfoByAccessibilityId(
+ AccessibilityNodeInfo.ROOT_NODE_ID, null, interactionId + 1, this, 0,
+ interrogatingPid, interrogatingTid, null, null);
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
+ }
+ // Pretend we already got a (null) list of replacement nodes
+ mMultiNodeCallbackHappened = true;
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info, int interactionId) {
+ boolean readyForCallback;
+ synchronized(mLock) {
+ if (interactionId == mInteractionId) {
+ mNodeFromOriginalWindow = info;
+ } else {
+ Slog.e(LOG_TAG, "Callback with unexpected interactionId");
+ throw new RuntimeException("Callback with unexpected interactionId"); // Remove
+ }
+
+ mSingleNodeCallbackHappened = true;
+ readyForCallback = mMultiNodeCallbackHappened;
+ }
+ if (readyForCallback) {
+ replaceInfoActionsAndCallService();
+ }
+ }
+
+ @Override
+ public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
+ int interactionId) {
+ boolean callbackForSingleNode;
+ boolean callbackForMultipleNodes;
+ synchronized(mLock) {
+ if (interactionId == mInteractionId) {
+ mNodesFromOriginalWindow = infos;
+ } else if (interactionId == mInteractionId + 1) {
+ mNodesWithReplacementActions = infos;
+ } else {
+ Slog.e(LOG_TAG, "Callback with unexpected interactionId");
+ throw new RuntimeException("Callback with unexpected interactionId"); // Remove
+ }
+ callbackForSingleNode = mSingleNodeCallbackHappened;
+ callbackForMultipleNodes = mMultiNodeCallbackHappened;
+ mMultiNodeCallbackHappened = true;
+ }
+ if (callbackForSingleNode) {
+ replaceInfoActionsAndCallService();
+ }
+ if (callbackForMultipleNodes) {
+ replaceInfosActionsAndCallService();
+ }
+ }
+
+ @Override
+ public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId)
+ throws RemoteException {
+ // There's no reason to use this class when performing actions. Do something reasonable.
+ mServiceCallback.setPerformAccessibilityActionResult(succeeded, interactionId);
+ }
+
+ private void replaceInfoActionsAndCallService() {
+ final AccessibilityNodeInfo nodeToReturn;
+ synchronized (mLock) {
+ if (mDone) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Extra callback");
+ }
+ throw new RuntimeException("Extra callback"); // Replace with return before submit
+ }
+ if (mNodeFromOriginalWindow != null) {
+ replaceActionsOnInfoLocked(mNodeFromOriginalWindow);
+ }
+ recycleReplaceActionNodesLocked();
+ nodeToReturn = mNodeFromOriginalWindow;
+ mDone = true;
+ }
+ try {
+ mServiceCallback.setFindAccessibilityNodeInfoResult(nodeToReturn, mInteractionId);
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfoResult");
+ }
+ }
+ }
+
+ private void replaceInfosActionsAndCallService() {
+ final List<AccessibilityNodeInfo> nodesToReturn;
+ synchronized (mLock) {
+ if (mDone) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Extra callback");
+ }
+ throw new RuntimeException("Extra callback"); // Replace with return before submit
+ }
+ if (mNodesFromOriginalWindow != null) {
+ for (int i = 0; i < mNodesFromOriginalWindow.size(); i++) {
+ replaceActionsOnInfoLocked(mNodesFromOriginalWindow.get(i));
+ }
+ }
+ recycleReplaceActionNodesLocked();
+ nodesToReturn = mNodesFromOriginalWindow;
+ mDone = true;
+ }
+ try {
+ mServiceCallback.setFindAccessibilityNodeInfosResult(nodesToReturn, mInteractionId);
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult");
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void replaceActionsOnInfoLocked(AccessibilityNodeInfo info) {
+ info.removeAllActions();
+ // We currently only replace actions for the root node
+ if ((info.getSourceNodeId() == AccessibilityNodeInfo.ROOT_NODE_ID)
+ && mNodesWithReplacementActions != null) {
+ // This list should always contain a single node with the root ID
+ for (int i = 0; i < mNodesWithReplacementActions.size(); i++) {
+ AccessibilityNodeInfo nodeWithReplacementActions =
+ mNodesWithReplacementActions.get(i);
+ if (nodeWithReplacementActions.getSourceNodeId()
+ == AccessibilityNodeInfo.ROOT_NODE_ID) {
+ List<AccessibilityAction> actions = nodeWithReplacementActions.getActionList();
+ if (actions != null) {
+ for (int j = 0; j < actions.size(); j++) {
+ info.addAction(actions.get(j));
+ }
+ // The PIP needs to be able to take accessibility focus
+ info.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS);
+ info.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ }
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void recycleReplaceActionNodesLocked() {
+ for (int i = mNodesWithReplacementActions.size() - 1; i >= 0; i--) {
+ AccessibilityNodeInfo nodeWithReplacementAction = mNodesWithReplacementActions.get(i);
+ nodeWithReplacementAction.recycle();
+ }
+ mNodesWithReplacementActions = null;
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 50882e1c55f8..fef7b513b7bc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -127,10 +127,11 @@ import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FO
import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -9654,7 +9655,7 @@ public class ActivityManagerService extends IActivityManager.Stub
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
"getTaskThumbnail()");
final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
- id, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
if (tr != null) {
return tr.getTaskThumbnailLocked();
}
@@ -9667,8 +9668,8 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"getTaskDescription()");
- final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
- id, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
if (tr != null) {
return tr.lastTaskDescription;
}
@@ -9780,7 +9781,7 @@ public class ActivityManagerService extends IActivityManager.Stub
public void setTaskResizeable(int taskId, int resizeableMode) {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
- taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
if (task == null) {
Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
return;
@@ -9842,8 +9843,8 @@ public class ActivityManagerService extends IActivityManager.Stub
Rect rect = new Rect();
try {
synchronized (this) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
- taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
if (task == null) {
Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
return rect;
@@ -9874,8 +9875,8 @@ public class ActivityManagerService extends IActivityManager.Stub
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
- taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ MATCH_TASK_IN_STACKS_ONLY, INVALID_STACK_ID);
if (task == null) {
Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
return;
@@ -9893,8 +9894,8 @@ public class ActivityManagerService extends IActivityManager.Stub
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
- taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ MATCH_TASK_IN_STACKS_ONLY, INVALID_STACK_ID);
if (task == null) {
Slog.w(TAG, "cancelTaskThumbnailTransition: taskId=" + taskId + " not found");
return;
@@ -9913,8 +9914,8 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
final TaskRecord task;
synchronized (this) {
- task = mStackSupervisor.anyTaskForIdLocked(
- taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
if (task == null) {
Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
return null;
@@ -19900,14 +19901,11 @@ public class ActivityManagerService extends IActivityManager.Stub
/** Helper method that requests bounds from WM and applies them to stack. */
private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
final Rect newStackBounds = new Rect();
- final Rect newTempTaskBounds = new Rect();
- mStackSupervisor.getStack(stackId).getBoundsForNewConfiguration(newStackBounds,
- newTempTaskBounds);
+ mStackSupervisor.getStack(stackId).getBoundsForNewConfiguration(newStackBounds);
mStackSupervisor.resizeStackLocked(
stackId, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
- !newTempTaskBounds.isEmpty() ? newTempTaskBounds : null /* tempTaskBounds */,
- null /* tempTaskInsetBounds */, false /* preserveWindows */,
- false /* allowResizeInDockedMode */, deferResume);
+ null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+ false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 15a9efc9ac57..2d91cadbd586 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -120,6 +120,7 @@ import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
import com.android.server.wm.StackWindowController;
import com.android.server.wm.StackWindowListener;
+import com.android.server.wm.TaskStack;
import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
@@ -544,10 +545,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mActivityContainer.mActivityDisplay.mDisplay.getSize(out);
}
- void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds, Rect outTempInsetBounds,
- boolean ignoreVisibility) {
- mWindowContainerController.getStackDockedModeBounds(outBounds, outTempBounds,
- outTempInsetBounds, ignoreVisibility);
+ /**
+ * @see ActivityStack.getStackDockedModeBounds(Rect, Rect, Rect, boolean)
+ */
+ void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
+ Rect outTempTaskBounds, boolean ignoreVisibility) {
+ mWindowContainerController.getStackDockedModeBounds(currentTempTaskBounds,
+ outStackBounds, outTempTaskBounds, ignoreVisibility);
}
void prepareFreezingTaskBounds() {
@@ -562,8 +566,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
outBounds.setEmpty();
}
- void getBoundsForNewConfiguration(Rect outBounds, Rect outTempBounds) {
- mWindowContainerController.getBoundsForNewConfiguration(outBounds, outTempBounds);
+ void getBoundsForNewConfiguration(Rect outBounds) {
+ mWindowContainerController.getBoundsForNewConfiguration(outBounds);
}
void positionChildWindowContainerAtTop(TaskRecord child) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 59f58d7e54e0..2ae815e305c1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -21,8 +21,6 @@ import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.ActivityManager.RESIZE_MODE_FORCED;
-import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
@@ -97,6 +95,7 @@ import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS
import static java.lang.Integer.MAX_VALUE;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
@@ -105,7 +104,6 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
-import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IActivityContainerCallback;
import android.app.ProfilerInfo;
@@ -179,10 +177,11 @@ import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Objects;
import java.util.Set;
public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener {
@@ -248,15 +247,31 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Force the focus to change to the stack we are moving a task to..
static final boolean FORCE_FOCUS = true;
- // Restore task from the saved recents if it can't be found in any live stack.
- static final boolean RESTORE_FROM_RECENTS = true;
-
// Don't execute any calls to resume.
static final boolean DEFER_RESUME = true;
// Used to indicate that a task is removed it should also be removed from recents.
static final boolean REMOVE_FROM_RECENTS = true;
+ /**
+ * The modes which affect which tasks are returned when calling
+ * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ MATCH_TASK_IN_STACKS_ONLY,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+ })
+ public @interface AnyTaskForIdMatchTaskMode {}
+ // Match only tasks in the current stacks
+ static final int MATCH_TASK_IN_STACKS_ONLY = 0;
+ // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
+ static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
+ // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
+ // provided stack id
+ static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
+
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
@@ -713,18 +728,26 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
TaskRecord anyTaskForIdLocked(int id) {
- return anyTaskForIdLocked(id, RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
+ INVALID_STACK_ID);
}
/**
* Returns a {@link TaskRecord} for the input id if available. Null otherwise.
* @param id Id of the task we would like returned.
- * @param restoreFromRecents If the id was not in the active list, but was found in recents,
- * restore the task from recents to the active list.
+ * @param matchMode The mode to match the given task id in.
* @param stackId The stack to restore the task to (default launch stack will be used if
- * stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}).
+ * stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}). Only
+ * valid if the matchMode is
+ * {@link #MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE}.
*/
- TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents, int stackId) {
+ TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode, int stackId) {
+ // If there is a stack id set, ensure that we are attempting to actually restore a task
+ if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE &&
+ stackId != INVALID_STACK_ID) {
+ throw new IllegalArgumentException("Should not specify stackId for non-restore lookup");
+ }
+
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -737,18 +760,23 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- // Don't give up! Look in recents.
- if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
- TaskRecord task = mRecentTasks.taskForIdLocked(id);
- if (task == null) {
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
+ // If we are matching stack tasks only, return now
+ if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
return null;
}
- if (!restoreFromRecents) {
+ // Otherwise, check the recent tasks and return if we find it there and we are not restoring
+ // the task from recents
+ if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
+ TaskRecord task = mRecentTasks.taskForIdLocked(id);
+ if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
+ if (DEBUG_RECENTS && task == null) {
+ Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
+ }
return task;
}
+ // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
if (!restoreRecentTaskLocked(task, stackId)) {
if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
"Couldn't restore task id=" + id + " found in recents");
@@ -844,7 +872,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId)
- || anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS,
+ || anyTaskForIdLocked(candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
INVALID_STACK_ID) != null) {
candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
if (candidateTaskId == currentTaskId) {
@@ -2249,7 +2277,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
final int taskId = mResizingTasksDuringAnimation.valueAt(i);
final TaskRecord task =
- anyTaskForIdLocked(taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY, INVALID_STACK_ID);
if (task != null) {
task.setTaskDockedResizing(false);
}
@@ -2375,19 +2403,19 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// static stacks need to be adjusted so they don't overlap with the docked stack.
// We get the bounds to use from window manager which has been adjusted for any
// screen controls and is also the same for all stacks.
- final Rect tempOtherTaskRect = new Rect();
- final Rect tempOtherTaskInsetRect = new Rect();
+ final Rect otherTaskRect = new Rect();
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
final ActivityStack current = getStack(i);
if (current != null && StackId.isResizeableByDockedStack(i)) {
- current.getStackDockedModeBounds(tempRect, tempOtherTaskRect,
- tempOtherTaskInsetRect, true /* ignoreVisibility */);
+ current.getStackDockedModeBounds(
+ tempOtherTaskBounds /* currentTempTaskBounds */,
+ tempRect /* outStackBounds */,
+ otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */);
+
resizeStackLocked(i, tempRect,
- !tempOtherTaskRect.isEmpty() ? tempOtherTaskRect :
- tempOtherTaskBounds,
- !tempOtherTaskInsetRect.isEmpty() ? tempOtherTaskInsetRect :
- tempOtherTaskInsetBounds,
- preserveWindows, true /* allowResizeInDockedMode */, deferResume);
+ !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
+ tempOtherTaskInsetBounds, preserveWindows,
+ true /* allowResizeInDockedMode */, deferResume);
}
}
}
@@ -2493,7 +2521,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* @return Returns true if the given task was found and removed.
*/
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
- final TaskRecord tr = anyTaskForIdLocked(taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
+ INVALID_STACK_ID);
if (tr != null) {
tr.removeTaskActivitiesLocked();
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
@@ -4784,7 +4813,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
}
- task = anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
+ task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
+ launchStackId);
if (task == null) {
continueUpdateBounds(HOME_STACK_ID);
mWindowManager.executeAppTransition();
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 9dde39e6e9ce..9deabb3d6de9 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -55,6 +55,9 @@ import java.util.Comparator;
import java.util.List;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
public class TaskPersister {
static final String TAG = "TaskPersister";
@@ -452,7 +455,8 @@ public class TaskPersister {
final int taskId = task.taskId;
if (mStackSupervisor.anyTaskForIdLocked(taskId,
- /* restoreFromRecents= */ false, HOME_STACK_ID) != null) {
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
+ INVALID_STACK_ID) != null) {
// Should not happen.
Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
} else if (userId != task.userId) {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index aa8e37742767..fc377973221b 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -109,7 +109,6 @@ public class DockedStackDividerController implements DimLayerUser {
private final Rect mTmpRect2 = new Rect();
private final Rect mTmpRect3 = new Rect();
private final Rect mLastRect = new Rect();
- private final Rect mMiddlePositionDockedStackRect = new Rect();
private boolean mLastVisibility = false;
private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
= new RemoteCallbackList<>();
@@ -186,16 +185,6 @@ public class DockedStackDividerController implements DimLayerUser {
return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
}
- /**
- * The middlePositionDockedStackRect is half the screen area that sits at the top (portrait) or
- * left (landscape).
- *
- * @return fixed rect for temp stack
- */
- Rect getMiddlePositionDockedStackRect() {
- return mMinimizedDock && isHomeStackResizable() ? mMiddlePositionDockedStackRect : null;
- }
-
void getHomeStackBoundsInDockedMode(Rect outBounds) {
final DisplayInfo di = mDisplayContent.getDisplayInfo();
mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
@@ -264,29 +253,8 @@ public class DockedStackDividerController implements DimLayerUser {
initSnapAlgorithmForRotations();
}
- /**
- * Calculates the constant rects {@link mMiddlePositionDockedStackRect} based on orientation,
- * stable insets and display size.
- */
- private void updateConstantRects() {
- final DisplayInfo di = mDisplayContent.getDisplayInfo();
- mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- mTmpRect);
- int dividerSize = mDividerWindowWidth - 2 * mDividerInsets;
- Configuration configuration = mDisplayContent.getConfiguration();
- boolean isHorizontal = configuration.orientation == Configuration.ORIENTATION_PORTRAIT;
- int middlePosition = DockedDividerUtils.calculateMiddlePosition(isHorizontal, mTmpRect,
- di.logicalWidth, di.logicalHeight, dividerSize);
- if (isHorizontal) {
- mMiddlePositionDockedStackRect.set(0, 0, di.logicalWidth, middlePosition);
- } else {
- mMiddlePositionDockedStackRect.set(0, 0, middlePosition, di.logicalHeight);
- }
- }
-
void onConfigurationChanged() {
loadDimens();
- updateConstantRects();
}
boolean isResizing() {
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index b0e115bce451..8186d30a55ff 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -199,17 +199,19 @@ public class StackWindowController
}
}
- public void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds,
- Rect outTempInsetBounds, boolean ignoreVisibility) {
+ /**
+ * @see TaskStack.getStackDockedModeBoundsLocked(Rect, Rect, Rect, boolean)
+ */
+ public void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
+ Rect outTempTaskBounds, boolean ignoreVisibility) {
synchronized (mWindowMap) {
if (mContainer != null) {
- mContainer.getStackDockedModeBoundsLocked(outBounds, outTempBounds,
- outTempInsetBounds, ignoreVisibility);
+ mContainer.getStackDockedModeBoundsLocked(currentTempTaskBounds, outStackBounds,
+ outTempTaskBounds, ignoreVisibility);
return;
}
- outBounds.setEmpty();
- outTempBounds.setEmpty();
- outTempInsetBounds.setEmpty();
+ outStackBounds.setEmpty();
+ outTempTaskBounds.setEmpty();
}
}
@@ -241,9 +243,9 @@ public class StackWindowController
}
}
- public void getBoundsForNewConfiguration(Rect outBounds, Rect outTempBounds) {
+ public void getBoundsForNewConfiguration(Rect outBounds) {
synchronized(mWindowMap) {
- mContainer.getBoundsForNewConfiguration(outBounds, outTempBounds);
+ mContainer.getBoundsForNewConfiguration(outBounds);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7588b71dced7..442cd54a2773 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -429,18 +429,9 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
return true;
}
- void getBoundsForNewConfiguration(Rect outBounds, Rect outTempBounds) {
+ void getBoundsForNewConfiguration(Rect outBounds) {
outBounds.set(mBoundsAfterRotation);
mBoundsAfterRotation.setEmpty();
- final DockedStackDividerController controller = getDisplayContent()
- .mDividerControllerLocked;
- if (mStackId == DOCKED_STACK_ID) {
- final Rect dockedStackRect = controller.getMiddlePositionDockedStackRect();
-
- if (dockedStackRect != null) {
- outTempBounds.set(dockedStackRect);
- }
- }
}
/**
@@ -686,21 +677,42 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
super.onDisplayChanged(dc);
}
- void getStackDockedModeBoundsLocked(Rect outBounds, Rect outTempBounds,
- Rect outTempInsetBounds, boolean ignoreVisibility) {
+ /**
+ * Determines the stack and task bounds of the other stack when in docked mode. The current task
+ * bounds is passed in but depending on the stack, the task and stack must match. Only in
+ * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds
+ * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds
+ * is calculated and is also used for its task bounds.
+ * If any of the out bounds are empty, it represents default bounds
+ *
+ * @param currentTempTaskBounds the current task bounds of the other stack
+ * @param outStackBounds the calculated stack bounds of the other stack
+ * @param outTempTaskBounds the calculated task bounds of the other stack
+ * @param ignoreVisibility ignore visibility in getting the stack bounds
+ */
+ void getStackDockedModeBoundsLocked(Rect currentTempTaskBounds, Rect outStackBounds,
+ Rect outTempTaskBounds, boolean ignoreVisibility) {
+ outTempTaskBounds.setEmpty();
+
+ // When the home stack is resizable, should always have the same stack and task bounds
if (mStackId == HOME_STACK_ID && findHomeTask().isResizeable()) {
// Calculate the home stack bounds when in docked mode
getDisplayContent().mDividerControllerLocked
- .getHomeStackBoundsInDockedMode(outTempBounds);
- outTempInsetBounds.set(outTempBounds);
- } else {
- outTempBounds.setEmpty();
- outTempInsetBounds.setEmpty();
+ .getHomeStackBoundsInDockedMode(outStackBounds);
+ outTempTaskBounds.set(outStackBounds);
+ return;
+ }
+
+ // When minimized state, the stack bounds for all non-home and docked stack bounds should
+ // match the passed task bounds
+ if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) {
+ outStackBounds.set(currentTempTaskBounds);
+ return;
}
if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId))
|| mDisplayContent == null) {
- outBounds.set(mBounds);
+ outStackBounds.set(mBounds);
return;
}
@@ -714,7 +726,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
// The docked stack is being dismissed, but we caught before it finished being
// dismissed. In that case we want to treat it as if it is not occupying any space and
// let others occupy the whole display.
- mDisplayContent.getLogicalDisplayRect(outBounds);
+ mDisplayContent.getLogicalDisplayRect(outStackBounds);
return;
}
@@ -722,14 +734,14 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
if (dockedSide == DOCKED_INVALID) {
// Not sure how you got here...Only thing we can do is return current bounds.
Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack);
- outBounds.set(mBounds);
+ outStackBounds.set(mBounds);
return;
}
mDisplayContent.getLogicalDisplayRect(mTmpRect);
dockedStack.getRawBounds(mTmpRect2);
final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
- getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2,
+ getStackDockedModeBounds(mTmpRect, outStackBounds, mStackId, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
}
@@ -812,8 +824,8 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
final Rect bounds = new Rect();
final Rect tempBounds = new Rect();
- final Rect tempInsetBounds = new Rect();
- getStackDockedModeBoundsLocked(bounds, tempBounds, tempInsetBounds, true /*ignoreVisibility*/);
+ getStackDockedModeBoundsLocked(null /* currentTempTaskBounds */, bounds, tempBounds,
+ true /*ignoreVisibility*/);
getController().requestResize(bounds);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 48060686a1de..7825903db4bd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -74,7 +74,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
@@ -82,7 +81,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -3839,6 +3837,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
windowInfo.title = mAttrs.accessibilityTitle;
windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
windowInfo.focused = isFocused();
+ Task task = getTask();
+ windowInfo.inPictureInPicture = (task != null) && task.inPinnedWorkspace();
if (mIsChildWindow) {
windowInfo.parentToken = getParentWindow().mClient.asBinder();
diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
index ad64e4e6e64d..e6e2cb3d99c9 100644
--- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
+++ b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
@@ -141,7 +141,7 @@ public class CompanionDeviceManagerService extends SystemService {
}
private ServiceConnection getServiceConnection(
- final AssociationRequest<?> request,
+ final AssociationRequest request,
final IFindDeviceCallback findDeviceCallback,
final String callingPackage) {
return new ServiceConnection() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
new file mode 100644
index 000000000000..8afe85389548
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2017 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 android.graphics.Region;
+import android.os.RemoteException;
+import android.view.MagnificationSpec;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+/**
+ * Tests for ActionReplacingCallback
+ */
+public class ActionReplacingCallbackTest {
+ private static final int INTERACTION_ID = 0xBEEF;
+ private static final int INTERROGATING_PID = 0xFEED;
+ private static final int APP_WINDOW_ID = 0xACE;
+ private static final int NON_ROOT_NODE_ID = 0xAAAA5555;
+ private static final long INTERROGATING_TID = 0x1234FACE;
+
+ private static final AccessibilityAction[] ACTIONS_FROM_REPLACER =
+ {ACTION_CLICK, ACTION_EXPAND};
+ private static final AccessibilityAction[] A11Y_FOCUS_ACTIONS =
+ {ACTION_ACCESSIBILITY_FOCUS, ACTION_CLEAR_ACCESSIBILITY_FOCUS};
+ // We expect both the replacer actions and a11y focus actions to appear
+ private static final AccessibilityAction[] REQUIRED_ACTIONS_ON_ROOT_TO_SERVICE =
+ {ACTION_CLICK, ACTION_EXPAND, ACTION_ACCESSIBILITY_FOCUS,
+ ACTION_CLEAR_ACCESSIBILITY_FOCUS};
+
+ @Mock IAccessibilityInteractionConnectionCallback mMockServiceCallback;
+ @Mock IAccessibilityInteractionConnection mMockReplacerConnection;
+
+ @Captor private ArgumentCaptor<Integer> mInteractionIdCaptor;
+ @Captor private ArgumentCaptor<AccessibilityNodeInfo> mInfoCaptor;
+ @Captor private ArgumentCaptor<List<AccessibilityNodeInfo>> mInfoListCaptor;
+
+ private ActionReplacingCallback mActionReplacingCallback;
+ private int mReplacerInteractionId;
+
+ @Before
+ public void setUp() throws RemoteException {
+ initMocks(this);
+ mActionReplacingCallback = new ActionReplacingCallback(
+ mMockServiceCallback, mMockReplacerConnection, INTERACTION_ID, INTERROGATING_PID,
+ INTERROGATING_TID);
+ verify(mMockReplacerConnection).findAccessibilityNodeInfoByAccessibilityId(
+ eq(AccessibilityNodeInfo.ROOT_NODE_ID), (Region) anyObject(),
+ mInteractionIdCaptor.capture(), eq(mActionReplacingCallback), eq(0),
+ eq(INTERROGATING_PID), eq(INTERROGATING_TID), (MagnificationSpec) anyObject(),
+ eq(null));
+ mReplacerInteractionId = mInteractionIdCaptor.getValue().intValue();
+ }
+
+ @Test
+ public void testConstructor_registersToGetRootNodeOfActionReplacer() throws RemoteException {
+ assertNotEquals(INTERACTION_ID, mReplacerInteractionId);
+ verifyNoMoreInteractions(mMockServiceCallback);
+ }
+
+ @Test
+ public void testCallbacks_singleRootNodeThenReplacer_returnsNodeWithReplacedActions()
+ throws RemoteException {
+ AccessibilityNodeInfo infoFromApp = AccessibilityNodeInfo.obtain();
+ infoFromApp.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID, APP_WINDOW_ID);
+ infoFromApp.addAction(ACTION_CONTEXT_CLICK);
+ mActionReplacingCallback.setFindAccessibilityNodeInfoResult(infoFromApp, INTERACTION_ID);
+ verifyNoMoreInteractions(mMockServiceCallback);
+
+ mActionReplacingCallback.setFindAccessibilityNodeInfosResult(getReplacerNodes(),
+ mReplacerInteractionId);
+
+ verify(mMockServiceCallback).setFindAccessibilityNodeInfoResult(mInfoCaptor.capture(),
+ eq(INTERACTION_ID));
+ AccessibilityNodeInfo infoSentToService = mInfoCaptor.getValue();
+ assertEquals(AccessibilityNodeInfo.ROOT_NODE_ID, infoSentToService.getSourceNodeId());
+ assertInfoHasExactlyTheseActions(infoSentToService, REQUIRED_ACTIONS_ON_ROOT_TO_SERVICE);
+ }
+
+ public void testCallbacks_singleNonrootNodeThenReplacer_returnsNodeWithNoActions()
+ throws RemoteException {
+ AccessibilityNodeInfo infoFromApp = AccessibilityNodeInfo.obtain();
+ infoFromApp.setSourceNodeId(NON_ROOT_NODE_ID, APP_WINDOW_ID);
+ infoFromApp.addAction(ACTION_CONTEXT_CLICK);
+ mActionReplacingCallback.setFindAccessibilityNodeInfoResult(infoFromApp, INTERACTION_ID);
+ verifyNoMoreInteractions(mMockServiceCallback);
+
+ mActionReplacingCallback.setFindAccessibilityNodeInfosResult(getReplacerNodes(),
+ mReplacerInteractionId);
+
+ verify(mMockServiceCallback).setFindAccessibilityNodeInfoResult(mInfoCaptor.capture(),
+ eq(INTERACTION_ID));
+ AccessibilityNodeInfo infoSentToService = mInfoCaptor.getValue();
+ assertEquals(NON_ROOT_NODE_ID, infoSentToService.getSourceNodeId());
+ assertTrue(infoSentToService.getActionList().isEmpty());
+ }
+
+ public void testCallbacks_replacerThenSingleRootNode_returnsNodeWithReplacedActions()
+ throws RemoteException {
+ mActionReplacingCallback.setFindAccessibilityNodeInfosResult(getReplacerNodes(),
+ mReplacerInteractionId);
+ verifyNoMoreInteractions(mMockServiceCallback);
+
+ AccessibilityNodeInfo infoFromApp = AccessibilityNodeInfo.obtain();
+ infoFromApp.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID, APP_WINDOW_ID);
+ infoFromApp.addAction(ACTION_CONTEXT_CLICK);
+ mActionReplacingCallback.setFindAccessibilityNodeInfoResult(infoFromApp, INTERACTION_ID);
+
+ verify(mMockServiceCallback).setFindAccessibilityNodeInfoResult(mInfoCaptor.capture(),
+ eq(INTERACTION_ID));
+ AccessibilityNodeInfo infoSentToService = mInfoCaptor.getValue();
+ assertEquals(AccessibilityNodeInfo.ROOT_NODE_ID, infoSentToService.getSourceNodeId());
+ assertInfoHasExactlyTheseActions(infoSentToService, REQUIRED_ACTIONS_ON_ROOT_TO_SERVICE);
+ }
+
+ public void testCallbacks_multipleNodesThenReplacer_clearsActionsAndAddsSomeToRoot()
+ throws RemoteException {
+ mActionReplacingCallback
+ .setFindAccessibilityNodeInfosResult(getAppNodeList(), INTERACTION_ID);
+ verifyNoMoreInteractions(mMockServiceCallback);
+
+ mActionReplacingCallback.setFindAccessibilityNodeInfosResult(getReplacerNodes(),
+ mReplacerInteractionId);
+
+ verify(mMockServiceCallback).setFindAccessibilityNodeInfosResult(mInfoListCaptor.capture(),
+ eq(INTERACTION_ID));
+ assertEquals(2, mInfoListCaptor.getValue().size());
+ AccessibilityNodeInfo rootInfoSentToService = getNodeWithIdFromList(
+ mInfoListCaptor.getValue(), AccessibilityNodeInfo.ROOT_NODE_ID);
+ AccessibilityNodeInfo otherInfoSentToService = getNodeWithIdFromList(
+ mInfoListCaptor.getValue(), NON_ROOT_NODE_ID);
+ assertInfoHasExactlyTheseActions(
+ rootInfoSentToService, REQUIRED_ACTIONS_ON_ROOT_TO_SERVICE);
+ assertTrue(otherInfoSentToService.getActionList().isEmpty());
+ }
+
+ public void testCallbacks_replacerThenMultipleNodes_clearsActionsAndAddsSomeToRoot()
+ throws RemoteException {
+ mActionReplacingCallback.setFindAccessibilityNodeInfosResult(getReplacerNodes(),
+ mReplacerInteractionId);
+ verifyNoMoreInteractions(mMockServiceCallback);
+
+ mActionReplacingCallback
+ .setFindAccessibilityNodeInfosResult(getAppNodeList(), INTERACTION_ID);
+
+ verify(mMockServiceCallback).setFindAccessibilityNodeInfosResult(mInfoListCaptor.capture(),
+ eq(INTERACTION_ID));
+ assertEquals(2, mInfoListCaptor.getValue().size());
+ AccessibilityNodeInfo rootInfoSentToService = getNodeWithIdFromList(
+ mInfoListCaptor.getValue(), AccessibilityNodeInfo.ROOT_NODE_ID);
+ AccessibilityNodeInfo otherInfoSentToService = getNodeWithIdFromList(
+ mInfoListCaptor.getValue(), NON_ROOT_NODE_ID);
+ assertInfoHasExactlyTheseActions(
+ rootInfoSentToService, REQUIRED_ACTIONS_ON_ROOT_TO_SERVICE);
+ assertTrue(otherInfoSentToService.getActionList().isEmpty());
+ }
+
+ public void testConstructor_actionReplacerThrowsException_passesDataToService()
+ throws RemoteException {
+ doThrow(RemoteException.class).when(mMockReplacerConnection)
+ .findAccessibilityNodeInfoByAccessibilityId(eq(AccessibilityNodeInfo.ROOT_NODE_ID),
+ (Region) anyObject(), mInteractionIdCaptor.capture(),
+ eq(mActionReplacingCallback), eq(0), eq(INTERROGATING_PID),
+ eq(INTERROGATING_TID), (MagnificationSpec) anyObject(), eq(null));
+ ActionReplacingCallback actionReplacingCallback = new ActionReplacingCallback(
+ mMockServiceCallback, mMockReplacerConnection, INTERACTION_ID, INTERROGATING_PID,
+ INTERROGATING_TID);
+
+ verifyNoMoreInteractions(mMockServiceCallback);
+ AccessibilityNodeInfo infoFromApp = AccessibilityNodeInfo.obtain();
+ infoFromApp.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID, APP_WINDOW_ID);
+ infoFromApp.addAction(ACTION_CONTEXT_CLICK);
+ actionReplacingCallback.setFindAccessibilityNodeInfoResult(infoFromApp, INTERACTION_ID);
+
+ verify(mMockServiceCallback).setFindAccessibilityNodeInfoResult(mInfoCaptor.capture(),
+ eq(INTERACTION_ID));
+ AccessibilityNodeInfo infoSentToService = mInfoCaptor.getValue();
+ assertEquals(AccessibilityNodeInfo.ROOT_NODE_ID, infoSentToService.getSourceNodeId());
+ assertEquals(1, infoSentToService.getActionList().size());
+ assertEquals(ACTION_CONTEXT_CLICK, infoSentToService.getActionList().get(0));
+ }
+
+ public void testSetPerformAccessibilityActionResult_actsAsPassThrough() throws RemoteException {
+ mActionReplacingCallback.setPerformAccessibilityActionResult(true, INTERACTION_ID);
+ verify(mMockServiceCallback).setPerformAccessibilityActionResult(true, INTERACTION_ID);
+ mActionReplacingCallback.setPerformAccessibilityActionResult(false, INTERACTION_ID);
+ verify(mMockServiceCallback).setPerformAccessibilityActionResult(false, INTERACTION_ID);
+ }
+
+
+ private List<AccessibilityNodeInfo> getReplacerNodes() {
+ AccessibilityNodeInfo root = AccessibilityNodeInfo.obtain();
+ root.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
+ for (AccessibilityAction action : ACTIONS_FROM_REPLACER) {
+ root.addAction(action);
+ }
+
+ // Second node should have no effect
+ AccessibilityNodeInfo other = AccessibilityNodeInfo.obtain();
+ other.setSourceNodeId(NON_ROOT_NODE_ID,
+ AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
+ other.addAction(ACTION_COLLAPSE);
+
+ return Arrays.asList(root, other);
+ }
+
+ private void assertInfoHasExactlyTheseActions(
+ AccessibilityNodeInfo info, AccessibilityAction[] actions) {
+ List<AccessibilityAction> nodeActions = info.getActionList();
+ assertEquals(new HashSet<AccessibilityAction>(nodeActions),
+ new HashSet<AccessibilityAction>(Arrays.asList(actions)));
+ }
+
+ private AccessibilityNodeInfo getNodeWithIdFromList(
+ List<AccessibilityNodeInfo> infos, long id) {
+ for (AccessibilityNodeInfo info : infos) {
+ if (info.getSourceNodeId() == id) {
+ return info;
+ }
+ }
+ assertTrue("Didn't find node", false);
+ return null;
+ }
+
+ private List<AccessibilityNodeInfo> getAppNodeList() {
+ AccessibilityNodeInfo rootInfoFromApp = AccessibilityNodeInfo.obtain();
+ rootInfoFromApp.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID, APP_WINDOW_ID);
+ rootInfoFromApp.addAction(ACTION_CONTEXT_CLICK);
+ AccessibilityNodeInfo otherInfoFromApp = AccessibilityNodeInfo.obtain();
+ otherInfoFromApp.setSourceNodeId(NON_ROOT_NODE_ID, APP_WINDOW_ID);
+ otherInfoFromApp.addAction(ACTION_CLICK);
+ return Arrays.asList(rootInfoFromApp, otherInfoFromApp);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 12495ce90cf4..ec99a9a2eb95 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -396,7 +396,7 @@ public class MotionEventInjectorTest {
hasEventTime(downTime + CONTINUED_LINE_INTERVAL)));
// Timing will restart when the gesture continues
long secondSequenceStart = events.get(2).getEventTime();
- assertTrue(secondSequenceStart > events.get(1).getEventTime());
+ assertTrue(secondSequenceStart >= events.get(1).getEventTime());
assertThat(events.get(2), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_MOVE));
assertThat(events.get(3), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_MOVE,
hasEventTime(secondSequenceStart + CONTINUED_LINE_INTERVAL)));