diff options
94 files changed, 2896 insertions, 464 deletions
diff --git a/Android.bp b/Android.bp index 834c9b08b07b..54b6619db25e 100644 --- a/Android.bp +++ b/Android.bp @@ -889,7 +889,12 @@ aidl_interface { "core/java/android/net/IIpMemoryStore.aidl", "core/java/android/net/INetworkStackConnector.aidl", "core/java/android/net/INetworkStackStatusCallback.aidl", + "core/java/android/net/IpPrefixParcelable.aidl", + "core/java/android/net/LinkAddressParcelable.aidl", + "core/java/android/net/LinkPropertiesParcelable.aidl", "core/java/android/net/PrivateDnsConfigParcel.aidl", + "core/java/android/net/ProxyInfoParcelable.aidl", + "core/java/android/net/RouteInfoParcelable.aidl", "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl", "core/java/android/net/dhcp/IDhcpServer.aidl", "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl", diff --git a/api/current.txt b/api/current.txt index 40264045d4d7..cea404f6f98c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6784,9 +6784,11 @@ package android.app.admin { method public void wipeData(int); method public void wipeData(int, java.lang.CharSequence); field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; + field public static final java.lang.String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE"; field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED"; + field public static final java.lang.String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE"; field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED"; field public static final java.lang.String ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED"; field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL"; @@ -6829,12 +6831,15 @@ package android.app.admin { field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT"; field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER"; field public static final deprecated java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS"; + field public static final java.lang.String EXTRA_PROVISIONING_IMEI = "android.app.extra.PROVISIONING_IMEI"; field public static final java.lang.String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION"; field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED"; field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE"; field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME"; field public static final java.lang.String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; field public static final java.lang.String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR"; + field public static final java.lang.String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE"; + field public static final java.lang.String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER"; field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; field public static final java.lang.String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT"; field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE"; @@ -6911,6 +6916,9 @@ package android.app.admin { field public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2; // 0x2 field public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1; // 0x1 field public static final int PRIVATE_DNS_SET_SUCCESS = 0; // 0x0 + field public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; // 0x1 + field public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; // 0x2 + field public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE = 3; // 0x3 field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2 field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1 field public static final int SKIP_SETUP_WIZARD = 1; // 0x1 @@ -24365,6 +24373,33 @@ package android.media { field public static final int AACObjectSSR = 3; // 0x3 field public static final int AACObjectScalable = 6; // 0x6 field public static final int AACObjectXHE = 42; // 0x2a + field public static final int AV1Level2 = 1; // 0x1 + field public static final int AV1Level21 = 2; // 0x2 + field public static final int AV1Level22 = 4; // 0x4 + field public static final int AV1Level23 = 8; // 0x8 + field public static final int AV1Level3 = 16; // 0x10 + field public static final int AV1Level31 = 32; // 0x20 + field public static final int AV1Level32 = 64; // 0x40 + field public static final int AV1Level33 = 128; // 0x80 + field public static final int AV1Level4 = 256; // 0x100 + field public static final int AV1Level41 = 512; // 0x200 + field public static final int AV1Level42 = 1024; // 0x400 + field public static final int AV1Level43 = 2048; // 0x800 + field public static final int AV1Level5 = 4096; // 0x1000 + field public static final int AV1Level51 = 8192; // 0x2000 + field public static final int AV1Level52 = 16384; // 0x4000 + field public static final int AV1Level53 = 32768; // 0x8000 + field public static final int AV1Level6 = 65536; // 0x10000 + field public static final int AV1Level61 = 131072; // 0x20000 + field public static final int AV1Level62 = 262144; // 0x40000 + field public static final int AV1Level63 = 524288; // 0x80000 + field public static final int AV1Level7 = 1048576; // 0x100000 + field public static final int AV1Level71 = 2097152; // 0x200000 + field public static final int AV1Level72 = 4194304; // 0x400000 + field public static final int AV1Level73 = 8388608; // 0x800000 + field public static final int AV1Profile0 = 1; // 0x1 + field public static final int AV1Profile1 = 2; // 0x2 + field public static final int AV1Profile2 = 4; // 0x4 field public static final int AVCLevel1 = 1; // 0x1 field public static final int AVCLevel11 = 4; // 0x4 field public static final int AVCLevel12 = 8; // 0x8 @@ -24382,6 +24417,9 @@ package android.media { field public static final int AVCLevel5 = 16384; // 0x4000 field public static final int AVCLevel51 = 32768; // 0x8000 field public static final int AVCLevel52 = 65536; // 0x10000 + field public static final int AVCLevel6 = 131072; // 0x20000 + field public static final int AVCLevel61 = 262144; // 0x40000 + field public static final int AVCLevel62 = 524288; // 0x80000 field public static final int AVCProfileBaseline = 1; // 0x1 field public static final int AVCProfileConstrainedBaseline = 65536; // 0x10000 field public static final int AVCProfileConstrainedHigh = 524288; // 0x80000 @@ -25002,6 +25040,7 @@ package android.media { field public static final java.lang.String MIMETYPE_TEXT_CEA_708 = "text/cea-708"; field public static final java.lang.String MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt"; + field public static final java.lang.String MIMETYPE_VIDEO_AV1 = "video/av01"; field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc"; field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision"; field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp"; @@ -41739,6 +41778,7 @@ package android.service.voice { field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10 field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8 field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4 + field public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 128; // 0x80 field public static final int SHOW_SOURCE_NOTIFICATION = 64; // 0x40 field public static final int SHOW_SOURCE_PUSH_TO_TALK = 32; // 0x20 field public static final int SHOW_WITH_ASSIST = 1; // 0x1 @@ -54244,6 +54284,8 @@ package android.webkit { method public static java.lang.ClassLoader getWebViewClassLoader(); method public android.webkit.WebViewClient getWebViewClient(); method public android.os.Looper getWebViewLooper(); + method public android.webkit.WebViewRenderer getWebViewRenderer(); + method public android.webkit.WebViewRendererClient getWebViewRendererClient(); method public void goBack(); method public void goBackOrForward(int); method public void goForward(); @@ -54293,6 +54335,8 @@ package android.webkit { method public void setWebChromeClient(android.webkit.WebChromeClient); method public static void setWebContentsDebuggingEnabled(boolean); method public void setWebViewClient(android.webkit.WebViewClient); + method public void setWebViewRendererClient(java.util.concurrent.Executor, android.webkit.WebViewRendererClient); + method public void setWebViewRendererClient(android.webkit.WebViewRendererClient); method public deprecated boolean shouldDelayChildPressedState(); method public deprecated boolean showFindDialog(java.lang.String, boolean); method public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>); @@ -54408,6 +54452,16 @@ package android.webkit { method public android.webkit.WebView getWebView(); } + public abstract class WebViewRenderer { + method public abstract boolean terminate(); + } + + public abstract class WebViewRendererClient { + ctor public WebViewRendererClient(); + method public abstract void onRendererResponsive(android.webkit.WebView, android.webkit.WebViewRenderer); + method public abstract void onRendererUnresponsive(android.webkit.WebView, android.webkit.WebViewRenderer); + } + } package android.widget { diff --git a/api/system-current.txt b/api/system-current.txt index acbc2a72241f..7ea7afbb8d20 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -128,6 +128,7 @@ package android { field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES"; field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; field public static final java.lang.String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS"; + field public static final java.lang.String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG"; field public static final java.lang.String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE"; field public static final java.lang.String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS"; field public static final java.lang.String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY"; @@ -184,6 +185,7 @@ package android { field public static final java.lang.String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES"; field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY"; field public static final java.lang.String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; + field public static final java.lang.String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG"; field public static final java.lang.String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE"; field public static final java.lang.String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"; field public static final java.lang.String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE"; @@ -1559,6 +1561,7 @@ package android.content.pm { public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { field public static final int FLAG_REMOVED = 2; // 0x2 + field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000 field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000 field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 @@ -8500,6 +8503,8 @@ package android.webkit { method public abstract int getVisibleTitleHeight(); method public abstract android.webkit.WebChromeClient getWebChromeClient(); method public abstract android.webkit.WebViewClient getWebViewClient(); + method public abstract android.webkit.WebViewRenderer getWebViewRenderer(); + method public abstract android.webkit.WebViewRendererClient getWebViewRendererClient(); method public abstract android.view.View getZoomControls(); method public abstract void goBack(); method public abstract void goBackOrForward(int); @@ -8549,6 +8554,7 @@ package android.webkit { method public abstract void setVerticalScrollbarOverlay(boolean); method public abstract void setWebChromeClient(android.webkit.WebChromeClient); method public abstract void setWebViewClient(android.webkit.WebViewClient); + method public abstract void setWebViewRendererClient(java.util.concurrent.Executor, android.webkit.WebViewRendererClient); method public abstract boolean showFindDialog(java.lang.String, boolean); method public abstract void stopLoading(); method public abstract boolean zoomBy(float); diff --git a/api/test-current.txt b/api/test-current.txt index 6ddb341191cb..846442dbe031 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -420,6 +420,7 @@ package android.content.pm { } public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000 field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000 field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9247486dff40..6d7c547f7cf1 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2168,6 +2168,74 @@ public class DevicePolicyManager { public @interface SetPrivateDnsModeResultConstants {} /** + * Activity action: Starts the administrator to get the mode for the provisioning. + * This intent may contain the following extras: + * <ul> + * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}</li> + * <li>{@link #EXTRA_PROVISIONING_IMEI}</li> + * <li>{@link #EXTRA_PROVISIONING_SERIAL_NUMBER}</li> + * </ul> + * + * <p>The target activity should return one of the following values in + * {@link #EXTRA_PROVISIONING_MODE} as result: + * <ul> + * <li>{@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}</li> + * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li> + * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE}</li> + * </ul> + * + * <p>The target activity may also return the account that needs to be migrated from primary + * user to managed profile in case of a profile owner provisioning in + * {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} as result. + */ + public static final String ACTION_GET_PROVISIONING_MODE = + "android.app.action.GET_PROVISIONING_MODE"; + + /** + * A string extra holding the IMEI (International Mobile Equipment Identity) of the device. + */ + public static final String EXTRA_PROVISIONING_IMEI = "android.app.extra.PROVISIONING_IMEI"; + + /** + * A string extra holding the serial number of the device. + */ + public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = + "android.app.extra.PROVISIONING_SERIAL_NUMBER"; + + /** + * An intent extra holding the provisioning mode returned by the administrator. + * The value for this extra should be one of the following: + * <ul> + * <li>{@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}</li> + * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li> + * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE}</li> + * </ul> + */ + public static final String EXTRA_PROVISIONING_MODE = + "android.app.extra.PROVISIONING_MODE"; + + /** + * The provisioning mode for fully managed device. + */ + public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; + + /** + * The provisioning mode for managed profile. + */ + public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; + + /** + * The provisioning mode for managed profile on a fully managed device. + */ + public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE = 3; + + /** + * Activity action: Starts the administrator to show policy compliance for the provisioning. + */ + public static final String ACTION_ADMIN_POLICY_COMPLIANCE = + "android.app.action.ADMIN_POLICY_COMPLIANCE"; + + /** * Return true if the given administrator component is currently active (enabled) in the system. * * @param admin The administrator component to check for. diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 45b5dcac6cc0..359adbdb6e24 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -55,6 +55,7 @@ public abstract class PackageManagerInternal { public static final int PACKAGE_PERMISSION_CONTROLLER = 6; public static final int PACKAGE_WELLBEING = 7; public static final int PACKAGE_DOCUMENTER = 8; + public static final int PACKAGE_CONFIGURATOR = 9; @IntDef(value = { PACKAGE_SYSTEM, PACKAGE_SETUP_WIZARD, @@ -65,6 +66,7 @@ public abstract class PackageManagerInternal { PACKAGE_PERMISSION_CONTROLLER, PACKAGE_WELLBEING, PACKAGE_DOCUMENTER, + PACKAGE_CONFIGURATOR, }) @Retention(RetentionPolicy.SOURCE) public @interface KnownPackage {} diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index a3395ac9fd13..5d2cf0a7d101 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -203,6 +203,16 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { @TestApi public static final int PROTECTION_FLAG_DOCUMENTER = 0x40000; + /** + * Additional flag for {@link #protectionLevel}, corresponding to the + * {@code configurator} value of {@link android.R.attr#protectionLevel}. + * + * @hide + */ + @SystemApi + @TestApi + public static final int PROTECTION_FLAG_CONFIGURATOR = 0x80000; + /** @hide */ @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = { @@ -222,6 +232,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER, PROTECTION_FLAG_WELLBEING, PROTECTION_FLAG_DOCUMENTER, + PROTECTION_FLAG_CONFIGURATOR, }) @Retention(RetentionPolicy.SOURCE) public @interface ProtectionFlags {} @@ -417,6 +428,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) { protLevel += "|documenter"; } + if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) { + protLevel += "|configurator"; + } return protLevel; } diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index a7734f544607..a98b31ad6a5e 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -55,6 +55,10 @@ public final class HdmiControlManager { @Nullable private final IHdmiControlService mService; + private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF; + + private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS; + /** * Broadcast Action: Display OSD message. * <p>Send when the service has a message to display on screen for events @@ -505,14 +509,41 @@ public final class HdmiControlManager { * @hide */ public int getPhysicalAddress() { + if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) { + return mPhysicalAddress; + } try { - return mService.getPhysicalAddress(); + mPhysicalAddress = mService.getPhysicalAddress(); + return mPhysicalAddress; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Check if the target device is connected to the current device. The + * API also returns true if the current device is the target. + * + * @param targetDevice {@link HdmiDeviceInfo} of the target device. + * @return true if {@code device} is directly or indirectly connected to the + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public boolean isTargetDeviceConnected(HdmiDeviceInfo targetDevice) { + mPhysicalAddress = getPhysicalAddress(); + if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { + return false; + } + int targetPhysicalAddress = targetDevice.getPhysicalAddress(); + if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { + return false; + } + return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress) + != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE; + } + + /** * Listener used to get hotplug event from HDMI port. */ public interface HotplugEventListener { diff --git a/core/java/android/hardware/hdmi/HdmiUtils.java b/core/java/android/hardware/hdmi/HdmiUtils.java new file mode 100644 index 000000000000..308173816f13 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014 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.hardware.hdmi; + +/** + * Various utilities to handle HDMI CEC messages. + * + * TODO(b/110094868): unhide for Q + * @hide + */ +public class HdmiUtils { + + /** + * Return value of {@link #getLocalPortFromPhysicalAddress(int, int)} + */ + static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1; + static final int TARGET_SAME_PHYSICAL_ADDRESS = 0; + + private HdmiUtils() { /* cannot be instantiated */ } + + /** + * Method to parse target physical address to the port number on the current device. + * + * <p>This check assumes target address is valid. + * + * @param targetPhysicalAddress is the physical address of the target device + * @param myPhysicalAddress is the physical address of the current device + * @return + * If the target device is under the current device, return the port number of current device + * that the target device is connected to. This also applies to the devices that are indirectly + * connected to the current device. + * + * <p>If the target device has the same physical address as the current device, return + * {@link #TARGET_SAME_PHYSICAL_ADDRESS}. + * + * <p>If the target device is not under the current device, return + * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}. + */ + public static int getLocalPortFromPhysicalAddress( + int targetPhysicalAddress, int myPhysicalAddress) { + if (myPhysicalAddress == targetPhysicalAddress) { + return TARGET_SAME_PHYSICAL_ADDRESS; + } + + int mask = 0xF000; + int finalMask = 0xF000; + int maskedAddress = myPhysicalAddress; + + while (maskedAddress != 0) { + maskedAddress = myPhysicalAddress & mask; + finalMask |= mask; + mask >>= 4; + } + + int portAddress = targetPhysicalAddress & finalMask; + if ((portAddress & (finalMask << 4)) != myPhysicalAddress) { + return TARGET_NOT_UNDER_LOCAL_DEVICE; + } + + mask <<= 4; + int port = portAddress & mask; + while ((port >> 4) != 0) { + port >>= 4; + } + return port; + } +} diff --git a/core/java/android/net/IpPrefixParcelable.aidl b/core/java/android/net/IpPrefixParcelable.aidl new file mode 100644 index 000000000000..93a8d41936cc --- /dev/null +++ b/core/java/android/net/IpPrefixParcelable.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package android.net; + +parcelable IpPrefixParcelable { + String address; + int prefixLength; +}
\ No newline at end of file diff --git a/core/java/android/net/LinkAddressParcelable.aidl b/core/java/android/net/LinkAddressParcelable.aidl new file mode 100644 index 000000000000..af8e79b21f69 --- /dev/null +++ b/core/java/android/net/LinkAddressParcelable.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package android.net; + +parcelable LinkAddressParcelable { + String address; + int prefixLength; + int flags; + int scope; +}
\ No newline at end of file diff --git a/core/java/android/net/LinkPropertiesParcelable.aidl b/core/java/android/net/LinkPropertiesParcelable.aidl new file mode 100644 index 000000000000..b153dc70e1b8 --- /dev/null +++ b/core/java/android/net/LinkPropertiesParcelable.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package android.net; + +import android.net.IpPrefixParcelable; +import android.net.LinkAddressParcelable; +import android.net.ProxyInfoParcelable; +import android.net.RouteInfoParcelable; + +parcelable LinkPropertiesParcelable { + String ifaceName; + LinkAddressParcelable[] linkAddresses; + String[] dnses; + String[] pcscfs; + String[] validatedPrivateDnses; + boolean usePrivateDns; + String privateDnsServerName; + String domains; + RouteInfoParcelable[] routes; + ProxyInfoParcelable httpProxy; + int mtu; + String tcpBufferSizes; + IpPrefixParcelable nat64Prefix; + LinkPropertiesParcelable[] stackedLinks; +}
\ No newline at end of file diff --git a/core/java/android/net/ProxyInfoParcelable.aidl b/core/java/android/net/ProxyInfoParcelable.aidl new file mode 100644 index 000000000000..59fd8467b820 --- /dev/null +++ b/core/java/android/net/ProxyInfoParcelable.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package android.net; + +parcelable ProxyInfoParcelable { + String host; + int port; + String[] exclusionList; + String pacFileUrl; +} diff --git a/core/java/android/net/RouteInfoParcelable.aidl b/core/java/android/net/RouteInfoParcelable.aidl new file mode 100644 index 000000000000..15bcdcfc2000 --- /dev/null +++ b/core/java/android/net/RouteInfoParcelable.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package android.net; + +import android.net.IpPrefixParcelable; + +parcelable RouteInfoParcelable { + IpPrefixParcelable destination; + String gatewayAddr; + String ifaceName; + int type; +} diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index e4622dbdc379..f51ba9a41a2b 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -306,9 +306,18 @@ public class GraphicsEnvironment { String packageName, String paths, String devOptIn) { - // Check for temporary rules if debuggable or root - if (!isDebuggable(context) && !(getCanLoadSystemLibraries() == 1)) { - Log.v(TAG, "Skipping loading temporary rules file"); + /** + * We only want to load a temp rules file for: + * - apps that are marked 'debuggable' in their manifest + * - devices that are running a userdebug build (ro.debuggable) or can inject libraries for + * debugging (PR_SET_DUMPABLE). + */ + boolean appIsDebuggable = isDebuggable(context); + boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1; + if (!(appIsDebuggable || deviceIsDebuggable)) { + Log.v(TAG, "Skipping loading temporary rules file: " + + "appIsDebuggable = " + appIsDebuggable + ", " + + "adbRootEnabled = " + deviceIsDebuggable); return false; } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index c79589577366..44cb2212cd0c 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -16,9 +16,13 @@ package android.provider; +import static android.Manifest.permission.READ_DEVICE_CONFIG; +import static android.Manifest.permission.WRITE_DEVICE_CONFIG; + import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityThread; import android.content.ContentResolver; @@ -78,6 +82,7 @@ public final class DeviceConfig { * @hide */ @SystemApi + @RequiresPermission(READ_DEVICE_CONFIG) public static String getProperty(String namespace, String name) { ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); String compositeName = createCompositeName(namespace, name); @@ -105,6 +110,7 @@ public final class DeviceConfig { * @hide */ @SystemApi + @RequiresPermission(WRITE_DEVICE_CONFIG) public static boolean setProperty( String namespace, String name, String value, boolean makeDefault) { ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); @@ -125,6 +131,7 @@ public final class DeviceConfig { * @hide */ @SystemApi + @RequiresPermission(WRITE_DEVICE_CONFIG) public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) { ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); Settings.Config.resetToDefaults(contentResolver, resetMode, namespace); @@ -146,10 +153,12 @@ public final class DeviceConfig { * @hide */ @SystemApi + @RequiresPermission(READ_DEVICE_CONFIG) public static void addOnPropertyChangedListener( @NonNull String namespace, @NonNull @CallbackExecutor Executor executor, @NonNull OnPropertyChangedListener onPropertyChangedListener) { + // TODO enforce READ_DEVICE_CONFIG permission synchronized (sLock) { Pair<String, Executor> oldNamespace = sListeners.get(onPropertyChangedListener); if (oldNamespace == null) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 73e90d6ee628..b39d871c8637 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7732,6 +7732,23 @@ public final class Settings { public static final String TV_INPUT_CUSTOM_LABELS = "tv_input_custom_labels"; /** + * Whether TV app uses non-system inputs. + * + * <p> + * The value is boolean (1 or 0), where 1 means non-system TV inputs are allowed, + * and 0 means non-system TV inputs are not allowed. + * + * <p> + * Devices such as sound bars may have changed the system property allow_third_party_inputs + * to false so the TV Application only uses HDMI and other built in inputs. This setting + * allows user to override the default and have the TV Application use third party TV inputs + * available on play store. + * + * @hide + */ + public static final String TV_APP_USES_NON_SYSTEM_INPUTS = "tv_app_uses_non_system_inputs"; + + /** * Whether automatic routing of system audio to USB audio peripheral is disabled. * The value is boolean (1 or 0), where 1 means automatic routing is disabled, * and 0 means automatic routing is enabled. @@ -14109,7 +14126,7 @@ public final class Settings { * * @hide */ - // TODO(b/117663715): require a new read permission + @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG) static String getString(ContentResolver resolver, String name) { return sNameValueCache.getStringForUser(resolver, name, resolver.getUserId()); } @@ -14132,8 +14149,7 @@ public final class Settings { * * @hide */ - // TODO(b/117663715): require a new write permission restricted to a single source - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) static boolean putString(@NonNull ContentResolver resolver, @NonNull String name, @Nullable String value, boolean makeDefault) { return sNameValueCache.putStringForUser(resolver, name, value, null, makeDefault, @@ -14155,7 +14171,7 @@ public final class Settings { * @hide */ // TODO(b/117663715): require a new write permission restricted to a single source - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode, @Nullable String prefix) { try { diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 163e3d5fab56..6f274477431f 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -121,6 +121,12 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall */ public static final int SHOW_SOURCE_NOTIFICATION = 1 << 6; + /** + * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked + * from an Android automotive system Ui. + */ + public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 1 << 7; + final Context mContext; final HandlerCaller mHandlerCaller; diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index bad2dbfebdc5..60393502bbe7 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -16,6 +16,7 @@ package android.webkit; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -69,6 +70,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; /** * A View that displays web pages. @@ -1688,6 +1690,84 @@ public class WebView extends AbsoluteLayout return mProvider.getWebViewClient(); } + + /** + * Gets the WebView renderer associated with this WebView. + * + * <p>In {@link android.os.Build.VERSION_CODES#O} and above, WebView may + * run in "multiprocess" mode. In multiprocess mode, rendering of web + * content is performed by a sandboxed renderer process separate to the + * application process. This renderer process may be shared with other + * WebViews in the application, but is not shared with other application + * processes. + * + * <p>If WebView is running in multiprocess mode, this method returns a + * handle to the renderer process associated with the WebView, which can + * be used to control the renderer process. + * + * @return the {@link WebViewRenderer} renderer handle associated + * with this {@link WebView}, or {@code null} if + * WebView is not runing in multiprocess mode. + */ + @Nullable + public WebViewRenderer getWebViewRenderer() { + checkThread(); + return mProvider.getWebViewRenderer(); + } + + /** + * Sets the renderer client object associated with this WebView. + * + * <p>The renderer client encapsulates callbacks relevant to WebView renderer + * state. See {@link WebViewRendererClient} for details. + * + * <p>Although many WebView instances may share a single underlying + * renderer, and renderers may live either in the application + * process, or in a sandboxed process that is isolated from the + * application process, instances of {@link WebViewRendererClient} + * are set per-WebView. Callbacks represent renderer events from + * the perspective of this WebView, and may or may not be correlated + * with renderer events affecting other WebViews. + * + * @param executor the Executor on which {@link WebViewRendererClient} callbacks will execute. + * @param webViewRendererClient the {@link WebViewRendererClient} object. + */ + public void setWebViewRendererClient( + @NonNull @CallbackExecutor Executor executor, + @NonNull WebViewRendererClient webViewRendererClient) { + checkThread(); + mProvider.setWebViewRendererClient(executor, webViewRendererClient); + } + + /** + * Sets the renderer client object associated with this WebView. + * + * See {@link #setWebViewRendererClient(Executor,WebViewRendererClient)} for details. + * + * <p> {@link WebViewRendererClient} callbacks will run on the thread that this WebView was + * initialized on. + * + * @param webViewRendererClient the {@link WebViewRendererClient} object. + */ + public void setWebViewRendererClient( + @Nullable WebViewRendererClient webViewRendererClient) { + checkThread(); + mProvider.setWebViewRendererClient(null, webViewRendererClient); + } + + /** + * Gets the renderer client object associated with this WebView. + * + * @return the {@link WebViewRendererClient} object associated with this WebView, if one has + * been set via {@link #setWebViewRendererClient(WebViewRendererClient)} or {@code null} + * otherwise. + */ + @Nullable + public WebViewRendererClient getWebViewRendererClient() { + checkThread(); + return mProvider.getWebViewRendererClient(); + } + /** * Registers the interface to be used when content can not be handled by * the rendering engine, and should be downloaded instead. This will replace diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index 95e7a986efd2..baf582693bd8 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -54,6 +54,7 @@ import android.webkit.WebView.VisualStateCallback; import java.io.BufferedWriter; import java.io.File; import java.util.Map; +import java.util.concurrent.Executor; /** * WebView backend provider interface: this interface is the abstract backend to a WebView @@ -237,6 +238,14 @@ public interface WebViewProvider { public WebViewClient getWebViewClient(); + public WebViewRenderer getWebViewRenderer(); + + public void setWebViewRendererClient( + @Nullable Executor executor, + @Nullable WebViewRendererClient client); + + public WebViewRendererClient getWebViewRendererClient(); + public void setDownloadListener(DownloadListener listener); public void setWebChromeClient(WebChromeClient client); diff --git a/core/java/android/webkit/WebViewRenderer.java b/core/java/android/webkit/WebViewRenderer.java new file mode 100644 index 000000000000..532825485ed3 --- /dev/null +++ b/core/java/android/webkit/WebViewRenderer.java @@ -0,0 +1,45 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +/** + * WebViewRenderer provides an opaque handle to a {@link WebView} renderer. + */ +public abstract class WebViewRenderer { + /** + * Cause this renderer to terminate. + * + * <p>Calling this on a not yet started, or an already terminated renderer will have no effect. + * + * <p>Terminating a renderer process may have an effect on multiple {@link WebView} instances. + * + * <p>Renderer termination must be handled by properly overriding + * {@link WebViewClient#onRenderProcessGone} for every WebView that shares this + * renderer. If termination is not handled by all associated WebViews, then the application + * process will also be terminated. + * + * @return {@code true} if it was possible to terminate this renderer, {@code false} otherwise. + */ + public abstract boolean terminate(); + + /** + * This class cannot be created by applications. + * @hide + */ + public WebViewRenderer() { + } +} diff --git a/core/java/android/webkit/WebViewRendererClient.java b/core/java/android/webkit/WebViewRendererClient.java new file mode 100644 index 000000000000..2fadf54fd434 --- /dev/null +++ b/core/java/android/webkit/WebViewRendererClient.java @@ -0,0 +1,77 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * Used to receive callbacks on {@link WebView} renderer events. + * + * WebViewRendererClient instances may be set or retrieved via {@link + * WebView#setWebViewRendererClient(WebViewRendererClient)} and {@link + * WebView#getWebViewRendererClient()}. + * + * Instances may be attached to multiple WebViews, and thus a single renderer event may cause + * a callback to be called multiple times with different WebView parameters. + */ +public abstract class WebViewRendererClient { + /** + * Called when the renderer currently associated with {@code view} becomes unresponsive as a + * result of a long running blocking task such as the execution of JavaScript. + * + * <p>If a WebView fails to process an input event, or successfully navigate to a new URL within + * a reasonable time frame, the renderer is considered to be unresponsive, and this callback + * will be called. + * + * <p>This callback will continue to be called at regular intervals as long as the renderer + * remains unresponsive. If the renderer becomes responsive again, {@link + * WebViewRendererClient#onRendererResponsive} will be called once, and this method will not + * subsequently be called unless another period of unresponsiveness is detected. + * + * <p>No action is taken by WebView as a result of this method call. Applications may + * choose to terminate the associated renderer via the object that is passed to this callback, + * if in multiprocess mode, however this must be accompanied by correctly handling + * {@link WebViewClient#onRenderProcessGone} for this WebView, and all other WebViews associated + * with the same renderer. Failure to do so will result in application termination. + * + * @param view The {@link WebView} for which unresponsiveness was detected. + * @param renderer The {@link WebViewRenderer} that has become unresponsive, + * or {@code null} if WebView is running in single process mode. + */ + public abstract void onRendererUnresponsive( + @NonNull WebView view, @Nullable WebViewRenderer renderer); + + /** + * Called once when an unresponsive renderer currently associated with {@code view} becomes + * responsive. + * + * <p>After a WebView renderer becomes unresponsive, which is notified to the application by + * {@link WebViewRendererClient#onRendererUnresponsive}, it is possible for the blocking + * renderer task to complete, returning the renderer to a responsive state. In that case, + * this method is called once to indicate responsiveness. + * + * <p>No action is taken by WebView as a result of this method call. + * + * @param view The {@link WebView} for which responsiveness was detected. + * + * @param renderer The {@link WebViewRenderer} that has become responsive, or {@code null} if + * WebView is running in single process mode. + */ + public abstract void onRendererResponsive( + @NonNull WebView view, @Nullable WebViewRenderer renderer); +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7813128d46c7..cf1f7bb2aac1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2532,6 +2532,16 @@ <permission android:name="android.permission.WRITE_GSERVICES" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide Allows an application to modify config settings. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.WRITE_DEVICE_CONFIG" + android:protectionLevel="signature|configurator"/> + + <!-- @SystemApi @hide Allows an application to read config settings. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.READ_DEVICE_CONFIG" + android:protectionLevel="signature|preinstalled" /> + <!-- @SystemApi @TestApi Allows an application to call {@link android.app.ActivityManager#forceStopPackage}. @hide --> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 18a42bc5ed0b..b15f72efd717 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -274,6 +274,9 @@ <!-- Additional flag from base permission type: this permission can be automatically granted to the document manager --> <flag name="documenter" value="0x40000" /> + <!-- Additional flag from base permission type: this permission automatically + granted to device configurator --> + <flag name="configurator" value="0x80000" /> </attr> <!-- Flags indicating more context for a permission group. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9a1360b18be8..aa1364f4a24e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2805,6 +2805,9 @@ <!-- Flag indicating which package name can access the persistent data partition --> <string name="config_persistentDataPackageName" translatable="false"></string> + <!-- Flag indicating which package name can access DeviceConfig table --> + <string name="config_deviceConfiguratorPackageName" translatable="false"></string> + <!-- Flag indicating apps will skip sending hold request before merge. In this case IMS service implementation will do both.i.e.hold followed by merge. --> <bool name="skipHoldBeforeMerge">true</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cd1ee2b0166d..eb0a7a1d8b10 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2140,6 +2140,7 @@ <java-symbol type="string" name="config_carrierAppInstallDialogComponent" /> <java-symbol type="string" name="config_defaultNetworkScorerPackageName" /> <java-symbol type="string" name="config_persistentDataPackageName" /> + <java-symbol type="string" name="config_deviceConfiguratorPackageName" /> <java-symbol type="layout" name="resolver_list" /> <java-symbol type="id" name="resolver_list" /> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index e80cb6d0295b..86818c611c1f 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -56,6 +56,7 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INJECT_EVENTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_DREAM_STATE" /> <uses-permission android:name="android.permission.WRITE_DREAM_STATE" /> <uses-permission android:name="android.permission.READ_LOGS"/> @@ -65,6 +66,7 @@ <uses-permission android:name="android.permission.USE_CREDENTIALS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> diff --git a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java new file mode 100644 index 000000000000..16be0b0a27c1 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.hdmi; + +import static com.google.common.truth.Truth.assertThat; + +import android.support.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@SmallTest +@RunWith(JUnit4.class) +/** Tests for {@link HdmiUtils} class. */ +public class HdmiUtilsTest { + + @Test + public void pathToPort_isMe() { + int targetPhysicalAddress = 0x1000; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_SAME_PHYSICAL_ADDRESS); + } + + @Test + public void pathToPort_isDirectlyBelow() { + int targetPhysicalAddress = 0x1100; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1); + } + + @Test + public void pathToPort_isBelow() { + int targetPhysicalAddress = 0x1110; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1); + } + + @Test + public void pathToPort_neitherMeNorBelow() { + int targetPhysicalAddress = 0x3000; + int myPhysicalAddress = 0x2000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2200; + myPhysicalAddress = 0x3300; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2213; + myPhysicalAddress = 0x2212; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2340; + myPhysicalAddress = 0x2310; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + } +} diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 13e548b2e688..93af0130d74c 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -668,6 +668,7 @@ public class SettingsBackupTest { Settings.Secure.SMS_DEFAULT_APPLICATION, Settings.Secure.SPELL_CHECKER_ENABLED, // Intentionally removed in Q Settings.Secure.TRUST_AGENTS_INITIALIZED, + Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS, Settings.Secure.TV_INPUT_CUSTOM_LABELS, Settings.Secure.TV_INPUT_HIDDEN_INPUTS, Settings.Secure.TV_USER_SETUP_COMPLETE, diff --git a/media/Android.bp b/media/Android.bp index d5da6f266952..8ebc91a06f7a 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -1,4 +1,21 @@ java_library { + name: "media1", + + srcs: [ + ":media1-srcs", + ], + + sdk_version: "system_current", +} + +filegroup { + name: "media1-srcs", + srcs: [ + "java/android/media/session/MediaSessionProviderService.java", + ], +} + +java_library { // TODO: include media2.jar in the media apex and add it to the bootclasspath. name: "media2", diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 10a1e3a72225..01def960189a 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -163,7 +163,7 @@ public final class MediaCodecInfo { // such as B-frame support, arithmetic coding... public CodecProfileLevel[] profileLevels; // NOTE this array is modifiable by user - // from OMX_COLOR_FORMATTYPE + // from MediaCodecConstants /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */ public static final int COLOR_FormatMonochrome = 1; /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */ @@ -344,7 +344,7 @@ public final class MediaCodecInfo { /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */ public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100; // COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference. - // In OMX this is called OMX_COLOR_FormatAndroidOpaque. + // Note: in OMX this is called OMX_COLOR_FormatAndroidOpaque. public static final int COLOR_FormatSurface = 0x7F000789; /** @@ -435,8 +435,7 @@ public final class MediaCodecInfo { public static final int COLOR_QCOM_FormatYUV420SemiPlanar = 0x7fa30c00; /** - * Defined in the OpenMAX IL specs, color format values are drawn from - * OMX_COLOR_FORMATTYPE. + * The color format for the media. This is one of the color constants defined in this class. */ public int[] colorFormats; // NOTE this array is modifiable by user @@ -2879,7 +2878,9 @@ public final class MediaCodecInfo { * {@link MediaCodecInfo.CodecCapabilities#profileLevels} field. */ public static final class CodecProfileLevel { - // from OMX_VIDEO_AVCPROFILETYPE + // These constants were originally in-line with OMX values, but this + // correspondence is no longer maintained. + public static final int AVCProfileBaseline = 0x01; public static final int AVCProfileMain = 0x02; public static final int AVCProfileExtended = 0x04; @@ -2890,7 +2891,6 @@ public final class MediaCodecInfo { public static final int AVCProfileConstrainedBaseline = 0x10000; public static final int AVCProfileConstrainedHigh = 0x80000; - // from OMX_VIDEO_AVCLEVELTYPE public static final int AVCLevel1 = 0x01; public static final int AVCLevel1b = 0x02; public static final int AVCLevel11 = 0x04; @@ -2908,8 +2908,10 @@ public final class MediaCodecInfo { public static final int AVCLevel5 = 0x4000; public static final int AVCLevel51 = 0x8000; public static final int AVCLevel52 = 0x10000; + public static final int AVCLevel6 = 0x20000; + public static final int AVCLevel61 = 0x40000; + public static final int AVCLevel62 = 0x80000; - // from OMX_VIDEO_H263PROFILETYPE public static final int H263ProfileBaseline = 0x01; public static final int H263ProfileH320Coding = 0x02; public static final int H263ProfileBackwardCompatible = 0x04; @@ -2920,7 +2922,6 @@ public final class MediaCodecInfo { public static final int H263ProfileInterlace = 0x80; public static final int H263ProfileHighLatency = 0x100; - // from OMX_VIDEO_H263LEVELTYPE public static final int H263Level10 = 0x01; public static final int H263Level20 = 0x02; public static final int H263Level30 = 0x04; @@ -2930,7 +2931,6 @@ public final class MediaCodecInfo { public static final int H263Level60 = 0x40; public static final int H263Level70 = 0x80; - // from OMX_VIDEO_MPEG4PROFILETYPE public static final int MPEG4ProfileSimple = 0x01; public static final int MPEG4ProfileSimpleScalable = 0x02; public static final int MPEG4ProfileCore = 0x04; @@ -2948,7 +2948,6 @@ public final class MediaCodecInfo { public static final int MPEG4ProfileAdvancedScalable = 0x4000; public static final int MPEG4ProfileAdvancedSimple = 0x8000; - // from OMX_VIDEO_MPEG4LEVELTYPE public static final int MPEG4Level0 = 0x01; public static final int MPEG4Level0b = 0x02; public static final int MPEG4Level1 = 0x04; @@ -2960,7 +2959,6 @@ public final class MediaCodecInfo { public static final int MPEG4Level5 = 0x80; public static final int MPEG4Level6 = 0x100; - // from OMX_VIDEO_MPEG2PROFILETYPE public static final int MPEG2ProfileSimple = 0x00; public static final int MPEG2ProfileMain = 0x01; public static final int MPEG2Profile422 = 0x02; @@ -2968,14 +2966,12 @@ public final class MediaCodecInfo { public static final int MPEG2ProfileSpatial = 0x04; public static final int MPEG2ProfileHigh = 0x05; - // from OMX_VIDEO_MPEG2LEVELTYPE public static final int MPEG2LevelLL = 0x00; public static final int MPEG2LevelML = 0x01; public static final int MPEG2LevelH14 = 0x02; public static final int MPEG2LevelHL = 0x03; public static final int MPEG2LevelHP = 0x04; - // from OMX_AUDIO_AACPROFILETYPE public static final int AACObjectMain = 1; public static final int AACObjectLC = 2; public static final int AACObjectSSR = 3; @@ -2990,16 +2986,13 @@ public final class MediaCodecInfo { /** xHE-AAC (includes USAC) */ public static final int AACObjectXHE = 42; - // from OMX_VIDEO_VP8LEVELTYPE public static final int VP8Level_Version0 = 0x01; public static final int VP8Level_Version1 = 0x02; public static final int VP8Level_Version2 = 0x04; public static final int VP8Level_Version3 = 0x08; - // from OMX_VIDEO_VP8PROFILETYPE public static final int VP8ProfileMain = 0x01; - // from OMX_VIDEO_VP9PROFILETYPE public static final int VP9Profile0 = 0x01; public static final int VP9Profile1 = 0x02; public static final int VP9Profile2 = 0x04; @@ -3010,7 +3003,6 @@ public final class MediaCodecInfo { public static final int VP9Profile2HDR10Plus = 0x4000; public static final int VP9Profile3HDR10Plus = 0x8000; - // from OMX_VIDEO_VP9LEVELTYPE public static final int VP9Level1 = 0x1; public static final int VP9Level11 = 0x2; public static final int VP9Level2 = 0x4; @@ -3026,14 +3018,12 @@ public final class MediaCodecInfo { public static final int VP9Level61 = 0x1000; public static final int VP9Level62 = 0x2000; - // from OMX_VIDEO_HEVCPROFILETYPE public static final int HEVCProfileMain = 0x01; public static final int HEVCProfileMain10 = 0x02; public static final int HEVCProfileMainStill = 0x04; public static final int HEVCProfileMain10HDR10 = 0x1000; public static final int HEVCProfileMain10HDR10Plus = 0x2000; - // from OMX_VIDEO_HEVCLEVELTYPE public static final int HEVCMainTierLevel1 = 0x1; public static final int HEVCHighTierLevel1 = 0x2; public static final int HEVCMainTierLevel2 = 0x4; @@ -3067,7 +3057,6 @@ public final class MediaCodecInfo { HEVCHighTierLevel51 | HEVCHighTierLevel52 | HEVCHighTierLevel6 | HEVCHighTierLevel61 | HEVCHighTierLevel62; - // from OMX_VIDEO_DOLBYVISIONPROFILETYPE public static final int DolbyVisionProfileDvavPer = 0x1; public static final int DolbyVisionProfileDvavPen = 0x2; public static final int DolbyVisionProfileDvheDer = 0x4; @@ -3079,7 +3068,6 @@ public final class MediaCodecInfo { public static final int DolbyVisionProfileDvheSt = 0x100; public static final int DolbyVisionProfileDvavSe = 0x200; - // from OMX_VIDEO_DOLBYVISIONLEVELTYPE public static final int DolbyVisionLevelHd24 = 0x1; public static final int DolbyVisionLevelHd30 = 0x2; public static final int DolbyVisionLevelFhd24 = 0x4; @@ -3090,17 +3078,44 @@ public final class MediaCodecInfo { public static final int DolbyVisionLevelUhd48 = 0x80; public static final int DolbyVisionLevelUhd60 = 0x100; + public static final int AV1Profile0 = 0x1; + public static final int AV1Profile1 = 0x2; + public static final int AV1Profile2 = 0x4; + + public static final int AV1Level2 = 0x1; + public static final int AV1Level21 = 0x2; + public static final int AV1Level22 = 0x4; + public static final int AV1Level23 = 0x8; + public static final int AV1Level3 = 0x10; + public static final int AV1Level31 = 0x20; + public static final int AV1Level32 = 0x40; + public static final int AV1Level33 = 0x80; + public static final int AV1Level4 = 0x100; + public static final int AV1Level41 = 0x200; + public static final int AV1Level42 = 0x400; + public static final int AV1Level43 = 0x800; + public static final int AV1Level5 = 0x1000; + public static final int AV1Level51 = 0x2000; + public static final int AV1Level52 = 0x4000; + public static final int AV1Level53 = 0x8000; + public static final int AV1Level6 = 0x10000; + public static final int AV1Level61 = 0x20000; + public static final int AV1Level62 = 0x40000; + public static final int AV1Level63 = 0x80000; + public static final int AV1Level7 = 0x100000; + public static final int AV1Level71 = 0x200000; + public static final int AV1Level72 = 0x400000; + public static final int AV1Level73 = 0x800000; + /** - * Defined in the OpenMAX IL specs, depending on the type of media - * this can be OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE, - * OMX_VIDEO_MPEG4PROFILETYPE, OMX_VIDEO_VP8PROFILETYPE or OMX_VIDEO_VP9PROFILETYPE. + * The profile of the media content. Depending on the type of media this can be + * one of the profile values defined in this class. */ public int profile; /** - * Defined in the OpenMAX IL specs, depending on the type of media - * this can be OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE - * OMX_VIDEO_MPEG4LEVELTYPE, OMX_VIDEO_VP8LEVELTYPE or OMX_VIDEO_VP9LEVELTYPE. + * The level of the media content. Depending on the type of media this can be + * one of the level values defined in this class. * * Note that VP9 decoder on platforms before {@link android.os.Build.VERSION_CODES#N} may * not advertise a profile level support. For those VP9 decoders, please use diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 594a22457cc3..c82b5f6f12a1 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -124,6 +124,7 @@ import java.util.stream.Collectors; public final class MediaFormat { public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; + public static final String MIMETYPE_VIDEO_AV1 = "video/av01"; public static final String MIMETYPE_VIDEO_AVC = "video/avc"; public static final String MIMETYPE_VIDEO_HEVC = "video/hevc"; public static final String MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es"; diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java index 95ee4c039f4b..c7b891151201 100644 --- a/media/java/android/media/Session2Token.java +++ b/media/java/android/media/Session2Token.java @@ -152,9 +152,7 @@ public final class Session2Token implements Parcelable { mType = in.readInt(); mPackageName = in.readString(); mServiceName = in.readString(); - // TODO: Uncomment below and stop hardcode mSessionLink - mSessionLink = null; - //mSessionLink = ISession.Stub.asInterface(in.readStrongBinder()); + mSessionLink = Session2Link.CREATOR.createFromParcel(in); mComponentName = ComponentName.unflattenFromString(in.readString()); } @@ -164,8 +162,7 @@ public final class Session2Token implements Parcelable { dest.writeInt(mType); dest.writeString(mPackageName); dest.writeString(mServiceName); - // TODO: Uncomment below - //dest.writeStrongBinder(mSessionLink.getBinder()); + mSessionLink.writeToParcel(dest, flags); dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString()); } diff --git a/media/java/android/media/session/MediaSessionProviderService.java b/media/java/android/media/session/MediaSessionProviderService.java new file mode 100644 index 000000000000..9a346ff4a12e --- /dev/null +++ b/media/java/android/media/session/MediaSessionProviderService.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +/** + * Abstract class for mainline module services. + * + * @hide // TODO: Make it as a @SystemApi + */ +public abstract class MediaSessionProviderService extends Service { + + @Override + public IBinder onBind(Intent intent) { + // TODO: Return IMediaSessionProviderService.Stub() + return null; + } +} diff --git a/media/packages/MediaCore/Android.bp b/media/packages/MediaCore/Android.bp new file mode 100644 index 000000000000..c7fd58bf933a --- /dev/null +++ b/media/packages/MediaCore/Android.bp @@ -0,0 +1,21 @@ +android_app { + name: "MediaCore", + + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + // TODO: Temporarily statically linked. Should go into "libs" + "media1", + ], + + // System app + platform_apis: true, + + // Privileged app + privileged: true, + + // Make sure that the implementation only relies on SDK or system APIs. + sdk_version: "system_current", +} diff --git a/media/packages/MediaCore/AndroidManifest.xml b/media/packages/MediaCore/AndroidManifest.xml new file mode 100644 index 000000000000..4e2b274511e8 --- /dev/null +++ b/media/packages/MediaCore/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/AndroidManifest.xml +** +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.media" coreApp="true" android:sharedUserId="android.uid.system" + android:sharedUserLabel="@string/android_system_label"> + <application android:process="system" + android:persistent="true" + android:directBootAware="true"> + <service android:name="AmlMediaSessionProviderService" android:singleUser="true"> + <intent-filter> + <action android:name="android.media.session.MediaSessionProviderService"/> + </intent-filter> + </service> + </application> +</manifest> diff --git a/media/packages/MediaCore/res/values/strings.xml b/media/packages/MediaCore/res/values/strings.xml new file mode 100644 index 000000000000..59fd635b905f --- /dev/null +++ b/media/packages/MediaCore/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <!-- Label for the Android system components when they are shown to the user. --> + <string name="android_system_label" translatable="false">Android System</string> +</resources> + diff --git a/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java b/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java new file mode 100644 index 000000000000..43b95ab7ebdb --- /dev/null +++ b/media/packages/MediaCore/src/com/android/media/AmlMediaSessionProviderService.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.media; + +import android.content.Context; +import android.media.session.MediaSessionProviderService; +import android.os.PowerManager; +import android.util.Log; + +/** + * System implementation of MediaSessionProviderService + */ +public class AmlMediaSessionProviderService extends MediaSessionProviderService { + private static final String TAG = "AmlMediaSessionProviderS"; + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private Context mContext; + + public AmlMediaSessionProviderService(Context context) { + mContext = context; + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index bce559396ddd..5153f9ea86d1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1052,7 +1052,7 @@ public class SettingsProvider extends ContentProvider { } // TODO(b/117663715): Ensure the caller can access the setting. - // enforceSettingReadable(name, SETTINGS_TYPE_CONFIG, UserHandle.getCallingUserId()); + // enforceReadPermission(READ_DEVICE_CONFIG); // Get the value. synchronized (mLock) { @@ -1088,8 +1088,9 @@ public class SettingsProvider extends ContentProvider { private boolean mutateConfigSetting(String name, String value, String prefix, boolean makeDefault, int operation, int mode) { - // TODO(b/117663715): check the new permission when it's added. - // enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS); + + // TODO(b/117663715): Ensure the caller can access the setting. + // enforceReadPermission(WRITE_DEVICE_CONFIG); // Perform the mutation. synchronized (mLock) { diff --git a/packages/SettingsProvider/test/AndroidManifest.xml b/packages/SettingsProvider/test/AndroidManifest.xml index 87a4f603f70b..ebdf9b1a2791 100644 --- a/packages/SettingsProvider/test/AndroidManifest.xml +++ b/packages/SettingsProvider/test/AndroidManifest.xml @@ -20,6 +20,8 @@ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.WRITE_SETTINGS"/> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> <uses-permission android:name="android.permission.MANAGE_USERS"/> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 9b775e058ea8..b903142c44c6 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -57,6 +57,8 @@ <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" /> <!-- Development tool permissions granted to the shell. --> diff --git a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml index 0d72657bb2df..6f7f39810608 100644 --- a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml @@ -19,15 +19,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - <TextClock + <include android:id="@+id/digital_clock" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:letterSpacing="0.03" - android:singleLine="true" - style="@style/widget_big" - android:format12Hour="@string/keyguard_widget_12_hours_format" - android:format24Hour="@string/keyguard_widget_24_hours_format" + layout="@layout/text_clock" /> <com.android.keyguard.clock.ImageClock android:id="@+id/analog_clock" diff --git a/packages/SystemUI/res-keyguard/layout/digital_clock.xml b/packages/SystemUI/res-keyguard/layout/digital_clock.xml index cf4a573834dd..e88e2c94e74f 100644 --- a/packages/SystemUI/res-keyguard/layout/digital_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/digital_clock.xml @@ -20,16 +20,9 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_alignParentTop="true"> - <TextClock + <include android:id="@+id/lock_screen_clock" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:letterSpacing="0.03" - android:singleLine="true" - style="@style/widget_big" - android:format12Hour="@string/keyguard_widget_12_hours_format" - android:format24Hour="@string/keyguard_widget_24_hours_format" /> + layout="@layout/text_clock" /> </FrameLayout> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index d52866fbd444..463367b2c600 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -30,17 +30,9 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_alignParentTop="true"> - <TextClock + <include android:id="@+id/default_clock_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:letterSpacing="0.03" - android:textColor="?attr/wallpaperTextColor" - android:singleLine="true" - style="@style/widget_big" - android:format12Hour="@string/keyguard_widget_12_hours_format" - android:format24Hour="@string/keyguard_widget_24_hours_format" /> + layout="@layout/text_clock" /> </FrameLayout> <include layout="@layout/keyguard_status_area" android:id="@+id/keyguard_status_area" diff --git a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml index 9033fce881c4..64b676f55fd6 100644 --- a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml @@ -19,15 +19,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - <TextClock + <include android:id="@+id/digital_clock" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:letterSpacing="0.03" - android:singleLine="true" - style="@style/widget_big" - android:format12Hour="@string/keyguard_widget_12_hours_format" - android:format24Hour="@string/keyguard_widget_24_hours_format" + layout="@layout/text_clock" /> <com.android.keyguard.clock.StretchAnalogClock android:id="@+id/analog_clock" diff --git a/packages/SystemUI/res-keyguard/layout/text_clock.xml b/packages/SystemUI/res-keyguard/layout/text_clock.xml new file mode 100644 index 000000000000..b61ad9c4fc11 --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/text_clock.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<TextClock + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:letterSpacing="0.03" + android:textColor="?attr/wallpaperTextColor" + android:singleLine="true" + style="@style/widget_big" + android:format12Hour="@string/keyguard_widget_12_hours_format" + android:format24Hour="@string/keyguard_widget_24_hours_format" + android:elegantTextHeight="false" +/> diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml index 7f4e0d21078f..f932303473bd 100644 --- a/packages/SystemUI/res/layout/global_actions_wrapped.xml +++ b/packages/SystemUI/res/layout/global_actions_wrapped.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <com.android.systemui.HardwareUiLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="top|right" diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 633f8686b804..bd34beac7fd6 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -114,5 +114,8 @@ <item type="id" name="aod_mask_transition_progress_tag" /> <item type="id" name="aod_mask_transition_progress_end_tag" /> <item type="id" name="aod_mask_transition_progress_start_tag" /> + + <!-- Global Actions Menu --> + <item type="id" name="global_actions_view" /> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index a8094d20d5a2..e9ce1a670fe1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -248,10 +248,6 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockView.setShowCurrentUserTime(showCurrentUserTime); } - public void setElegantTextHeight(boolean elegant) { - mClockView.setElegantTextHeight(elegant); - } - public void setTextSize(int unit, float size) { mClockView.setTextSize(unit, size); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index f0cdc890306f..7ae4c41d318f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -202,10 +202,6 @@ public class KeyguardStatusView extends GridLayout implements updateOwnerInfo(); updateLogoutView(); updateDark(); - - // Disable elegant text height because our fancy colon makes the ymin value huge for no - // reason. - mClockView.setElegantTextHeight(false); } /** diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java index 16e869e9d317..e28aa9d369cb 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -37,23 +37,25 @@ import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.leak.RotationUtils; -public class HardwareUiLayout extends LinearLayout implements Tunable { +/** + * Layout for placing two containers at a specific physical position on the device, relative to the + * device's hardware, regardless of screen rotation. + */ +public class HardwareUiLayout extends MultiListLayout implements Tunable { private static final String EDGE_BLEED = "sysui_hwui_edge_bleed"; private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider"; private final int[] mTmp2 = new int[2]; - private View mList; - private View mSeparatedView; + private ViewGroup mList; + private ViewGroup mSeparatedView; private int mOldHeight; private boolean mAnimating; private AnimatorSet mAnimation; private View mDivision; - private boolean mHasOutsideTouch; private HardwareBgDrawable mListBackground; private HardwareBgDrawable mSeparatedViewBackground; private Animator mAnimator; private boolean mCollapse; - private boolean mHasSeparatedButton; private int mEndPoint; private boolean mEdgeBleed; private boolean mRoundedDivider; @@ -67,6 +69,35 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { } @Override + protected ViewGroup getSeparatedView() { + return findViewById(com.android.systemui.R.id.separated_button); + } + + @Override + protected ViewGroup getListView() { + return findViewById(android.R.id.list); + } + + @Override + public void removeAllItems() { + if (mList != null) { + mList.removeAllViews(); + } + if (mSeparatedView != null) { + mSeparatedView.removeAllViews(); + } + } + + @Override + public ViewGroup getParentView(boolean separated, int index) { + if (separated) { + return getSeparatedView(); + } else { + return getListView(); + } + } + + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); updateSettings(); @@ -137,9 +168,9 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mList == null) { if (getChildCount() != 0) { - mList = getChildAt(0); + mList = getListView(); mList.setBackground(mListBackground); - mSeparatedView = getChildAt(1); + mSeparatedView = getSeparatedView(); mSeparatedView.setBackground(mSeparatedViewBackground); updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); mOldHeight = mList.getMeasuredHeight(); @@ -187,7 +218,7 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { } else { rotateLeft(); } - if (mHasSeparatedButton) { + if (mHasSeparatedView) { if (from == ROTATION_SEASCAPE || to == ROTATION_SEASCAPE) { // Separated view has top margin, so seascape separated view need special rotation, // not a full left or right rotation. @@ -408,8 +439,8 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { if (mList == null) return; // If got separated button, setRotatedBackground to false, // all items won't get white background. - mListBackground.setRotatedBackground(mHasSeparatedButton); - mSeparatedViewBackground.setRotatedBackground(mHasSeparatedButton); + mListBackground.setRotatedBackground(mHasSeparatedView); + mSeparatedViewBackground.setRotatedBackground(mHasSeparatedView); if (mDivision != null && mDivision.getVisibility() == VISIBLE) { int index = mRotatedBackground ? 0 : 1; mDivision.getLocationOnScreen(mTmp2); @@ -460,21 +491,21 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { case RotationUtils.ROTATION_LANDSCAPE: defaultTopPadding = getPaddingLeft(); viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); - separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0; + separatedViewTopMargin = mHasSeparatedView ? params.leftMargin : 0; screenHeight = getMeasuredWidth(); targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP; break; case RotationUtils.ROTATION_SEASCAPE: defaultTopPadding = getPaddingRight(); viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); - separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0; + separatedViewTopMargin = mHasSeparatedView ? params.leftMargin : 0; screenHeight = getMeasuredWidth(); targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM; break; default: // Portrait defaultTopPadding = getPaddingTop(); viewsTotalHeight = mList.getMeasuredHeight() + mSeparatedView.getMeasuredHeight(); - separatedViewTopMargin = mHasSeparatedButton ? params.topMargin : 0; + separatedViewTopMargin = mHasSeparatedView ? params.topMargin : 0; screenHeight = getMeasuredHeight(); targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT; break; @@ -491,30 +522,10 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { return super.getOutlineProvider(); } - public void setOutsideTouchListener(OnClickListener onClickListener) { - mHasOutsideTouch = true; - requestLayout(); - setOnClickListener(onClickListener); - setClickable(true); - setFocusable(true); - } - public void setCollapse() { mCollapse = true; } - public void setHasSeparatedButton(boolean hasSeparatedButton) { - mHasSeparatedButton = hasSeparatedButton; - } - - public static HardwareUiLayout get(View v) { - if (v instanceof HardwareUiLayout) return (HardwareUiLayout) v; - if (v.getParent() instanceof View) { - return get((View) v.getParent()); - } - return null; - } - private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> { if (mHasOutsideTouch || (mList == null)) { inoutInfo.setTouchableInsets( diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java new file mode 100644 index 000000000000..0c7a9a9fffd2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +/** + * Layout class representing the Global Actions menu which appears when the power button is held. + */ +public abstract class MultiListLayout extends LinearLayout { + boolean mHasOutsideTouch; + boolean mHasSeparatedView; + + int mExpectedSeparatedItemCount; + int mExpectedListItemCount; + + public MultiListLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + protected abstract ViewGroup getSeparatedView(); + + protected abstract ViewGroup getListView(); + + /** + * Removes all child items from the separated and list views, if they exist. + */ + public abstract void removeAllItems(); + + /** + * Get the parent view which will be used to contain the item at the specified index. + * @param separated Whether or not this index refers to a position in the separated or list + * container. + * @param index The index of the item within the container. + * @return The parent ViewGroup which will be used to contain the specified item + * after it has been added to the layout. + */ + public abstract ViewGroup getParentView(boolean separated, int index); + + /** + * Sets the divided view, which may have a differently-colored background. + */ + public abstract void setDivisionView(View v); + + /** + * Set the view accessibility delegate for the list view container. + */ + public void setListViewAccessibilityDelegate(View.AccessibilityDelegate delegate) { + getListView().setAccessibilityDelegate(delegate); + } + + protected void setSeparatedViewVisibility(boolean visible) { + getSeparatedView().setVisibility(visible ? View.VISIBLE : View.GONE); + } + + /** + * Sets the number of items expected to be rendered in the separated container. This allows the + * layout to correctly determine which parent containers will be used for items before they have + * beenadded to the layout. + * @param count The number of items expected. + */ + public void setExpectedSeparatedItemCount(int count) { + mExpectedSeparatedItemCount = count; + } + + /** + * Sets the number of items expected to be rendered in the list container. This allows the + * layout to correctly determine which parent containers will be used for items before they have + * beenadded to the layout. + * @param count The number of items expected. + */ + public void setExpectedListItemCount(int count) { + mExpectedListItemCount = count; + } + + /** + * Sets whether the separated view should be shown, and handles updating visibility on + * that view. + */ + public void setHasSeparatedView(boolean hasSeparatedView) { + mHasSeparatedView = hasSeparatedView; + setSeparatedViewVisibility(hasSeparatedView); + } + + /** + * Sets this layout to respond to an outside touch listener. + */ + public void setOutsideTouchListener(OnClickListener onClickListener) { + mHasOutsideTouch = true; + requestLayout(); + setOnClickListener(onClickListener); + setClickable(true); + setFocusable(true); + } + + /** + * Retrieve the MultiListLayout associated with the given view. + */ + public static MultiListLayout get(View v) { + if (v instanceof MultiListLayout) return (MultiListLayout) v; + if (v.getParent() instanceof View) { + return get((View) v.getParent()); + } + return null; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 268245bd4acd..7b18fad0e105 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -67,10 +67,8 @@ import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.BaseAdapter; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ImageView.ScaleType; -import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.R; @@ -86,8 +84,8 @@ import com.android.internal.util.ScreenRecordHelper; import com.android.internal.util.ScreenshotHelper; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Dependency; -import com.android.systemui.HardwareUiLayout; import com.android.systemui.Interpolators; +import com.android.systemui.MultiListLayout; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.statusbar.phone.ScrimController; @@ -490,6 +488,11 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, public boolean showBeforeProvisioning() { return true; } + + @Override + public boolean shouldBeSeparated() { + return true; + } } private final class RestartAction extends SinglePressAction implements LongPressAction { @@ -926,6 +929,34 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, return getItem(position).isEnabled(); } + public ArrayList<Action> getSeparatedActions(boolean shouldUseSeparatedView) { + ArrayList<Action> separatedActions = new ArrayList<Action>(); + if (!shouldUseSeparatedView) { + return separatedActions; + } + for (int i = 0; i < mItems.size(); i++) { + final Action action = mItems.get(i); + if (action.shouldBeSeparated()) { + separatedActions.add(action); + } + } + return separatedActions; + } + + public ArrayList<Action> getListActions(boolean shouldUseSeparatedView) { + if (!shouldUseSeparatedView) { + return new ArrayList<Action>(mItems); + } + ArrayList<Action> listActions = new ArrayList<Action>(); + for (int i = 0; i < mItems.size(); i++) { + final Action action = mItems.get(i); + if (!action.shouldBeSeparated()) { + listActions.add(action); + } + } + return listActions; + } + @Override public boolean areAllItemsEnabled() { return false; @@ -965,7 +996,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext)); // Everything but screenshot, the last item, gets white background. if (position == getCount() - 1) { - HardwareUiLayout.get(parent).setDivisionView(view); + MultiListLayout.get(parent).setDivisionView(view); } return view; } @@ -1004,6 +1035,10 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, boolean showBeforeProvisioning(); boolean isEnabled(); + + default boolean shouldBeSeparated() { + return false; + } } /** @@ -1423,9 +1458,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final Context mContext; private final MyAdapter mAdapter; - private final LinearLayout mListView; - private final FrameLayout mSeparatedView; - private final HardwareUiLayout mHardwareLayout; + private final MultiListLayout mGlobalActionsLayout; private final OnClickListener mClickListener; private final OnItemLongClickListener mLongClickListener; private final GradientDrawable mGradientDrawable; @@ -1466,16 +1499,11 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); setContentView(com.android.systemui.R.layout.global_actions_wrapped); - mListView = findViewById(android.R.id.list); - mSeparatedView = findViewById(com.android.systemui.R.id.separated_button); - if (!mShouldDisplaySeparatedButton) { - mSeparatedView.setVisibility(View.GONE); - } - mHardwareLayout = HardwareUiLayout.get(mListView); - mHardwareLayout.setOutsideTouchListener(view -> dismiss()); - mHardwareLayout.setHasSeparatedButton(mShouldDisplaySeparatedButton); - setTitle(R.string.global_actions); - mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() { + mGlobalActionsLayout = (MultiListLayout) + findViewById(com.android.systemui.R.id.global_actions_view); + mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss()); + mGlobalActionsLayout.setHasSeparatedView(mShouldDisplaySeparatedButton); + mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() { @Override public boolean dispatchPopulateAccessibilityEvent( View host, AccessibilityEvent event) { @@ -1484,20 +1512,33 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, return true; } }); + setTitle(R.string.global_actions); } private void updateList() { - mListView.removeAllViews(); - mSeparatedView.removeAllViews(); + mGlobalActionsLayout.removeAllItems(); + ArrayList<Action> separatedActions = + mAdapter.getSeparatedActions(mShouldDisplaySeparatedButton); + ArrayList<Action> listActions = mAdapter.getListActions(mShouldDisplaySeparatedButton); + mGlobalActionsLayout.setExpectedListItemCount(listActions.size()); + mGlobalActionsLayout.setExpectedSeparatedItemCount(separatedActions.size()); + for (int i = 0; i < mAdapter.getCount(); i++) { - ViewGroup parentView = mShouldDisplaySeparatedButton && i == mAdapter.getCount() - 1 - ? mSeparatedView : mListView; - View v = mAdapter.getView(i, null, parentView); + Action action = mAdapter.getItem(i); + int separatedIndex = separatedActions.indexOf(action); + ViewGroup parent; + if (separatedIndex != -1) { + parent = mGlobalActionsLayout.getParentView(true, separatedIndex); + } else { + int listIndex = listActions.indexOf(action); + parent = mGlobalActionsLayout.getParentView(false, listIndex); + } + View v = mAdapter.getView(i, null, parent); final int pos = i; v.setOnClickListener(view -> mClickListener.onClick(this, pos)); v.setOnLongClickListener(view -> mLongClickListener.onItemLongClick(null, v, pos, 0)); - parentView.addView(v); + parent.addView(v); } } @@ -1543,9 +1584,9 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, super.show(); mShowing = true; mGradientDrawable.setAlpha(0); - mHardwareLayout.setTranslationX(getAnimTranslation()); - mHardwareLayout.setAlpha(0); - mHardwareLayout.animate() + mGlobalActionsLayout.setTranslationX(getAnimTranslation()); + mGlobalActionsLayout.setAlpha(0); + mGlobalActionsLayout.animate() .alpha(1) .translationX(0) .setDuration(300) @@ -1564,9 +1605,9 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, return; } mShowing = false; - mHardwareLayout.setTranslationX(0); - mHardwareLayout.setAlpha(1); - mHardwareLayout.animate() + mGlobalActionsLayout.setTranslationX(0); + mGlobalActionsLayout.setAlpha(1); + mGlobalActionsLayout.animate() .alpha(0) .translationX(getAnimTranslation()) .setDuration(300) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java index eea44906029d..839b06cec496 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java @@ -33,6 +33,13 @@ public interface NotificationEntryListener { default void onPendingEntryAdded(NotificationEntry entry) { } + // TODO: Combine this with onPreEntryUpdated into "onBeforeEntryFiltered" or similar + /** + * Called when a new entry is created but before it has been filtered or displayed to the user. + */ + default void onBeforeNotificationAdded(NotificationEntry entry) { + } + /** * Called when a new entry is created. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 45db00210a2e..989e781ab5ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -18,11 +18,9 @@ package com.android.systemui.statusbar.notification; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; -import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -41,7 +39,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; @@ -68,8 +65,6 @@ public class NotificationEntryManager implements @VisibleForTesting protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>(); - private final DeviceProvisionedController mDeviceProvisionedController = - Dependency.get(DeviceProvisionedController.class); private final ForegroundServiceController mForegroundServiceController = Dependency.get(ForegroundServiceController.class); @@ -81,25 +76,12 @@ public class NotificationEntryManager implements private NotificationListenerService.RankingMap mLatestRankingMap; @VisibleForTesting protected NotificationData mNotificationData; - private NotificationListContainer mListContainer; + @VisibleForTesting final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders = new ArrayList<>(); private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>(); - private final DeviceProvisionedController.DeviceProvisionedListener - mDeviceProvisionedListener = - new DeviceProvisionedController.DeviceProvisionedListener() { - @Override - public void onDeviceProvisionedChanged() { - updateNotifications(); - } - }; - - public void destroy() { - mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener); - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NotificationEntryManager state:"); @@ -151,9 +133,6 @@ public class NotificationEntryManager implements HeadsUpManager headsUpManager) { mPresenter = presenter; mNotificationData.setHeadsUpManager(headsUpManager); - mListContainer = listContainer; - - mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); } /** Adds multiple {@link NotificationLifetimeExtender}s. */ @@ -227,7 +206,9 @@ public class NotificationEntryManager implements listener.onEntryInflated(entry, inflatedFlags); } mNotificationData.add(entry); - tagForeground(entry.notification); + for (NotificationEntryListener listener : mNotificationEntryListeners) { + listener.onBeforeNotificationAdded(entry); + } updateNotifications(); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onNotificationAdded(entry); @@ -283,7 +264,6 @@ public class NotificationEntryManager implements if (entry.rowExists()) { entry.removeRow(); - mListContainer.cleanUpViewStateForEntry(entry); } // Let's remove the children if this was a summary @@ -368,19 +348,6 @@ public class NotificationEntryManager implements } } - @VisibleForTesting - void tagForeground(StatusBarNotification notification) { - ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps( - notification.getUserId(), notification.getPackageName()); - if (activeOps != null) { - int N = activeOps.size(); - for (int i = 0; i < N; i++) { - updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(), - notification.getPackageName(), true); - } - } - } - @Override public void addNotification(StatusBarNotification notification, NotificationListenerService.RankingMap ranking) { @@ -391,15 +358,6 @@ public class NotificationEntryManager implements } } - public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) { - String foregroundKey = mForegroundServiceController.getStandardLayoutKey( - UserHandle.getUserId(uid), pkg); - if (foregroundKey != null) { - mNotificationData.updateAppOp(appOp, uid, pkg, foregroundKey, showIcon); - updateNotifications(); - } - } - private void updateNotificationInternal(StatusBarNotification notification, NotificationListenerService.RankingMap ranking) throws InflationException { if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); @@ -452,8 +410,9 @@ public class NotificationEntryManager implements public void updateNotifications() { mNotificationData.filterAndSort(); - - mPresenter.updateNotificationViews(); + if (mPresenter != null) { + mPresenter.updateNotificationViews(); + } } public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java new file mode 100644 index 000000000000..88f4ca239af4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.util.ArraySet; + +import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.ForegroundServiceController; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; + +/** + * Root controller for the list of notifications in the shade. + * + * TODO: Much of the code in NotificationPresenter should eventually move in here. It will proxy + * domain-specific behavior (ARC, etc) to subcontrollers. + */ +public class NotificationListController { + private final NotificationEntryManager mEntryManager; + private final NotificationListContainer mListContainer; + private final ForegroundServiceController mForegroundServiceController; + private final DeviceProvisionedController mDeviceProvisionedController; + + public NotificationListController( + NotificationEntryManager entryManager, + NotificationListContainer listContainer, + ForegroundServiceController foregroundServiceController, + DeviceProvisionedController deviceProvisionedController) { + mEntryManager = checkNotNull(entryManager); + mListContainer = checkNotNull(listContainer); + mForegroundServiceController = checkNotNull(foregroundServiceController); + mDeviceProvisionedController = checkNotNull(deviceProvisionedController); + } + + /** + * Causes the controller to register listeners on its dependencies. This method must be called + * before the controller is ready to perform its duties. + */ + public void bind() { + mEntryManager.addNotificationEntryListener(mEntryListener); + mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); + } + + /** Should be called when the list controller is being destroyed. */ + public void destroy() { + mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener); + } + + @SuppressWarnings("FieldCanBeLocal") + private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { + @Override + public void onEntryRemoved( + NotificationEntry entry, + NotificationVisibility visibility, + boolean removedByUser) { + mListContainer.cleanUpViewStateForEntry(entry); + } + + @Override + public void onBeforeNotificationAdded(NotificationEntry entry) { + tagForeground(entry.notification); + } + }; + + private final DeviceProvisionedListener mDeviceProvisionedListener = + new DeviceProvisionedListener() { + @Override + public void onDeviceProvisionedChanged() { + mEntryManager.updateNotifications(); + } + }; + + // TODO: This method is horrifically inefficient + private void tagForeground(StatusBarNotification notification) { + ArraySet<Integer> activeOps = + mForegroundServiceController.getAppOps( + notification.getUserId(), notification.getPackageName()); + if (activeOps != null) { + int len = activeOps.size(); + for (int i = 0; i < len; i++) { + updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(), + notification.getPackageName(), true); + } + } + } + + /** When an app op changes, propagate that change to notifications. */ + public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) { + String foregroundKey = + mForegroundServiceController.getStandardLayoutKey(UserHandle.getUserId(uid), pkg); + if (foregroundKey != null) { + mEntryManager + .getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon); + mEntryManager.updateNotifications(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 853d7ab9a76d..3568f2846a51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -612,9 +612,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo anim.setStartDelay(mAnimationDelay); anim.setDuration(mAnimationDuration); anim.addListener(new AnimatorListenerAdapter() { + private Callback lastCallback = mCallback; + @Override public void onAnimationEnd(Animator animation) { - onFinished(); + onFinished(lastCallback); scrim.setTag(TAG_KEY_ANIM, null); dispatchScrimsVisible(); @@ -672,14 +674,23 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } private void onFinished() { + onFinished(mCallback); + } + + private void onFinished(Callback callback) { if (mWakeLockHeld) { mWakeLock.release(); mWakeLockHeld = false; } - if (mCallback != null) { - mCallback.onFinished(); - mCallback = null; + + if (callback != null) { + callback.onFinished(); + + if (callback == mCallback) { + mCallback = null; + } } + // When unlocking with fingerprint, we'll fade the scrims from black to transparent. // At the end of the animation we need to remove the tint. if (mState == ScrimState.UNLOCKED) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 7569a50a8c54..ed71598e22ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -196,6 +196,7 @@ import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationClicker; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.NotificationListController; import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -387,6 +388,7 @@ public class StatusBar extends SystemUI implements DemoMode, private NotificationGutsManager mGutsManager; protected NotificationLogger mNotificationLogger; protected NotificationEntryManager mEntryManager; + private NotificationListController mNotificationListController; private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private NotificationRowBinder mNotificationRowBinder; protected NotificationViewHierarchyManager mViewHierarchyManager; @@ -593,7 +595,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { mForegroundServiceController.onAppOpChanged(code, uid, packageName, active); Dependency.get(Dependency.MAIN_HANDLER).post(() -> { - mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active); + mNotificationListController.updateNotificationsForAppOp(code, uid, packageName, active); }); } @@ -1044,6 +1046,13 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager, mNotificationAlertingManager); + mNotificationListController = + new NotificationListController( + mEntryManager, + (NotificationListContainer) mStackScroller, + mForegroundServiceController, + mDeviceProvisionedController); + mAppOpsController.addCallback(APP_OPS, this); mNotificationListener.setUpWithPresenter(mPresenter); mNotificationShelf.setOnActivatedListener(mPresenter); @@ -1056,6 +1065,7 @@ public class StatusBar extends SystemUI implements DemoMode, this, Dependency.get(BubbleController.class), mNotificationActivityStarter)); mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager); + mNotificationListController.bind(); } /** @@ -2831,7 +2841,7 @@ public class StatusBar extends SystemUI implements DemoMode, } catch (RemoteException e) { // Ignore. } - mEntryManager.destroy(); + mNotificationListController.destroy(); // End old BaseStatusBar.destroy(). if (mStatusBarWindow != null) { mWindowManager.removeViewImmediate(mStatusBarWindow); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index d937f93482d5..9ce6ae139998 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -22,19 +22,15 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; -import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -49,7 +45,6 @@ import android.service.notification.StatusBarNotification; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.ArraySet; import android.widget.FrameLayout; import com.android.internal.logging.MetricsLogger; @@ -79,8 +74,6 @@ import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; -import junit.framework.Assert; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -346,7 +339,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { verify(mEntryListener, never()).onInflationError(any(), any()); - verify(mListContainer).cleanUpViewStateForEntry(mEntry); verify(mPresenter).updateNotificationViews(); verify(mEntryListener).onEntryRemoved( mEntry, null, false /* removedByUser */); @@ -401,90 +393,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { } @Test - public void testUpdateAppOps_foregroundNoti() { - com.android.systemui.util.Assert.isNotMainThread(); - - when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString())) - .thenReturn(mEntry.key); - mEntry.setRow(mRow); - mEntryManager.getNotificationData().add(mEntry); - - mEntryManager.updateNotificationsForAppOp( - AppOpsManager.OP_CAMERA, mEntry.notification.getUid(), - mEntry.notification.getPackageName(), true); - - verify(mPresenter, times(1)).updateNotificationViews(); - assertTrue(mEntryManager.getNotificationData().get(mEntry.key).mActiveAppOps.contains( - AppOpsManager.OP_CAMERA)); - } - - @Test - public void testUpdateAppOps_otherNoti() { - com.android.systemui.util.Assert.isNotMainThread(); - - when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString())) - .thenReturn(null); - mEntryManager.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true); - - verify(mPresenter, never()).updateNotificationViews(); - } - - @Test - public void testAddNotificationExistingAppOps() { - mEntry.setRow(mRow); - mEntryManager.getNotificationData().add(mEntry); - ArraySet<Integer> expected = new ArraySet<>(); - expected.add(3); - expected.add(235); - expected.add(1); - - when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(expected); - when(mForegroundServiceController.getStandardLayoutKey( - mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(mEntry.key); - - mEntryManager.tagForeground(mEntry.notification); - - Assert.assertEquals(expected.size(), mEntry.mActiveAppOps.size()); - for (int op : expected) { - assertTrue("Entry missing op " + op, mEntry.mActiveAppOps.contains(op)); - } - } - - @Test - public void testAdd_noExistingAppOps() { - mEntry.setRow(mRow); - mEntryManager.getNotificationData().add(mEntry); - when(mForegroundServiceController.getStandardLayoutKey( - mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(mEntry.key); - when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(null); - - mEntryManager.tagForeground(mEntry.notification); - Assert.assertEquals(0, mEntry.mActiveAppOps.size()); - } - - @Test - public void testAdd_existingAppOpsNotForegroundNoti() { - mEntry.setRow(mRow); - mEntryManager.getNotificationData().add(mEntry); - ArraySet<Integer> ops = new ArraySet<>(); - ops.add(3); - ops.add(235); - ops.add(1); - when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn(ops); - when(mForegroundServiceController.getStandardLayoutKey( - mEntry.notification.getUserId(), - mEntry.notification.getPackageName())).thenReturn("something else"); - - mEntryManager.tagForeground(mEntry.notification); - Assert.assertEquals(0, mEntry.mActiveAppOps.size()); - } - - @Test public void testUpdateNotificationRanking() { when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); when(mEnvironment.isDeviceProvisioned()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java new file mode 100644 index 000000000000..4b5037bb3f64 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.app.Notification; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.util.ArraySet; + +import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.ForegroundServiceController; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class NotificationListControllerTest extends SysuiTestCase { + private NotificationListController mController; + + @Mock private NotificationEntryManager mEntryManager; + @Mock private NotificationListContainer mListContainer; + @Mock private ForegroundServiceController mForegroundServiceController; + @Mock private DeviceProvisionedController mDeviceProvisionedController; + + @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor; + @Captor private ArgumentCaptor<DeviceProvisionedListener> mProvisionedCaptor; + + private NotificationEntryListener mEntryListener; + private DeviceProvisionedListener mProvisionedListener; + + // TODO: Remove this once EntryManager no longer needs to be mocked + private NotificationData mNotificationData = new NotificationData(); + + private int mNextNotifId = 0; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); + + mController = new NotificationListController( + mEntryManager, + mListContainer, + mForegroundServiceController, + mDeviceProvisionedController); + mController.bind(); + + // Capture callbacks passed to mocks + verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture()); + mEntryListener = mEntryListenerCaptor.getValue(); + verify(mDeviceProvisionedController).addCallback(mProvisionedCaptor.capture()); + mProvisionedListener = mProvisionedCaptor.getValue(); + } + + @Test + public void testCleanUpViewStateOnEntryRemoved() { + final NotificationEntry entry = buildEntry(); + mEntryListener.onEntryRemoved( + entry, + NotificationVisibility.obtain(entry.key, 0, 0, true), + false); + verify(mListContainer).cleanUpViewStateForEntry(entry); + } + + @Test + public void testCallUpdateNotificationsOnDeviceProvisionedChange() { + mProvisionedListener.onDeviceProvisionedChanged(); + verify(mEntryManager).updateNotifications(); + } + + @Test + public void testAppOps_appOpAddedToForegroundNotif() { + // GIVEN a notification associated with a foreground service + final NotificationEntry entry = buildEntry(); + mNotificationData.add(entry); + when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString())) + .thenReturn(entry.key); + + // WHEN we are notified of a new app op + mController.updateNotificationsForAppOp( + AppOpsManager.OP_CAMERA, + entry.notification.getUid(), + entry.notification.getPackageName(), + true); + + // THEN the app op is added to the entry + assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); + // THEN updateNotifications() is called + verify(mEntryManager, times(1)).updateNotifications(); + } + + @Test + public void testAppOps_appOpAddedToUnrelatedNotif() { + // GIVEN No current foreground notifs + when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString())) + .thenReturn(null); + + // WHEN An unrelated notification gets a new app op + mController.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true); + + // THEN We never call updateNotifications() + verify(mEntryManager, never()).updateNotifications(); + } + + @Test + public void testAppOps_addNotificationWithExistingAppOps() { + // GIVEN a notification with three associated app ops that is associated with a foreground + // service + final NotificationEntry entry = buildEntry(); + mNotificationData.add(entry); + ArraySet<Integer> expected = new ArraySet<>(); + expected.add(3); + expected.add(235); + expected.add(1); + when(mForegroundServiceController.getStandardLayoutKey( + entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(entry.key); + when(mForegroundServiceController.getAppOps(entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(expected); + + // WHEN the notification is added + mEntryListener.onBeforeNotificationAdded(entry); + + // THEN the entry is tagged with all three app ops + assertEquals(expected.size(), entry.mActiveAppOps.size()); + for (int op : expected) { + assertTrue("Entry missing op " + op, entry.mActiveAppOps.contains(op)); + } + } + + @Test + public void testAdd_addNotificationWithNoExistingAppOps() { + // GIVEN a notification with NO associated app ops + final NotificationEntry entry = buildEntry(); + + mNotificationData.add(entry); + when(mForegroundServiceController.getStandardLayoutKey( + entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(entry.key); + when(mForegroundServiceController.getAppOps(entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(null); + + // WHEN the notification is added + mEntryListener.onBeforeNotificationAdded(entry); + + // THEN the entry doesn't have any app ops associated with it + assertEquals(0, entry.mActiveAppOps.size()); + } + + @Test + public void testAdd_addNonForegroundNotificationWithExistingAppOps() { + // GIVEN a notification with app ops that isn't associated with a foreground service + final NotificationEntry entry = buildEntry(); + mNotificationData.add(entry); + ArraySet<Integer> ops = new ArraySet<>(); + ops.add(3); + ops.add(235); + ops.add(1); + when(mForegroundServiceController.getAppOps(entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn(ops); + when(mForegroundServiceController.getStandardLayoutKey( + entry.notification.getUserId(), + entry.notification.getPackageName())).thenReturn("something else"); + + // WHEN the notification is added + mEntryListener.onBeforeNotificationAdded(entry); + + // THEN the entry doesn't have any app ops associated with it + assertEquals(0, entry.mActiveAppOps.size()); + } + + private NotificationEntry buildEntry() { + mNextNotifId++; + + Notification.Builder n = new Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .setContentTitle("Title") + .setContentText("Text"); + + StatusBarNotification notification = + new StatusBarNotification( + TEST_PACKAGE_NAME, + TEST_PACKAGE_NAME, + mNextNotifId, + null, + TEST_UID, + 0, + n.build(), + new UserHandle(ActivityManager.getCurrentUser()), + null, + 0); + return new NotificationEntry(notification); + } + + private static final String TEST_PACKAGE_NAME = "test"; + private static final int TEST_UID = 0; +} diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java index cf9f233b8fe2..8ffaddefd3ef 100644 --- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java @@ -462,7 +462,7 @@ class TouchExplorer extends BaseEventStreamTransformation return false; } - endGestureDetection(); + endGestureDetection(true); mAms.onGesture(gestureId); @@ -472,7 +472,7 @@ class TouchExplorer extends BaseEventStreamTransformation @Override public boolean onGestureCancelled(MotionEvent event, int policyFlags) { if (mCurrentState == STATE_GESTURE_DETECTING) { - endGestureDetection(); + endGestureDetection(event.getActionMasked() == MotionEvent.ACTION_UP); return true; } else if (mCurrentState == STATE_TOUCH_EXPLORING) { // If the finger is still moving, pass the event on. @@ -804,13 +804,19 @@ class TouchExplorer extends BaseEventStreamTransformation } } - private void endGestureDetection() { + private void endGestureDetection(boolean interactionEnd) { mAms.onTouchInteractionEnd(); // Announce the end of the gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); - // Announce the end of a the touch interaction. - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); + if (interactionEnd) { + // Announce the end of a the touch interaction. + sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); + } else { + // If gesture detection is end, but user doesn't release the finger, announce the + // transition to exploration state. + sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); + } mExitGestureDetectionModeDelayed.cancel(); mCurrentState = STATE_TOUCH_EXPLORING; @@ -889,7 +895,6 @@ class TouchExplorer extends BaseEventStreamTransformation MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { final int pointerIdBits = event.getPointerIdBits(); - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); } } @@ -1148,8 +1153,8 @@ class TouchExplorer extends BaseEventStreamTransformation sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); // Clearing puts is in touch exploration state with a finger already // down, so announce the transition to exploration state. - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); clear(); + sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); } } diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index ecc3d2da8226..174ecfa12ee6 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -116,6 +116,7 @@ public abstract class BiometricServiceBase extends SystemService protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>(); // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap. protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>(); + protected int mHALDeathCount; protected class PerformanceStats { public int accept; // number of accepted biometrics @@ -596,6 +597,7 @@ public abstract class BiometricServiceBase extends SystemService public void serviceDied(long cookie) { Slog.e(getTag(), "HAL died"); mMetricsLogger.count(getMetrics().tagHalDied(), 1); + mHALDeathCount++; handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /*vendorCode */); } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 5a9c1aca081a..a2aacdde4d9f 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -675,6 +675,12 @@ public class FaceService extends BiometricServiceBase { } @Override + public void serviceDied(long cookie) { + super.serviceDied(cookie); + mDaemon = null; + } + + @Override protected void updateActiveGroup(int userId, String clientPackage) { IBiometricsFace daemon = getFaceDaemon(); @@ -864,6 +870,8 @@ public class FaceService extends BiometricServiceBase { Slog.e(TAG, "dump formatting failure", e); } pw.println(dump); + pw.println("HAL Deaths: " + mHALDeathCount); + mHALDeathCount = 0; } private void dumpProto(FileDescriptor fd) { diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index 1613dc97225b..3db6a74a1c6c 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -777,6 +777,12 @@ public class FingerprintService extends BiometricServiceBase { } @Override + public void serviceDied(long cookie) { + super.serviceDied(cookie); + mDaemon = null; + } + + @Override protected void updateActiveGroup(int userId, String clientPackage) { IBiometricsFingerprint daemon = getFingerprintDaemon(); @@ -1074,6 +1080,8 @@ public class FingerprintService extends BiometricServiceBase { Slog.e(TAG, "dump formatting failure", e); } pw.println(dump); + pw.println("HAL Deaths: " + mHALDeathCount); + mHALDeathCount = 0; } private void dumpProto(FileDescriptor fd) { diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 3ea9810f2d76..97896889f243 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -282,6 +282,7 @@ public class PacManager { private void setCurrentProxyScript(String script) { if (mProxyService == null) { Log.e(TAG, "setCurrentProxyScript: no proxy service"); + return; } try { mProxyService.setPacFile(script); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index eb4e662b2c82..414f6bbfb995 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -56,11 +56,6 @@ abstract class HdmiCecLocalDevice { // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior. // When it expires, we can assume <User Control Release> is received. private static final int FOLLOWER_SAFETY_TIMEOUT = 550; - /** - * Return value of {@link #getLocalPortFromPhysicalAddress(int)} - */ - private static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1; - private static final int TARGET_SAME_PHYSICAL_ADDRESS = 0; protected final HdmiControlService mService; protected final int mDeviceType; @@ -1061,47 +1056,6 @@ abstract class HdmiCecLocalDevice { pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath)); } - /** - * Method to parse target physical address to the port number on the current device. - * - * <p>This check assumes target address is valid. - * @param targetPhysicalAddress is the physical address of the target device - * @return - * If the target device is under the current device, return the port number of current device - * that the target device is connected to. - * - * <p>If the target device has the same physical address as the current device, return - * {@link #TARGET_SAME_PHYSICAL_ADDRESS}. - * - * <p>If the target device is not under the current device, return - * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}. - */ - protected int getLocalPortFromPhysicalAddress(int targetPhysicalAddress) { - int myPhysicalAddress = mService.getPhysicalAddress(); - if (myPhysicalAddress == targetPhysicalAddress) { - return TARGET_SAME_PHYSICAL_ADDRESS; - } - int finalMask = 0xF000; - int mask; - int port = 0; - for (mask = 0x0F00; mask > 0x000F; mask >>= 4) { - if ((myPhysicalAddress & mask) == 0) { - port = mask & targetPhysicalAddress; - break; - } else { - finalMask |= mask; - } - } - if (finalMask != 0xFFFF && (finalMask & targetPhysicalAddress) == myPhysicalAddress) { - while (mask != 0x000F) { - mask >>= 4; - port >>= 4; - } - return port; - } - return TARGET_NOT_UNDER_LOCAL_DEVICE; - } - /** Calculates the physical address for {@code activePortId}. * * <p>This method assumes current device physical address is valid. diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 71075f3d71ce..63214ed651b9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -41,7 +41,15 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.hdmi.Constants.AudioCodec; import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; +import com.android.server.hdmi.HdmiUtils.CodecSad; +import com.android.server.hdmi.HdmiUtils.DeviceConfig; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; @@ -105,6 +113,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { mTvInputs.put(1, "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7"); } + private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml"; + /** * Called when a device is newly added or a new device is detected or * an existing device is updated. @@ -258,7 +268,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { mTvSystemAudioModeSupport = false; // Record the last state of System Audio Control before going to standby synchronized (mLock) { - SystemProperties.set( + mService.writeStringSetting( Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, mSystemAudioActivated ? "true" : "false"); } @@ -320,7 +330,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected void setPreferredAddress(int addr) { assertRunOnServiceThread(); - SystemProperties.set( + mService.writeStringSetting( Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, String.valueOf(addr)); } @@ -459,7 +469,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { protected boolean handleRequestArcInitiate(HdmiCecMessage message) { assertRunOnServiceThread(); removeAction(ArcInitiationActionFromAvr.class); - if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) { + if (!mService.readBooleanSetting(Constants.PROPERTY_ARC_SUPPORT, true)) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE); } else if (!isDirectConnectToTv()) { HdmiLogger.debug("AVR device is not directly connected with TV"); @@ -498,13 +508,35 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE); return true; } - AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo(); - if (deviceInfo == null) { - mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE); - return true; + + List<DeviceConfig> config = null; + File file = new File(SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH); + if (file.exists()) { + try { + InputStream in = new FileInputStream(file); + config = HdmiUtils.ShortAudioDescriptorXmlParser.parse(in); + in.close(); + } catch (IOException e) { + Slog.e(TAG, "Error reading file: " + file, e); + } catch (XmlPullParserException e) { + Slog.e(TAG, "Unable to parse file: " + file, e); + } } + @AudioCodec int[] audioFormatCodes = parseAudioFormatCodes(message.getParams()); - byte[] sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes); + byte[] sadBytes; + if (config != null && config.size() > 0) { + sadBytes = getSupportedShortAudioDescriptorsFromConfig(config, audioFormatCodes); + } else { + AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo(); + if (deviceInfo == null) { + mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE); + return true; + } + + sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes); + } + if (sadBytes.length == 0) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND); } else { @@ -531,6 +563,42 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } } } + return getShortAudioDescriptorBytes(sads); + } + + private byte[] getSupportedShortAudioDescriptorsFromConfig( + List<DeviceConfig> deviceConfig, @AudioCodec int[] audioFormatCodes) { + DeviceConfig deviceConfigToUse = null; + for (DeviceConfig device : deviceConfig) { + // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name + if (device.name.equals("VX_AUDIO_DEVICE_IN_HDMI_ARC")) { + deviceConfigToUse = device; + break; + } + } + if (deviceConfigToUse == null) { + // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name + Slog.w(TAG, "sadConfig.xml does not have required device info for " + + "VX_AUDIO_DEVICE_IN_HDMI_ARC"); + return new byte[0]; + } + HashMap<Integer, byte[]> map = new HashMap<>(); + ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length); + for (CodecSad codecSad : deviceConfigToUse.supportedCodecs) { + map.put(codecSad.audioCodec, codecSad.sad); + } + for (int i = 0; i < audioFormatCodes.length; i++) { + if (map.containsKey(audioFormatCodes[i])) { + byte[] sad = map.get(audioFormatCodes[i]); + if (sad != null && sad.length == 3) { + sads.add(sad); + } + } + } + return getShortAudioDescriptorBytes(sads); + } + + private byte[] getShortAudioDescriptorBytes(ArrayList<byte[]> sads) { // Short Audio Descriptors are always 3 bytes long. byte[] bytes = new byte[sads.size() * 3]; int index = 0; @@ -761,7 +829,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { boolean currentMuteStatus = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC); if (currentMuteStatus == newSystemAudioMode) { - if (SystemProperties.getBoolean( + if (mService.readBooleanSetting( Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true) || newSystemAudioMode) { mService.getAudioManager() diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 32288de15a00..7a0c27906122 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -100,7 +100,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected void setPreferredAddress(int addr) { assertRunOnServiceThread(); - SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, + mService.writeStringSetting(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, String.valueOf(addr)); } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 486faf3e9fa5..46219d5cbe8a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -646,6 +646,7 @@ public class HdmiControlService extends SystemService { return enabled ? ENABLED : DISABLED; } + @VisibleForTesting boolean readBooleanSetting(String key, boolean defVal) { ContentResolver cr = getContext().getContentResolver(); return Global.getInt(cr, key, toInt(defVal)) == ENABLED; @@ -656,6 +657,11 @@ public class HdmiControlService extends SystemService { Global.putInt(cr, key, toInt(value)); } + void writeStringSetting(String key, String value) { + ContentResolver cr = getContext().getContentResolver(); + Global.putString(cr, key, value); + } + private void initializeCec(int initiatedBy) { mAddressAllocated = false; mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true); diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index 11e557c024ce..e44f1d1522ec 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -16,23 +16,35 @@ package com.android.server.hdmi; +import android.annotation.Nullable; import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; import android.util.SparseArray; +import android.util.Xml; +import com.android.internal.util.HexDump; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.hdmi.Constants.AudioCodec; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; - +import java.util.Objects; /** * Various utilities to handle HDMI CEC messages. */ final class HdmiUtils { + private static final String TAG = "HdmiUtils"; + private static final int[] ADDRESS_TO_TYPE = { HdmiDeviceInfo.DEVICE_TV, // ADDR_TV HdmiDeviceInfo.DEVICE_RECORDER, // ADDR_RECORDER_1 @@ -69,6 +81,12 @@ final class HdmiUtils { "Secondary_TV", }; + /** + * Return value of {@link #getLocalPortFromPhysicalAddress(int, int)} + */ + static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1; + static final int TARGET_SAME_PHYSICAL_ADDRESS = 0; + private HdmiUtils() { /* cannot be instantiated */ } /** @@ -392,6 +410,203 @@ final class HdmiUtils { pw.decreaseIndent(); } + /** + * Method to parse target physical address to the port number on the current device. + * + * <p>This check assumes target address is valid. + * + * @param targetPhysicalAddress is the physical address of the target device + * @param myPhysicalAddress is the physical address of the current device + * @return + * If the target device is under the current device, return the port number of current device + * that the target device is connected to. This also applies to the devices that are indirectly + * connected to the current device. + * + * <p>If the target device has the same physical address as the current device, return + * {@link #TARGET_SAME_PHYSICAL_ADDRESS}. + * + * <p>If the target device is not under the current device, return + * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}. + */ + public static int getLocalPortFromPhysicalAddress( + int targetPhysicalAddress, int myPhysicalAddress) { + if (myPhysicalAddress == targetPhysicalAddress) { + return TARGET_SAME_PHYSICAL_ADDRESS; + } + + int mask = 0xF000; + int finalMask = 0xF000; + int maskedAddress = myPhysicalAddress; + + while (maskedAddress != 0) { + maskedAddress = myPhysicalAddress & mask; + finalMask |= mask; + mask >>= 4; + } + + int portAddress = targetPhysicalAddress & finalMask; + if ((portAddress & (finalMask << 4)) != myPhysicalAddress) { + return TARGET_NOT_UNDER_LOCAL_DEVICE; + } + + mask <<= 4; + int port = portAddress & mask; + while ((port >> 4) != 0) { + port >>= 4; + } + return port; + } + + public static class ShortAudioDescriptorXmlParser { + // We don't use namespaces + private static final String NS = null; + + // return a list of devices config + public static List<DeviceConfig> parse(InputStream in) + throws XmlPullParserException, IOException { + XmlPullParser parser = Xml.newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(in, null); + parser.nextTag(); + return readDevices(parser); + } + + private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } + + private static List<DeviceConfig> readDevices(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<DeviceConfig> devices = new ArrayList<>(); + + parser.require(XmlPullParser.START_TAG, NS, "config"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + // Starts by looking for the device tag + if (name.equals("device")) { + String deviceType = parser.getAttributeValue(null, "type"); + DeviceConfig config = null; + if (deviceType != null) { + config = readDeviceConfig(parser, deviceType); + } + if (config != null) { + devices.add(config); + } + } else { + skip(parser); + } + } + return devices; + } + + // Processes device tags in the config. + @Nullable + private static DeviceConfig readDeviceConfig(XmlPullParser parser, String deviceType) + throws XmlPullParserException, IOException { + List<CodecSad> codecSads = new ArrayList<>(); + int format; + byte[] descriptor; + + parser.require(XmlPullParser.START_TAG, NS, "device"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String tagName = parser.getName(); + + // Starts by looking for the supportedFormat tag + if (tagName.equals("supportedFormat")) { + String codecAttriValue = parser.getAttributeValue(null, "format"); + String sadAttriValue = parser.getAttributeValue(null, "descriptor"); + format = (codecAttriValue) == null + ? Constants.AUDIO_CODEC_NONE : formatNameToNum(codecAttriValue); + descriptor = readSad(sadAttriValue); + if (format != Constants.AUDIO_CODEC_NONE && descriptor != null) { + codecSads.add(new CodecSad(format, descriptor)); + } + parser.nextTag(); + parser.require(XmlPullParser.END_TAG, NS, "supportedFormat"); + } else { + skip(parser); + } + } + if (codecSads.size() == 0) { + return null; + } + return new DeviceConfig(deviceType, codecSads); + } + + // Processes sad attribute in the supportedFormat. + @Nullable + private static byte[] readSad(String sad) { + if (sad == null || sad.length() == 0) { + return null; + } + byte[] sadBytes = HexDump.hexStringToByteArray(sad); + if (sadBytes.length != 3) { + Slog.w(TAG, "SAD byte array length is not 3. Length = " + sadBytes.length); + return null; + } + return sadBytes; + } + + @AudioCodec + private static int formatNameToNum(String codecAttriValue) { + switch (codecAttriValue) { + case "AUDIO_FORMAT_NONE": + return Constants.AUDIO_CODEC_NONE; + case "AUDIO_FORMAT_LPCM": + return Constants.AUDIO_CODEC_LPCM; + case "AUDIO_FORMAT_DD": + return Constants.AUDIO_CODEC_DD; + case "AUDIO_FORMAT_MPEG1": + return Constants.AUDIO_CODEC_MPEG1; + case "AUDIO_FORMAT_MP3": + return Constants.AUDIO_CODEC_MP3; + case "AUDIO_FORMAT_MPEG2": + return Constants.AUDIO_CODEC_MPEG2; + case "AUDIO_FORMAT_AAC": + return Constants.AUDIO_CODEC_AAC; + case "AUDIO_FORMAT_DTS": + return Constants.AUDIO_CODEC_DTS; + case "AUDIO_FORMAT_ATRAC": + return Constants.AUDIO_CODEC_ATRAC; + case "AUDIO_FORMAT_ONEBITAUDIO": + return Constants.AUDIO_CODEC_ONEBITAUDIO; + case "AUDIO_FORMAT_DDP": + return Constants.AUDIO_CODEC_DDP; + case "AUDIO_FORMAT_DTSHD": + return Constants.AUDIO_CODEC_DTSHD; + case "AUDIO_FORMAT_TRUEHD": + return Constants.AUDIO_CODEC_TRUEHD; + case "AUDIO_FORMAT_DST": + return Constants.AUDIO_CODEC_DST; + case "AUDIO_FORMAT_WMAPRO": + return Constants.AUDIO_CODEC_WMAPRO; + case "AUDIO_FORMAT_MAX": + return Constants.AUDIO_CODEC_MAX; + default: + return Constants.AUDIO_CODEC_NONE; + } + } + } + // Device configuration of its supported Codecs and their Short Audio Descriptors. public static class DeviceConfig { /** Name of the device. Should be {@link Constants.AudioDevice}. **/ @@ -399,10 +614,27 @@ final class HdmiUtils { /** List of a {@link CodecSad}. **/ public final List<CodecSad> supportedCodecs; - private DeviceConfig(String name, List<CodecSad> supportedCodecs) { + public DeviceConfig(String name, List<CodecSad> supportedCodecs) { this.name = name; this.supportedCodecs = supportedCodecs; } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DeviceConfig) { + DeviceConfig that = (DeviceConfig) obj; + return that.name.equals(this.name) + && that.supportedCodecs.equals(this.supportedCodecs); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + name, + supportedCodecs.hashCode()); + } } // Short Audio Descriptor of a specific Codec @@ -419,5 +651,27 @@ final class HdmiUtils { this.audioCodec = audioCodec; this.sad = sad; } + + public CodecSad(int audioCodec, String sad) { + this.audioCodec = audioCodec; + this.sad = HexDump.hexStringToByteArray(sad); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof CodecSad) { + CodecSad that = (CodecSad) obj; + return that.audioCodec == this.audioCodec + && Arrays.equals(that.sad, this.sad); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash( + audioCodec, + Arrays.hashCode(sad)); + } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6baf12ce3b44..70ead41853d0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1342,6 +1342,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mSystemTextClassifierPackage; final @Nullable String mWellbeingPackage; final @Nullable String mDocumenterPackage; + final @Nullable String mConfiguratorPackage; final @NonNull String mServicesSystemSharedLibraryPackageName; final @NonNull String mSharedSystemSharedLibraryPackageName; @@ -2865,6 +2866,8 @@ public class PackageManagerService extends IPackageManager.Stub mWellbeingPackage = getWellbeingPackageName(); mDocumenterPackage = getDocumenterPackageName(); + mConfiguratorPackage = + mContext.getString(R.string.config_deviceConfiguratorPackageName); // Now that we know all of the shared libraries, update all clients to have // the correct library paths. @@ -23122,6 +23125,8 @@ public class PackageManagerService extends IPackageManager.Stub return mWellbeingPackage; case PackageManagerInternal.PACKAGE_DOCUMENTER: return mDocumenterPackage; + case PackageManagerInternal.PACKAGE_CONFIGURATOR: + return mConfiguratorPackage; } return null; } diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 3a49412357d8..17f83479a3b1 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -247,6 +247,10 @@ public final class BasePermission { public boolean isDocumenter() { return (protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0; } + public boolean isConfigurator() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) + != 0; + } public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) { if (!origPackageName.equals(sourcePackageName)) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 93964cb09ae6..30b5e49bc3fd 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1640,6 +1640,13 @@ public class PermissionManagerService { // Special permissions for the system default text classifier. allowed = true; } + if (!allowed && bp.isConfigurator() + && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName( + PackageManagerInternal.PACKAGE_CONFIGURATOR, + UserHandle.USER_SYSTEM))) { + // Special permissions for the device configurator. + allowed = true; + } if (!allowed && bp.isWellbeing() && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName( PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4bc241665023..6b111a0fa4fb 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -480,6 +480,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mShortPressOnSleepBehavior; int mShortPressOnWindowBehavior; boolean mHasSoftInput = false; + boolean mHapticTextHandleEnabled; boolean mUseTvRouting; int mVeryLongPressTimeout; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; @@ -567,6 +568,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mScreenshotChordPowerKeyTriggered; private long mScreenshotChordPowerKeyTime; + private static final long MOVING_DISPLAY_TO_TOP_DURATION_MILLIS = 10; + private volatile boolean mMovingDisplayToTopKeyTriggered; + private volatile long mMovingDisplayToTopKeyTime; + // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up private int mRingerToggleChord = VOLUME_HUSH_OFF; @@ -606,7 +611,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mAodShowing; private boolean mPerDisplayFocusEnabled = false; - private int mTopFocusedDisplayId = INVALID_DISPLAY; + private volatile int mTopFocusedDisplayId = INVALID_DISPLAY; private static final int MSG_ENABLE_POINTER_LOCATION = 1; private static final int MSG_DISABLE_POINTER_LOCATION = 2; @@ -634,6 +639,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_POWER_VERY_LONG_PRESS = 25; private static final int MSG_NOTIFY_USER_ACTIVITY = 26; private static final int MSG_RINGER_TOGGLE_CHORD = 27; + private static final int MSG_MOVE_DISPLAY_TO_TOP = 28; private class PolicyHandler extends Handler { @Override @@ -729,6 +735,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_RINGER_TOGGLE_CHORD: handleRingerChordGesture(); break; + case MSG_MOVE_DISPLAY_TO_TOP: + mWindowManagerFuncs.moveDisplayToTop(msg.arg1); + mMovingDisplayToTopKeyTriggered = false; + break; } } } @@ -1827,6 +1837,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup); + mHapticTextHandleEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enableHapticTextHandle); + mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION; mHandleVolumeKeysInWM = mContext.getResources().getBoolean( @@ -2570,12 +2583,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int eventDisplayId = event.getDisplayId(); if (result == 0 && !mPerDisplayFocusEnabled && eventDisplayId != INVALID_DISPLAY && eventDisplayId != mTopFocusedDisplayId) { - // Someone tries to send a key event to a display which doesn't have a focused window. - // We drop the event here, or it will cause ANR. - // TODO (b/121057974): The user may be confused about why the key doesn't work, so we - // may need to deal with this problem. - Slog.i(TAG, "Dropping this event targeting display #" + eventDisplayId - + " because the focus is on display #" + mTopFocusedDisplayId); + // An event is targeting a non-focused display. Try to move the display to top so that + // it can become the focused display to interact with the user. + final long eventDownTime = event.getDownTime(); + if (mMovingDisplayToTopKeyTime < eventDownTime) { + // We have not handled this event yet. Move the display to top, and then tell + // dispatcher to try again later. + mMovingDisplayToTopKeyTime = eventDownTime; + mMovingDisplayToTopKeyTriggered = true; + mHandler.sendMessage( + mHandler.obtainMessage(MSG_MOVE_DISPLAY_TO_TOP, eventDisplayId, 0)); + return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS; + } else if (mMovingDisplayToTopKeyTriggered) { + // The message has not been handled yet. Tell dispatcher to try again later. + return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS; + } + // The target display is still not the top focused display. Drop the event because the + // display may not contain any window which can receive keys. + Slog.w(TAG, "Dropping key targeting non-focused display #" + eventDisplayId + + " keyCode=" + KeyEvent.keyCodeToString(event.getKeyCode())); return -1; } return result; @@ -5179,8 +5205,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { case HapticFeedbackConstants.CLOCK_TICK: case HapticFeedbackConstants.CONTEXT_CLICK: return VibrationEffect.get(VibrationEffect.EFFECT_TICK); - case HapticFeedbackConstants.KEYBOARD_RELEASE: case HapticFeedbackConstants.TEXT_HANDLE_MOVE: + if (!mHapticTextHandleEnabled) { + return null; + } + case HapticFeedbackConstants.KEYBOARD_RELEASE: case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: case HapticFeedbackConstants.ENTRY_BUMP: case HapticFeedbackConstants.DRAG_CROSSING: @@ -5345,11 +5374,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup); pw.print(prefix); pw.print("mHasSoftInput="); pw.print(mHasSoftInput); - pw.print(" mDismissImeOnBackKeyPressed="); pw.println(mDismissImeOnBackKeyPressed); + pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled); + pw.print(prefix); + pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed); + pw.print(" mIncallPowerBehavior="); + pw.println(incallPowerBehaviorToString(mIncallPowerBehavior)); pw.print(prefix); - pw.print("mIncallPowerBehavior="); - pw.print(incallPowerBehaviorToString(mIncallPowerBehavior)); - pw.print(" mIncallBackBehavior="); + pw.print("mIncallBackBehavior="); pw.print(incallBackBehaviorToString(mIncallBackBehavior)); pw.print(" mEndcallBehavior="); pw.println(endcallBehaviorToString(mEndcallBehavior)); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 3da325c55b32..c37254b22ea5 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -634,6 +634,12 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * Notifies window manager that user is switched. */ void onUserSwitched(); + + /** + * Hint to window manager that the user is interacting with a display that should be treated + * as the top display. + */ + void moveDisplayToTop(int displayId); } /** diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index 06168461ca1e..65d66f44b5dd 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -625,6 +625,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> return; } + // Collect the stacks that are necessary to be removed instead of performing the removal + // by looping mStacks, so that we don't miss any stacks after the stack size changed or + // stacks reordered. + final ArrayList<ActivityStack> stacks = new ArrayList<>(); for (int j = windowingModes.length - 1 ; j >= 0; --j) { final int windowingMode = windowingModes[j]; for (int i = mStacks.size() - 1; i >= 0; --i) { @@ -635,9 +639,13 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> if (stack.getWindowingMode() != windowingMode) { continue; } - mRootActivityContainer.mStackSupervisor.removeStack(stack); + stacks.add(stack); } } + + for (int i = stacks.size() - 1; i >= 0; --i) { + mRootActivityContainer.mStackSupervisor.removeStack(stacks.get(i)); + } } void removeStacksWithActivityTypes(int... activityTypes) { @@ -645,15 +653,23 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> return; } + // Collect the stacks that are necessary to be removed instead of performing the removal + // by looping mStacks, so that we don't miss any stacks after the stack size changed or + // stacks reordered. + final ArrayList<ActivityStack> stacks = new ArrayList<>(); for (int j = activityTypes.length - 1 ; j >= 0; --j) { final int activityType = activityTypes[j]; for (int i = mStacks.size() - 1; i >= 0; --i) { final ActivityStack stack = mStacks.get(i); if (stack.getActivityType() == activityType) { - mRootActivityContainer.mStackSupervisor.removeStack(stack); + stacks.add(stack); } } } + + for (int i = stacks.size() - 1; i >= 0; --i) { + mRootActivityContainer.mStackSupervisor.removeStack(stacks.get(i)); + } } void onStackWindowingModeChanged(ActivityStack stack) { diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index a4cda5aac895..3a288ca5560d 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2641,6 +2641,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { try { mService.moveTaskToFrontLocked(task.taskId, 0, options, true /* fromRecents */); + // Apply options to prevent pendingOptions be taken by client to make sure + // the override pending app transition will be applied immediately. + targetActivity.applyOptionsLocked(); } finally { mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, targetActivity); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 937c9d9fc809..58cf73a9a2bd 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -47,10 +47,9 @@ import android.view.IWindowId; import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InputChannel; -import android.view.Surface; +import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceSession; -import android.view.InsetsState; import android.view.WindowManager; import com.android.internal.os.logging.MetricsLoggerWrapper; @@ -432,7 +431,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void insetsModified(IWindow window, InsetsState state) { - synchronized (mService.mWindowMap) { + synchronized (mService.mGlobalLock) { final WindowState windowState = mService.windowForClientLocked(this, window, false /* throwOnError */); if (windowState != null) { diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index b2194190f4f4..905787051de8 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -67,7 +67,7 @@ public class TaskTapPointerEventListener implements PointerEventListener { return; } WindowContainer parent = mDisplayContent.getParent(); - if (parent != null) { + if (parent != null && parent.getTopChild() != mDisplayContent) { parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent, true /* includingParents */); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 25e61f876722..19058776d187 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -437,6 +437,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mChildren.peekLast() != child) { mChildren.remove(child); mChildren.add(child); + onChildPositionChanged(); } if (includingParents && getParent() != null) { getParent().positionChildAt(POSITION_TOP, this /* child */, @@ -447,6 +448,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mChildren.peekFirst() != child) { mChildren.remove(child); mChildren.addFirst(child); + onChildPositionChanged(); } if (includingParents && getParent() != null) { getParent().positionChildAt(POSITION_BOTTOM, this /* child */, @@ -460,8 +462,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // doing this adjustment here and remove any adjustments in the callers. mChildren.remove(child); mChildren.add(position, child); + onChildPositionChanged(); } - onChildPositionChanged(); } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 90506e744250..0c4f4961d215 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -723,7 +723,7 @@ public class WindowManagerService extends IWindowManager.Stub void updateSystemUiSettings() { boolean changed; - synchronized (mWindowMap) { + synchronized (mGlobalLock) { changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext) || PolicyControl.reloadFromSetting(mContext); } @@ -2629,12 +2629,23 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void onUserSwitched() { mSettingsObserver.updateSystemUiSettings(); - synchronized (mWindowMap) { + synchronized (mGlobalLock) { // force a re-application of focused window sysui visibility on each display. mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemUiVisibilityLw); } } + @Override + public void moveDisplayToTop(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null && mRoot.getTopChild() != displayContent) { + mRoot.positionChildAt(WindowContainer.POSITION_TOP, displayContent, + true /* includingParents */); + } + } + } + /** * Starts deferring layout passes. Useful when doing multiple changes but to optimize * performance, only one layout pass should be done. This can be called multiple times, and @@ -6379,7 +6390,7 @@ public class WindowManagerService extends IWindowManager.Stub } void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) { - synchronized (mWindowMap) { + synchronized (mGlobalLock) { mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays; } } diff --git a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java new file mode 100644 index 000000000000..5b77f543c62b --- /dev/null +++ b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import static android.net.shared.ParcelableUtil.fromParcelableArray; +import static android.net.shared.ParcelableUtil.toParcelableArray; + +import android.annotation.Nullable; +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.IpPrefixParcelable; +import android.net.LinkAddress; +import android.net.LinkAddressParcelable; +import android.net.LinkProperties; +import android.net.LinkPropertiesParcelable; +import android.net.ProxyInfo; +import android.net.ProxyInfoParcelable; +import android.net.RouteInfo; +import android.net.RouteInfoParcelable; +import android.net.Uri; + +import java.net.InetAddress; +import java.util.Arrays; + +/** + * Collection of utility methods to convert to and from stable AIDL parcelables for LinkProperties + * and its attributes. + * @hide + */ +public final class LinkPropertiesParcelableUtil { + + /** + * Convert a ProxyInfo to a ProxyInfoParcelable + */ + public static ProxyInfoParcelable toStableParcelable(@Nullable ProxyInfo proxyInfo) { + if (proxyInfo == null) { + return null; + } + final ProxyInfoParcelable parcel = new ProxyInfoParcelable(); + parcel.host = proxyInfo.getHost(); + parcel.port = proxyInfo.getPort(); + parcel.exclusionList = proxyInfo.getExclusionList(); + parcel.pacFileUrl = proxyInfo.getPacFileUrl().toString(); + return parcel; + } + + /** + * Convert a ProxyInfoParcelable to a ProxyInfo + */ + public static ProxyInfo fromStableParcelable(@Nullable ProxyInfoParcelable parcel) { + if (parcel == null) { + return null; + } + if (Uri.EMPTY.toString().equals(parcel.pacFileUrl)) { + return ProxyInfo.buildDirectProxy( + parcel.host, parcel.port, Arrays.asList(parcel.exclusionList)); + } else { + return ProxyInfo.buildPacProxy(Uri.parse(parcel.pacFileUrl)); + } + } + + /** + * Convert an IpPrefixParcelable to an IpPrefix + */ + public static IpPrefixParcelable toStableParcelable(@Nullable IpPrefix ipPrefix) { + if (ipPrefix == null) { + return null; + } + final IpPrefixParcelable parcel = new IpPrefixParcelable(); + parcel.address = ipPrefix.getAddress().getHostAddress(); + parcel.prefixLength = ipPrefix.getPrefixLength(); + return parcel; + } + + /** + * Convert an IpPrefix to an IpPrefixParcelable + */ + public static IpPrefix fromStableParcelable(@Nullable IpPrefixParcelable parcel) { + if (parcel == null) { + return null; + } + return new IpPrefix(InetAddresses.parseNumericAddress(parcel.address), parcel.prefixLength); + } + + /** + * Convert a RouteInfoParcelable to a RouteInfo + */ + public static RouteInfoParcelable toStableParcelable(@Nullable RouteInfo routeInfo) { + if (routeInfo == null) { + return null; + } + final RouteInfoParcelable parcel = new RouteInfoParcelable(); + parcel.destination = toStableParcelable(routeInfo.getDestination()); + parcel.gatewayAddr = routeInfo.getGateway().getHostAddress(); + parcel.ifaceName = routeInfo.getInterface(); + parcel.type = routeInfo.getType(); + return parcel; + } + + /** + * Convert a RouteInfo to a RouteInfoParcelable + */ + public static RouteInfo fromStableParcelable(@Nullable RouteInfoParcelable parcel) { + if (parcel == null) { + return null; + } + final IpPrefix destination = fromStableParcelable(parcel.destination); + return new RouteInfo( + destination, InetAddresses.parseNumericAddress(parcel.gatewayAddr), + parcel.ifaceName, parcel.type); + } + + /** + * Convert a LinkAddressParcelable to a LinkAddress + */ + public static LinkAddressParcelable toStableParcelable(@Nullable LinkAddress la) { + if (la == null) { + return null; + } + final LinkAddressParcelable parcel = new LinkAddressParcelable(); + parcel.address = la.getAddress().getHostAddress(); + parcel.prefixLength = la.getPrefixLength(); + parcel.flags = la.getFlags(); + parcel.scope = la.getScope(); + return parcel; + } + + /** + * Convert a LinkAddress to a LinkAddressParcelable + */ + public static LinkAddress fromStableParcelable(@Nullable LinkAddressParcelable parcel) { + if (parcel == null) { + return null; + } + return new LinkAddress( + InetAddresses.parseNumericAddress(parcel.address), + parcel.prefixLength, + parcel.flags, + parcel.scope); + } + + /** + * Convert a LinkProperties to a LinkPropertiesParcelable + */ + public static LinkPropertiesParcelable toStableParcelable(@Nullable LinkProperties lp) { + if (lp == null) { + return null; + } + final LinkPropertiesParcelable parcel = new LinkPropertiesParcelable(); + parcel.ifaceName = lp.getInterfaceName(); + parcel.linkAddresses = toParcelableArray( + lp.getLinkAddresses(), + LinkPropertiesParcelableUtil::toStableParcelable, + LinkAddressParcelable.class); + parcel.dnses = toParcelableArray( + lp.getDnsServers(), InetAddress::getHostAddress, String.class); + parcel.pcscfs = toParcelableArray( + lp.getPcscfServers(), InetAddress::getHostAddress, String.class); + parcel.validatedPrivateDnses = toParcelableArray( + lp.getValidatedPrivateDnsServers(), InetAddress::getHostAddress, String.class); + parcel.usePrivateDns = lp.isPrivateDnsActive(); + parcel.privateDnsServerName = lp.getPrivateDnsServerName(); + parcel.domains = lp.getDomains(); + parcel.routes = toParcelableArray( + lp.getRoutes(), LinkPropertiesParcelableUtil::toStableParcelable, + RouteInfoParcelable.class); + parcel.httpProxy = toStableParcelable(lp.getHttpProxy()); + parcel.mtu = lp.getMtu(); + parcel.tcpBufferSizes = lp.getTcpBufferSizes(); + parcel.nat64Prefix = toStableParcelable(lp.getNat64Prefix()); + parcel.stackedLinks = toParcelableArray( + lp.getStackedLinks(), LinkPropertiesParcelableUtil::toStableParcelable, + LinkPropertiesParcelable.class); + return parcel; + } + + /** + * Convert a LinkPropertiesParcelable to a LinkProperties + */ + public static LinkProperties fromStableParcelable(@Nullable LinkPropertiesParcelable parcel) { + if (parcel == null) { + return null; + } + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(parcel.ifaceName); + lp.setLinkAddresses(fromParcelableArray(parcel.linkAddresses, + LinkPropertiesParcelableUtil::fromStableParcelable)); + lp.setDnsServers(fromParcelableArray(parcel.dnses, InetAddresses::parseNumericAddress)); + lp.setPcscfServers(fromParcelableArray(parcel.pcscfs, InetAddresses::parseNumericAddress)); + lp.setValidatedPrivateDnsServers( + fromParcelableArray(parcel.validatedPrivateDnses, + InetAddresses::parseNumericAddress)); + lp.setUsePrivateDns(parcel.usePrivateDns); + lp.setPrivateDnsServerName(parcel.privateDnsServerName); + lp.setDomains(parcel.domains); + for (RouteInfoParcelable route : parcel.routes) { + lp.addRoute(fromStableParcelable(route)); + } + lp.setHttpProxy(fromStableParcelable(parcel.httpProxy)); + lp.setMtu(parcel.mtu); + lp.setTcpBufferSizes(parcel.tcpBufferSizes); + lp.setNat64Prefix(fromStableParcelable(parcel.nat64Prefix)); + for (LinkPropertiesParcelable stackedLink : parcel.stackedLinks) { + lp.addStackedLink(fromStableParcelable(stackedLink)); + } + return lp; + } +} diff --git a/services/net/java/android/net/shared/ParcelableUtil.java b/services/net/java/android/net/shared/ParcelableUtil.java new file mode 100644 index 000000000000..a18976c9eee6 --- /dev/null +++ b/services/net/java/android/net/shared/ParcelableUtil.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import android.annotation.NonNull; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +/** + * Utility methods to help convert to/from stable parcelables. + * @hide + */ +public final class ParcelableUtil { + // Below methods could be implemented easily with streams, but streams are frowned upon in + // frameworks code. + + /** + * Convert a list of BaseType items to an array of ParcelableType items using the specified + * converter function. + */ + public static <ParcelableType, BaseType> ParcelableType[] toParcelableArray( + @NonNull List<BaseType> base, + @NonNull Function<BaseType, ParcelableType> conv, + @NonNull Class<ParcelableType> parcelClass) { + final ParcelableType[] out = (ParcelableType[]) Array.newInstance(parcelClass, base.size()); + int i = 0; + for (BaseType b : base) { + out[i] = conv.apply(b); + i++; + } + return out; + } + + /** + * Convert an array of ParcelableType items to a list of BaseType items using the specified + * converter function. + */ + public static <ParcelableType, BaseType> ArrayList<BaseType> fromParcelableArray( + @NonNull ParcelableType[] parceled, @NonNull Function<ParcelableType, BaseType> conv) { + final ArrayList<BaseType> out = new ArrayList<>(parceled.length); + for (ParcelableType t : parceled) { + out.add(conv.apply(t)); + } + return out; + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java index 8b0e8abf069d..eb9b98ec65f7 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java @@ -30,6 +30,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -147,6 +148,7 @@ public class ArcInitiationActionFromAvrTest { mTestLooper.dispatchAll(); } + @Ignore("b/120845532") @Test public void arcInitiation_requestActiveSource() { mSendCecCommandSuccess = true; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java index 93a09dc3ff04..5d8131f35eb7 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java @@ -40,7 +40,6 @@ import androidx.test.filters.SmallTest; import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -142,7 +141,6 @@ public class HdmiCecControllerTest { assertEquals(ADDR_UNREGISTERED, mLogicalAddress); } - @Ignore("b/110413065 Support multiple device types 4 and 5.") @Test public void testAllocatLogicalAddress_PlaybackPreferredNotOccupied() { mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback); @@ -158,7 +156,6 @@ public class HdmiCecControllerTest { assertEquals(ADDR_PLAYBACK_2, mLogicalAddress); } - @Ignore("b/110413065 Support multiple device types 4 and 5.") @Test public void testAllocatLogicalAddress_PlaybackNoPreferredNotOcuppied() { mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java index 3b51a2a70f83..3a6cdc2ad7c9 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java @@ -21,6 +21,7 @@ import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_UPDATE_DEVIC import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; import static com.android.server.hdmi.Constants.ADDR_TUNER_1; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -29,9 +30,9 @@ import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF; import static com.google.common.truth.Truth.assertThat; import android.hardware.hdmi.HdmiDeviceInfo; +import android.hardware.hdmi.HdmiPortInfo; import android.media.AudioManager; import android.os.Looper; -import android.os.SystemProperties; import android.os.test.TestLooper; import androidx.test.InstrumentationRegistry; @@ -46,7 +47,6 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.ArrayList; - @SmallTest @RunWith(JUnit4.class) /** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */ @@ -67,9 +67,15 @@ public class HdmiCecLocalDeviceAudioSystemTest { private int mMusicVolume; private int mMusicMaxVolume; private boolean mMusicMute; - private int mAvrPhysicalAddress; + private static final int SELF_PHYSICAL_ADDRESS = 0x2000; + private static final int HDMI_1_PHYSICAL_ADDRESS = 0x2100; + private static final int HDMI_2_PHYSICAL_ADDRESS = 0x2200; + private static final int HDMI_3_PHYSICAL_ADDRESS = 0x2300; private int mInvokeDeviceEventState; private HdmiDeviceInfo mDeviceInfo; + private boolean mMutingEnabled; + private boolean mArcSupport; + private HdmiPortInfo[] mHdmiPortInfo; @Before public void setUp() { @@ -141,9 +147,20 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Override - int pathToPortId(int path) { - // port id is not useful for the test right now - return 1; + void writeStringSetting(String key, String value) { + // do nothing + } + + @Override + boolean readBooleanSetting(String key, boolean defVal) { + switch (key) { + case Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE: + return mMutingEnabled; + case Constants.PROPERTY_ARC_SUPPORT: + return mArcSupport; + default: + return defVal; + } } }; @@ -154,11 +171,17 @@ public class HdmiCecLocalDeviceAudioSystemTest { void setIsActiveSource(boolean on) { mIsActiveSource = on; } + + @Override + protected int getPreferredAddress() { + return ADDR_PLAYBACK_1; + } }; mHdmiCecLocalDeviceAudioSystem.init(); mHdmiCecLocalDevicePlayback.init(); mHdmiControlService.setIoLooper(mMyLooper); mNativeWrapper = new FakeNativeWrapper(); + mNativeWrapper.setPhysicalAddress(SELF_PHYSICAL_ADDRESS); mHdmiCecController = HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper); mHdmiControlService.setCecController(mHdmiCecController); @@ -166,15 +189,28 @@ public class HdmiCecLocalDeviceAudioSystemTest { mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService)); mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem); mLocalDevices.add(mHdmiCecLocalDevicePlayback); + mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnables(true); + mHdmiPortInfo = new HdmiPortInfo[4]; + mHdmiPortInfo[0] = + new HdmiPortInfo( + 0, HdmiPortInfo.PORT_INPUT, SELF_PHYSICAL_ADDRESS, true, false, false); + mHdmiPortInfo[1] = + new HdmiPortInfo( + 2, HdmiPortInfo.PORT_INPUT, HDMI_1_PHYSICAL_ADDRESS, true, false, false); + mHdmiPortInfo[2] = + new HdmiPortInfo( + 1, HdmiPortInfo.PORT_INPUT, HDMI_2_PHYSICAL_ADDRESS, true, false, false); + mHdmiPortInfo[3] = + new HdmiPortInfo( + 4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false); + mNativeWrapper.setPortInfo(mHdmiPortInfo); mHdmiControlService.initPortInfo(); // No TV device interacts with AVR so system audio control won't be turned on here mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); mNativeWrapper.clearResultMessages(); - mAvrPhysicalAddress = 0x2000; - mNativeWrapper.setPhysicalAddress(mAvrPhysicalAddress); - SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true"); - SystemProperties.set(Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, "true"); + mMutingEnabled = true; + mArcSupport = true; mInvokeDeviceEventState = 0; mDeviceInfo = null; } @@ -244,6 +280,8 @@ public class HdmiCecLocalDeviceAudioSystemTest { assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); } + // Testing device has sadConfig.xml + @Ignore("b/120845532") @Test public void handleRequestShortAudioDescriptor_noAudioDeviceInfo() throws Exception { HdmiCecMessage expectedMessage = @@ -414,51 +452,6 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Test - public void pathToPort_isMe() throws Exception { - int targetPhysicalAddress = 0x1000; - mNativeWrapper.setPhysicalAddress(0x1000); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(0); - } - - @Test - public void pathToPort_isBelow() throws Exception { - int targetPhysicalAddress = 0x1100; - mNativeWrapper.setPhysicalAddress(0x1000); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(1); - } - - @Test - public void pathToPort_neitherMeNorBelow() throws Exception { - int targetPhysicalAddress = 0x3000; - mNativeWrapper.setPhysicalAddress(0x2000); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(-1); - - targetPhysicalAddress = 0x2200; - mNativeWrapper.setPhysicalAddress(0x3300); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(-1); - - targetPhysicalAddress = 0x2213; - mNativeWrapper.setPhysicalAddress(0x2212); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(-1); - - targetPhysicalAddress = 0x2340; - mNativeWrapper.setPhysicalAddress(0x2310); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getLocalPortFromPhysicalAddress(targetPhysicalAddress)) - .isEqualTo(-1); - } - - @Test public void handleRequestArcInitiate_isNotDirectConnectedToTv() throws Exception { HdmiCecMessage message = HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM); @@ -530,7 +523,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { ADDR_TV, Constants.MESSAGE_REQUEST_ARC_INITIATION, Constants.ABORT_UNRECOGNIZED_OPCODE); - SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "false"); + mArcSupport = false; assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue(); mTestLooper.dispatchAll(); @@ -541,7 +534,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { HdmiCecMessage message = HdmiCecMessageBuilder.buildSystemAudioModeRequest( ADDR_TUNER_1, ADDR_AUDIO_SYSTEM, - mAvrPhysicalAddress, true); + SELF_PHYSICAL_ADDRESS, true); HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand( ADDR_AUDIO_SYSTEM, @@ -559,7 +552,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { HdmiCecMessage message = HdmiCecMessageBuilder.buildSystemAudioModeRequest( ADDR_TUNER_1, ADDR_AUDIO_SYSTEM, - mAvrPhysicalAddress, true); + SELF_PHYSICAL_ADDRESS, true); HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildSetSystemAudioMode( ADDR_AUDIO_SYSTEM, Constants.ADDR_BROADCAST, true); @@ -585,15 +578,14 @@ public class HdmiCecLocalDeviceAudioSystemTest { .isEqualTo(expectedActiveSource); } - @Ignore("b/110413065 Support multiple device types 4 and 5.") @Test public void handleRoutingChange_currentActivePortIsHome() { HdmiCecMessage message = - HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, mAvrPhysicalAddress); + HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, SELF_PHYSICAL_ADDRESS); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, mAvrPhysicalAddress); - ActiveSource expectedActiveSource = ActiveSource.of(ADDR_PLAYBACK_1, mAvrPhysicalAddress); + HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, SELF_PHYSICAL_ADDRESS); + ActiveSource expectedActiveSource = ActiveSource.of(ADDR_PLAYBACK_1, SELF_PHYSICAL_ADDRESS); int expectedLocalActivePort = Constants.CEC_SWITCH_HOME; assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingChange(message)).isTrue(); @@ -608,17 +600,18 @@ public class HdmiCecLocalDeviceAudioSystemTest { @Test public void handleRoutingInformation_currentActivePortIsHDMI1() { HdmiCecMessage message = - HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x2000); - mHdmiCecLocalDeviceAudioSystem.setRoutingPort(Constants.CEC_SWITCH_HDMI1); + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, SELF_PHYSICAL_ADDRESS); + mHdmiCecLocalDeviceAudioSystem.setRoutingPort(mHdmiPortInfo[1].getId()); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildRoutingInformation(ADDR_AUDIO_SYSTEM, 0x2100); + HdmiCecMessageBuilder.buildRoutingInformation( + ADDR_AUDIO_SYSTEM, HDMI_1_PHYSICAL_ADDRESS); assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingInformation(message)).isTrue(); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); } - @Ignore("b/110413065 Support multiple device types 4 and 5.") + @Ignore("b/120845532") @Test public void handleRoutingChange_homeIsActive_playbackSendActiveSource() { HdmiCecMessage message = @@ -667,7 +660,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice); HdmiDeviceInfo differentDevice = new HdmiDeviceInfo( - ADDR_PLAYBACK_1, 0x2100, 4, HdmiDeviceInfo.DEVICE_PLAYBACK, + ADDR_PLAYBACK_1, 0x2300, 4, HdmiDeviceInfo.DEVICE_PLAYBACK, Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1)); mHdmiCecLocalDeviceAudioSystem.updateCecDevice(differentDevice); @@ -686,14 +679,13 @@ public class HdmiCecLocalDeviceAudioSystemTest { mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice); HdmiDeviceInfo differentDevice = new HdmiDeviceInfo( - ADDR_PLAYBACK_1, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK, - Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1)); + ADDR_PLAYBACK_2, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK, + Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_2)); HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder .buildReportPhysicalAddressCommand( - ADDR_PLAYBACK_1, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK); + ADDR_PLAYBACK_2, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK); mHdmiCecLocalDeviceAudioSystem.handleReportPhysicalAddress(reportPhysicalAddress); - mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice); mTestLooper.dispatchAll(); assertThat(mDeviceInfo).isEqualTo(differentDevice); assertThat(mHdmiCecLocalDeviceAudioSystem diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 792c617b99ea..feae4eed7eb1 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -81,7 +81,8 @@ public class HdmiCecLocalDevicePlaybackTest { mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress); } - @Ignore + // Playback device does not handle routing control related feature right now + @Ignore("b/120845532") @Test public void handleSetStreamPath_underCurrentDevice() { assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(0); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 67ce13fdef72..1f660742122d 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -179,6 +179,7 @@ public class HdmiControlServiceTest { @Test public void pathToPort_pathExists_weAreNonTv() { mNativeWrapper.setPhysicalAddress(0x2000); + mHdmiControlService.initPortInfo(); assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(1); assertThat(mHdmiControlService.pathToPortId(0x2234)).isEqualTo(2); } @@ -186,6 +187,7 @@ public class HdmiControlServiceTest { @Test public void pathToPort_pathExists_weAreTv() { mNativeWrapper.setPhysicalAddress(0x0000); + mHdmiControlService.initPortInfo(); assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(3); assertThat(mHdmiControlService.pathToPortId(0x3234)).isEqualTo(4); } @@ -193,6 +195,7 @@ public class HdmiControlServiceTest { @Test public void pathToPort_pathInvalid() { mNativeWrapper.setPhysicalAddress(0x2000); + mHdmiControlService.initPortInfo(); assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID); } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java new file mode 100644 index 000000000000..985c6476d767 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.hdmi; + +import static com.google.common.truth.Truth.assertThat; + +import android.util.Slog; + +import androidx.test.filters.SmallTest; + +import com.android.server.hdmi.HdmiUtils.CodecSad; +import com.android.server.hdmi.HdmiUtils.DeviceConfig; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +@SmallTest +@RunWith(JUnit4.class) +/** Tests for {@link HdmiUtils} class. */ +public class HdmiUtilsTest { + + private static final String TAG = "HdmiUtilsTest"; + + private final String mExampleXML = + "<!-- A sample Short Audio Descriptor configuration xml -->" + + "<config version=\"1.0\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">" + + "<device type=\"VX_AUDIO_DEVICE_IN_HDMI_ARC\">" + + "<supportedFormat format=\"AUDIO_FORMAT_LPCM\" descriptor=\"011a03\"/>" + + "<supportedFormat format=\"AUDIO_FORMAT_DD\" descriptor=\"0d0506\"/>" + + "</device>" + + "<device type=\"AUDIO_DEVICE_IN_SPDIF\">" + + "<supportedFormat format=\"AUDIO_FORMAT_LPCM\" descriptor=\"010203\"/>" + + "<supportedFormat format=\"AUDIO_FORMAT_DD\" descriptor=\"040506\"/>" + + "</device>" + + "</config>"; + + @Test + public void pathToPort_isMe() { + int targetPhysicalAddress = 0x1000; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_SAME_PHYSICAL_ADDRESS); + } + + @Test + public void pathToPort_isDirectlyBelow() { + int targetPhysicalAddress = 0x1100; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1); + } + + @Test + public void pathToPort_isBelow() { + int targetPhysicalAddress = 0x1110; + int myPhysicalAddress = 0x1000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1); + } + + @Test + public void pathToPort_neitherMeNorBelow() { + int targetPhysicalAddress = 0x3000; + int myPhysicalAddress = 0x2000; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2200; + myPhysicalAddress = 0x3300; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2213; + myPhysicalAddress = 0x2212; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + + targetPhysicalAddress = 0x2340; + myPhysicalAddress = 0x2310; + assertThat(HdmiUtils.getLocalPortFromPhysicalAddress( + targetPhysicalAddress, myPhysicalAddress)).isEqualTo( + HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE); + } + + @Test + public void parseSampleXML() { + List<DeviceConfig> config = new ArrayList<>(); + try { + config = HdmiUtils.ShortAudioDescriptorXmlParser.parse( + new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8))); + } catch (IOException e) { + Slog.e(TAG, e.getMessage(), e); + } catch (XmlPullParserException e) { + Slog.e(TAG, e.getMessage(), e); + } + + CodecSad expectedCodec1 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "011a03"); + CodecSad expectedCodec2 = new CodecSad(Constants.AUDIO_CODEC_DD, "0d0506"); + CodecSad expectedCodec3 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "010203"); + CodecSad expectedCodec4 = new CodecSad(Constants.AUDIO_CODEC_DD, "040506"); + + List<CodecSad> expectedList1 = new ArrayList<>(); + expectedList1.add(expectedCodec1); + expectedList1.add(expectedCodec2); + + List<CodecSad> expectedList2 = new ArrayList<>(); + expectedList2.add(expectedCodec3); + expectedList2.add(expectedCodec4); + + DeviceConfig expectedDevice1 = new DeviceConfig( + "VX_AUDIO_DEVICE_IN_HDMI_ARC", expectedList1); + DeviceConfig expectedDevice2 = new DeviceConfig( + "AUDIO_DEVICE_IN_SPDIF", expectedList2); + + List<DeviceConfig> expectedConfig = new ArrayList<>(); + expectedConfig.add(expectedDevice1); + expectedConfig.add(expectedDevice2); + + assertThat(config).isEqualTo(expectedConfig); + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java index bd297eecf25c..440a49ab81fc 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java @@ -31,6 +31,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -150,6 +151,11 @@ public class SystemAudioInitiationActionFromAvrTest { int sourceAddress, int physicalAddress) { mBroadcastActiveSource = true; } + + @Override + int pathToPortId(int path) { + return -1; + } }; mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) { @@ -270,6 +276,7 @@ public class SystemAudioInitiationActionFromAvrTest { assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()); } + @Ignore("b/120845532") @Test public void testIsPlaybackDevice_cannotReceiveActiveSource() { resetTestVariables(); @@ -282,10 +289,10 @@ public class SystemAudioInitiationActionFromAvrTest { mTestLooper.dispatchAll(); assertThat(mMsgRequestActiveSourceCount).isEqualTo(1); - assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1); + assertThat(mBroadcastActiveSource).isTrue(); assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1); + assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1); assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue(); - assertThat(mBroadcastActiveSource).isTrue(); } private void resetTestVariables() { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java index 5bf3d2dabe24..f947baceafd8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java @@ -36,6 +36,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import android.platform.test.annotations.Presubmit; @@ -277,4 +280,60 @@ public class ActivityDisplayTests extends ActivityTestsBase { assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop()); assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 1)); } + + @Test + public void testRemoveStackInWindowingModes() { + removeStackTests(() -> mRootActivityContainer.removeStacksInWindowingModes( + WINDOWING_MODE_FULLSCREEN)); + } + + @Test + public void testRemoveStackWithActivityTypes() { + removeStackTests( + () -> mRootActivityContainer.removeStacksWithActivityTypes(ACTIVITY_TYPE_STANDARD)); + } + + private void removeStackTests(Runnable runnable) { + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack stack1 = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final ActivityStack stack2 = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final ActivityStack stack3 = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final ActivityStack stack4 = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor).setStack( + stack1).setTaskId(1).build(); + final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack( + stack2).setTaskId(2).build(); + final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor).setStack( + stack3).setTaskId(3).build(); + final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor).setStack( + stack4).setTaskId(4).build(); + + // Reordering stacks while removing stacks. + doAnswer(invocation -> { + display.positionChildAtTop(stack3, false); + return true; + }).when(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(), + any()); + + // Removing stacks from the display while removing stacks. + doAnswer(invocation -> { + display.removeChild(stack2); + return true; + }).when(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(), + any()); + + runnable.run(); + verify(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(), + any()); + verify(mSupervisor).removeTaskByIdLocked(eq(task3.taskId), anyBoolean(), anyBoolean(), + any()); + verify(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(), + any()); + verify(mSupervisor).removeTaskByIdLocked(eq(task1.taskId), anyBoolean(), anyBoolean(), + any()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 1c33dfb79551..99be50be642e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -314,13 +314,11 @@ public class TaskRecordTests extends ActivityTestsBase { // Wire up task and stack. task.mTask.mTaskRecord = task; doCallRealMethod().when(task.mTask).onDescendantOrientationChanged(any(), any()); - doReturn(stack.getWindowContainerController().mContainer).when(task.mTask).getParent(); + doReturn(stack.getTaskStack()).when(task.mTask).getParent(); // Wire up stack and display content. - doCallRealMethod().when(stack.mWindowContainerController.mContainer) - .onDescendantOrientationChanged(any(), any()); - doReturn(display.mDisplayContent).when(stack.mWindowContainerController.mContainer) - .getParent(); + doCallRealMethod().when(stack.mTaskStack).onDescendantOrientationChanged(any(), any()); + doReturn(display.mDisplayContent).when(stack.mTaskStack).getParent(); activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); assertTrue("Bounds of the task should be pillarboxed.", diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 3408291825d1..17516bc24aca 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -66,7 +66,8 @@ public class DctConstants { public static final int EVENT_DATA_CONNECTION_DETACHED = BASE + 9; public static final int EVENT_ROAMING_ON = BASE + 11; public static final int EVENT_ROAMING_OFF = BASE + 12; - public static final int EVENT_ENABLE_NEW_APN = BASE + 13; + public static final int EVENT_ENABLE_APN = BASE + 13; + public static final int EVENT_DISABLE_APN = BASE + 14; public static final int EVENT_DISCONNECT_DONE = BASE + 15; public static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16; public static final int EVENT_DATA_STALL_ALARM = BASE + 17; diff --git a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java new file mode 100644 index 000000000000..4cabfc95b49d --- /dev/null +++ b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.shared; + +import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable; +import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable; + +import static org.junit.Assert.assertEquals; + +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.ProxyInfo; +import android.net.RouteInfo; +import android.net.Uri; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collections; + +/** + * Tests for {@link LinkPropertiesParcelableUtil} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LinkPropertiesParcelableUtilTest { + private LinkProperties mLinkProperties; + + private static final String TEST_LINKPROPS_IFACE = "TEST_IFACE"; + private static final String TEST_STACKED_LINK_1_IFACE = "TEST_STACKED_IFACE_1"; + private static final String TEST_STACKED_LINK_2_IFACE = "TEST_STACKED_IFACE_2"; + + @Before + public void setUp() { + mLinkProperties = makeLinkProperties(TEST_LINKPROPS_IFACE); + mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_1_IFACE)); + mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_2_IFACE)); + } + + private static LinkProperties makeLinkProperties(String iface) { + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(iface); + lp.setLinkAddresses(Arrays.asList( + new LinkAddress(InetAddresses.parseNumericAddress("192.168.0.42"), 16), + new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::7"), 42))); + lp.setDnsServers(Arrays.asList( + InetAddresses.parseNumericAddress("2001:db8::42"), + InetAddresses.parseNumericAddress("192.168.1.1") + )); + lp.setValidatedPrivateDnsServers(Arrays.asList( + InetAddresses.parseNumericAddress("2001:db8::43"), + InetAddresses.parseNumericAddress("192.168.42.43") + )); + lp.setPcscfServers(Arrays.asList( + InetAddresses.parseNumericAddress("2001:db8::47"), + InetAddresses.parseNumericAddress("192.168.42.47") + )); + lp.setUsePrivateDns(true); + lp.setPrivateDnsServerName("test.example.com"); + lp.setDomains("test1.example.com,test2.example.com"); + lp.addRoute(new RouteInfo( + new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::44"), 45), + InetAddresses.parseNumericAddress("2001:db8::45"), + iface, + RouteInfo.RTN_UNICAST + )); + lp.addRoute(new RouteInfo( + new IpPrefix(InetAddresses.parseNumericAddress("192.168.44.45"), 16), + InetAddresses.parseNumericAddress("192.168.45.1"), + iface, + RouteInfo.RTN_THROW + )); + lp.setHttpProxy(new ProxyInfo("test3.example.com", 8000, + "excl1.example.com,excl2.example.com")); + lp.setMtu(5000); + lp.setTcpBufferSizes("1,2,3,4,5,6"); + lp.setNat64Prefix(new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96)); + + // Verify that this test does not miss any new field added later. + // If any added field is not included in LinkProperties#equals, assertLinkPropertiesEquals + // must also be updated. + assertEquals(14, Arrays.stream(LinkProperties.class.getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); + + return lp; + } + + @Test + public void testParcelUnparcel() { + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullInterface() { + mLinkProperties.setInterfaceName(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullPrivateDnsServer() { + mLinkProperties.setPrivateDnsServerName(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullDomains() { + mLinkProperties.setDomains(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullProxy() { + mLinkProperties.setHttpProxy(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullTcpBufferSizes() { + mLinkProperties.setTcpBufferSizes(null); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyLinkAddresses() { + mLinkProperties.setLinkAddresses(Collections.emptyList()); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyDnses() { + mLinkProperties.setDnsServers(Collections.emptyList()); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyValidatedPrivateDnses() { + mLinkProperties.setValidatedPrivateDnsServers(Collections.emptyList()); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_EmptyRoutes() { + for (RouteInfo r : mLinkProperties.getAllRoutes()) { + mLinkProperties.removeRoute(r); + } + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_PacFileProxyInfo() { + mLinkProperties.setHttpProxy(new ProxyInfo(Uri.parse("http://pacfile.example.com"))); + doParcelUnparcelTest(); + } + + @Test + public void testParcelUnparcel_NullNat64Prefix() { + mLinkProperties.setNat64Prefix(null); + doParcelUnparcelTest(); + } + + private void doParcelUnparcelTest() { + final LinkProperties unparceled = fromStableParcelable(toStableParcelable(mLinkProperties)); + assertLinkPropertiesEquals(mLinkProperties, unparceled); + } + + private static void assertLinkPropertiesEquals(LinkProperties expected, LinkProperties actual) { + assertEquals(expected, actual); + + // LinkProperties equals() does not include stacked links + assertEquals(expected.getStackedLinks(), actual.getStackedLinks()); + } +} |